Threads without active or focus window should not receive keyboard
[wine.git] / controls / listbox.c
blob4d6b39a4211a66cc987f212c5eba5517d7161fe2
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 /* flag listbox changed while setredraw false - internal style */
45 #define LBS_DISPLAYCHANGED 0x80000000
47 /* Item structure */
48 typedef struct
50 LPSTR str; /* Item text */
51 BOOL selected; /* Is item selected? */
52 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
53 DWORD data; /* User data */
54 } LB_ITEMDATA;
56 /* Listbox structure */
57 typedef struct
59 HANDLE heap; /* Heap for this listbox */
60 HWND owner; /* Owner window to send notifications to */
61 UINT style; /* Window style */
62 INT width; /* Window width */
63 INT height; /* Window height */
64 LB_ITEMDATA *items; /* Array of items */
65 INT nb_items; /* Number of items */
66 INT top_item; /* Top visible item */
67 INT selected_item; /* Selected item */
68 INT focus_item; /* Item that has the focus */
69 INT anchor_item; /* Anchor item for extended selection */
70 INT item_height; /* Default item height */
71 INT page_size; /* Items per listbox page */
72 INT column_width; /* Column width for multi-column listboxes */
73 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
74 INT horz_pos; /* Horizontal position */
75 INT nb_tabs; /* Number of tabs in array */
76 INT *tabs; /* Array of tabs */
77 BOOL caret_on; /* Is caret on? */
78 BOOL captured; /* Is mouse captured? */
79 BOOL in_focus;
80 HFONT font; /* Current font */
81 LCID locale; /* Current locale for string comparisons */
82 LPHEADCOMBO lphc; /* ComboLBox */
83 } LB_DESCR;
86 #define IS_OWNERDRAW(descr) \
87 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
89 #define HAS_STRINGS(descr) \
90 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
93 #define IS_MULTISELECT(descr) \
94 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
96 #define SEND_NOTIFICATION(wnd,descr,code) \
97 (SendMessageA( (descr)->owner, WM_COMMAND, \
98 MAKEWPARAM((wnd)->wIDmenu, (code)), (wnd)->hwndSelf ))
100 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
102 /* Current timer status */
103 typedef enum
105 LB_TIMER_NONE,
106 LB_TIMER_UP,
107 LB_TIMER_LEFT,
108 LB_TIMER_DOWN,
109 LB_TIMER_RIGHT
110 } TIMER_DIRECTION;
112 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
115 /***********************************************************************
116 * LISTBOX_Dump
118 void LISTBOX_Dump( WND *wnd )
120 INT i;
121 LB_ITEMDATA *item;
122 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
124 TRACE( "Listbox:\n" );
125 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
126 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
127 descr->top_item );
128 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
130 TRACE( "%4d: %-40s %d %08lx %3d\n",
131 i, item->str, item->selected, item->data, item->height );
136 /***********************************************************************
137 * LISTBOX_GetCurrentPageSize
139 * Return the current page size
141 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
143 INT i, height;
144 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
145 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
147 if ((height += descr->items[i].height) > descr->height) break;
149 if (i == descr->top_item) return 1;
150 else return i - descr->top_item;
154 /***********************************************************************
155 * LISTBOX_GetMaxTopIndex
157 * Return the maximum possible index for the top of the listbox.
159 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
161 INT max, page;
163 if (descr->style & LBS_OWNERDRAWVARIABLE)
165 page = descr->height;
166 for (max = descr->nb_items - 1; max >= 0; max--)
167 if ((page -= descr->items[max].height) < 0) break;
168 if (max < descr->nb_items - 1) max++;
170 else if (descr->style & LBS_MULTICOLUMN)
172 if ((page = descr->width / descr->column_width) < 1) page = 1;
173 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
174 max = (max - page) * descr->page_size;
176 else
178 max = descr->nb_items - descr->page_size;
180 if (max < 0) max = 0;
181 return max;
185 /***********************************************************************
186 * LISTBOX_UpdateScroll
188 * Update the scrollbars. Should be called whenever the content
189 * of the listbox changes.
191 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
193 SCROLLINFO info;
195 /* Check the listbox scroll bar flags individually before we call
196 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
197 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
198 scroll bar when we do not need one.
199 if (!(descr->style & WS_VSCROLL)) return;
202 /* It is important that we check descr->style, and not wnd->dwStyle,
203 for WS_VSCROLL, as the former is exactly the one passed in
204 argument to CreateWindow.
205 In Windows (and from now on in Wine :) a listbox created
206 with such a style (no WS_SCROLL) does not update
207 the scrollbar with listbox-related data, thus letting
208 the programmer use it for his/her own purposes. */
210 if (descr->style & LBS_NOREDRAW) return;
211 info.cbSize = sizeof(info);
213 if (descr->style & LBS_MULTICOLUMN)
215 info.nMin = 0;
216 info.nMax = (descr->nb_items - 1) / descr->page_size;
217 info.nPos = descr->top_item / descr->page_size;
218 info.nPage = descr->width / descr->column_width;
219 if (info.nPage < 1) info.nPage = 1;
220 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
221 if (descr->style & LBS_DISABLENOSCROLL)
222 info.fMask |= SIF_DISABLENOSCROLL;
223 if (descr->style & WS_HSCROLL)
224 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
225 info.nMax = 0;
226 info.fMask = SIF_RANGE;
227 if (descr->style & WS_VSCROLL)
228 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
230 else
232 info.nMin = 0;
233 info.nMax = descr->nb_items - 1;
234 info.nPos = descr->top_item;
235 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
236 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
237 if (descr->style & LBS_DISABLENOSCROLL)
238 info.fMask |= SIF_DISABLENOSCROLL;
239 if (descr->style & WS_VSCROLL)
240 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
242 if (descr->horz_extent)
244 info.nMin = 0;
245 info.nMax = descr->horz_extent - 1;
246 info.nPos = descr->horz_pos;
247 info.nPage = descr->width;
248 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
249 if (descr->style & LBS_DISABLENOSCROLL)
250 info.fMask |= SIF_DISABLENOSCROLL;
251 if (descr->style & WS_HSCROLL)
252 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
259 /***********************************************************************
260 * LISTBOX_SetTopItem
262 * Set the top item of the listbox, scrolling up or down if necessary.
264 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
265 BOOL scroll )
267 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
268 if (index > max) index = max;
269 if (index < 0) index = 0;
270 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
271 if (descr->top_item == index) return LB_OKAY;
272 if (descr->style & LBS_MULTICOLUMN)
274 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
275 if (scroll && (abs(diff) < descr->width))
276 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
277 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
279 else
280 scroll = FALSE;
282 else if (scroll)
284 INT diff;
285 if (descr->style & LBS_OWNERDRAWVARIABLE)
287 INT i;
288 diff = 0;
289 if (index > descr->top_item)
291 for (i = index - 1; i >= descr->top_item; i--)
292 diff -= descr->items[i].height;
294 else
296 for (i = index; i < descr->top_item; i++)
297 diff += descr->items[i].height;
300 else
301 diff = (descr->top_item - index) * descr->item_height;
303 if (abs(diff) < descr->height)
304 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
305 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
306 else
307 scroll = FALSE;
309 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
310 descr->top_item = index;
311 LISTBOX_UpdateScroll( wnd, descr );
312 return LB_OKAY;
316 /***********************************************************************
317 * LISTBOX_UpdatePage
319 * Update the page size. Should be called when the size of
320 * the client area or the item height changes.
322 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
324 INT page_size;
326 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
327 page_size = 1;
328 if (page_size == descr->page_size) return;
329 descr->page_size = page_size;
330 if (descr->style & LBS_MULTICOLUMN)
331 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
332 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
336 /***********************************************************************
337 * LISTBOX_UpdateSize
339 * Update the size of the listbox. Should be called when the size of
340 * the client area changes.
342 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
344 RECT rect;
346 GetClientRect( wnd->hwndSelf, &rect );
347 descr->width = rect.right - rect.left;
348 descr->height = rect.bottom - rect.top;
349 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
351 UINT remaining = descr->height % descr->item_height;
352 if ((descr->height > descr->item_height) && remaining)
354 if (!(wnd->flags & WIN_ISWIN32))
355 { /* give a margin for error to 16 bits programs - if we need
356 less than the height of the nonclient area, round to the
357 *next* number of items */
358 int ncheight = wnd->rectWindow.bottom - wnd->rectWindow.top - descr->height;
359 if ((descr->item_height - remaining) <= ncheight)
360 remaining = remaining - descr->item_height;
362 TRACE("[%04x]: changing height %d -> %d\n",
363 wnd->hwndSelf, descr->height,
364 descr->height - remaining );
365 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
366 wnd->rectWindow.right - wnd->rectWindow.left,
367 wnd->rectWindow.bottom - wnd->rectWindow.top - remaining,
368 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
369 return;
372 TRACE("[%04x]: new size = %d,%d\n",
373 wnd->hwndSelf, descr->width, descr->height );
374 LISTBOX_UpdatePage( wnd, descr );
375 LISTBOX_UpdateScroll( wnd, descr );
379 /***********************************************************************
380 * LISTBOX_GetItemRect
382 * Get the rectangle enclosing an item, in listbox client coordinates.
383 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
385 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
386 RECT *rect )
388 /* Index <= 0 is legal even on empty listboxes */
389 if (index && (index >= descr->nb_items)) return -1;
390 SetRect( rect, 0, 0, descr->width, descr->height );
391 if (descr->style & LBS_MULTICOLUMN)
393 INT col = (index / descr->page_size) -
394 (descr->top_item / descr->page_size);
395 rect->left += col * descr->column_width;
396 rect->right = rect->left + descr->column_width;
397 rect->top += (index % descr->page_size) * descr->item_height;
398 rect->bottom = rect->top + descr->item_height;
400 else if (descr->style & LBS_OWNERDRAWVARIABLE)
402 INT i;
403 rect->right += descr->horz_pos;
404 if ((index >= 0) && (index < descr->nb_items))
406 if (index < descr->top_item)
408 for (i = descr->top_item-1; i >= index; i--)
409 rect->top -= descr->items[i].height;
411 else
413 for (i = descr->top_item; i < index; i++)
414 rect->top += descr->items[i].height;
416 rect->bottom = rect->top + descr->items[index].height;
420 else
422 rect->top += (index - descr->top_item) * descr->item_height;
423 rect->bottom = rect->top + descr->item_height;
424 rect->right += descr->horz_pos;
427 return ((rect->left < descr->width) && (rect->right > 0) &&
428 (rect->top < descr->height) && (rect->bottom > 0));
432 /***********************************************************************
433 * LISTBOX_GetItemFromPoint
435 * Return the item nearest from point (x,y) (in client coordinates).
437 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
438 INT x, INT y )
440 INT index = descr->top_item;
442 if (!descr->nb_items) return -1; /* No items */
443 if (descr->style & LBS_OWNERDRAWVARIABLE)
445 INT pos = 0;
446 if (y >= 0)
448 while (index < descr->nb_items)
450 if ((pos += descr->items[index].height) > y) break;
451 index++;
454 else
456 while (index > 0)
458 index--;
459 if ((pos -= descr->items[index].height) <= y) break;
463 else if (descr->style & LBS_MULTICOLUMN)
465 if (y >= descr->item_height * descr->page_size) return -1;
466 if (y >= 0) index += y / descr->item_height;
467 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
468 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
470 else
472 index += (y / descr->item_height);
474 if (index < 0) return 0;
475 if (index >= descr->nb_items) return -1;
476 return index;
480 /***********************************************************************
481 * LISTBOX_PaintItem
483 * Paint an item.
485 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
486 const RECT *rect, INT index, UINT action )
488 LB_ITEMDATA *item = NULL;
489 if (index < descr->nb_items) item = &descr->items[index];
491 if (IS_OWNERDRAW(descr))
493 DRAWITEMSTRUCT dis;
494 RECT r;
495 HRGN hrgn;
497 if (!item)
499 if (action == ODA_FOCUS)
500 DrawFocusRect( hdc, rect );
501 else
502 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
503 return;
506 /* some programs mess with the clipping region when
507 drawing the item, *and* restore the previous region
508 after they are done, so a region has better to exist
509 else everything ends clipped */
510 GetClientRect(wnd->hwndSelf, &r);
511 hrgn = CreateRectRgnIndirect(&r);
512 SelectClipRgn( hdc, hrgn);
513 DeleteObject( hrgn );
515 dis.CtlType = ODT_LISTBOX;
516 dis.CtlID = wnd->wIDmenu;
517 dis.hwndItem = wnd->hwndSelf;
518 dis.itemAction = action;
519 dis.hDC = hdc;
520 dis.itemID = index;
521 dis.itemState = 0;
522 if (item && item->selected) dis.itemState |= ODS_SELECTED;
523 if ((descr->focus_item == index) &&
524 (descr->caret_on) &&
525 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
526 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
527 dis.itemData = item ? item->data : 0;
528 dis.rcItem = *rect;
529 TRACE("[%04x]: drawitem %d (%s) action=%02x "
530 "state=%02x rect=%d,%d-%d,%d\n",
531 wnd->hwndSelf, index, item ? item->str : "", action,
532 dis.itemState, rect->left, rect->top,
533 rect->right, rect->bottom );
534 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
536 else
538 COLORREF oldText = 0, oldBk = 0;
540 if (action == ODA_FOCUS)
542 DrawFocusRect( hdc, rect );
543 return;
545 if (item && item->selected)
547 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
548 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
551 TRACE("[%04x]: painting %d (%s) action=%02x "
552 "rect=%d,%d-%d,%d\n",
553 wnd->hwndSelf, index, item ? item->str : "", action,
554 rect->left, rect->top, rect->right, rect->bottom );
555 if (!item)
556 ExtTextOutA( hdc, rect->left + 1, rect->top,
557 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
558 else if (!(descr->style & LBS_USETABSTOPS))
559 ExtTextOutA( hdc, rect->left + 1, rect->top,
560 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
561 strlen(item->str), NULL );
562 else
564 /* Output empty string to paint background in the full width. */
565 ExtTextOutA( hdc, rect->left + 1, rect->top,
566 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
567 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
568 item->str, strlen(item->str),
569 descr->nb_tabs, descr->tabs, 0);
571 if (item && item->selected)
573 SetBkColor( hdc, oldBk );
574 SetTextColor( hdc, oldText );
576 if ((descr->focus_item == index) &&
577 (descr->caret_on) &&
578 (descr->in_focus)) DrawFocusRect( hdc, rect );
583 /***********************************************************************
584 * LISTBOX_SetRedraw
586 * Change the redraw flag.
588 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
590 if (on)
592 if (!(descr->style & LBS_NOREDRAW)) return;
593 descr->style &= ~LBS_NOREDRAW;
594 if (descr->style & LBS_DISPLAYCHANGED)
595 { /* page was changed while setredraw false, refresh automatically */
596 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
597 if ((descr->top_item + descr->page_size) > descr->nb_items)
598 { /* reset top of page if less than number of items/page */
599 descr->top_item = descr->nb_items - descr->page_size;
600 if (descr->top_item < 0) descr->top_item = 0;
602 descr->style &= ~LBS_DISPLAYCHANGED;
604 LISTBOX_UpdateScroll( wnd, descr );
606 else descr->style |= LBS_NOREDRAW;
610 /***********************************************************************
611 * LISTBOX_RepaintItem
613 * Repaint a single item synchronously.
615 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
616 UINT action )
618 HDC hdc;
619 RECT rect;
620 HFONT oldFont = 0;
621 HBRUSH hbrush, oldBrush = 0;
623 /* Do not repaint the item if the item is not visible */
624 if (!IsWindowVisible(wnd->hwndSelf)) return;
625 if (descr->style & LBS_NOREDRAW)
627 descr->style |= LBS_DISPLAYCHANGED;
628 return;
630 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
631 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
632 if (descr->font) oldFont = SelectObject( hdc, descr->font );
633 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
634 hdc, (LPARAM)wnd->hwndSelf );
635 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
636 if (wnd->dwStyle & WS_DISABLED)
637 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
638 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
639 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
640 if (oldFont) SelectObject( hdc, oldFont );
641 if (oldBrush) SelectObject( hdc, oldBrush );
642 ReleaseDC( wnd->hwndSelf, hdc );
646 /***********************************************************************
647 * LISTBOX_InitStorage
649 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
650 DWORD bytes )
652 LB_ITEMDATA *item;
654 nb_items += LB_ARRAY_GRANULARITY - 1;
655 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
656 if (descr->items)
657 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
658 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
659 nb_items * sizeof(LB_ITEMDATA) )))
661 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
662 return LB_ERRSPACE;
664 descr->items = item;
665 return LB_OKAY;
669 /***********************************************************************
670 * LISTBOX_SetTabStops
672 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
673 LPINT tabs, BOOL short_ints )
675 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
676 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
677 if (!(descr->nb_tabs = count))
679 descr->tabs = NULL;
680 return TRUE;
682 /* FIXME: count = 1 */
683 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
684 descr->nb_tabs * sizeof(INT) )))
685 return FALSE;
686 if (short_ints)
688 INT i;
689 LPINT16 p = (LPINT16)tabs;
691 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
692 for (i = 0; i < descr->nb_tabs; i++) {
693 descr->tabs[i] = *p++<<1; /* FIXME */
694 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
696 if (TRACE_ON(listbox)) DPRINTF("\n");
698 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
699 /* FIXME: repaint the window? */
700 return TRUE;
704 /***********************************************************************
705 * LISTBOX_GetText
707 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
708 LPSTR buffer )
710 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
711 if (HAS_STRINGS(descr))
713 if (!buffer)
714 return strlen(descr->items[index].str);
715 strcpy( buffer, descr->items[index].str );
716 return strlen(buffer);
717 } else {
718 if (buffer)
719 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
720 return sizeof(DWORD);
725 /***********************************************************************
726 * LISTBOX_FindStringPos
728 * Find the nearest string located before a given string in sort order.
729 * If 'exact' is TRUE, return an error if we don't get an exact match.
731 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
732 BOOL exact )
734 INT index, min, max, res = -1;
736 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
737 min = 0;
738 max = descr->nb_items;
739 while (min != max)
741 index = (min + max) / 2;
742 if (HAS_STRINGS(descr))
743 res = lstrcmpiA( descr->items[index].str, str );
744 else
746 COMPAREITEMSTRUCT cis;
748 cis.CtlType = ODT_LISTBOX;
749 cis.CtlID = wnd->wIDmenu;
750 cis.hwndItem = wnd->hwndSelf;
751 cis.itemID1 = index;
752 cis.itemData1 = descr->items[index].data;
753 cis.itemID2 = -1;
754 cis.itemData2 = (DWORD)str;
755 cis.dwLocaleId = descr->locale;
756 res = SendMessageA( descr->owner, WM_COMPAREITEM,
757 wnd->wIDmenu, (LPARAM)&cis );
759 if (!res) return index;
760 if (res > 0) max = index;
761 else min = index + 1;
763 return exact ? -1 : max;
767 /***********************************************************************
768 * LISTBOX_FindFileStrPos
770 * Find the nearest string located before a given string in directory
771 * sort order (i.e. first files, then directories, then drives).
773 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
775 INT min, max, res = -1;
777 if (!HAS_STRINGS(descr))
778 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
779 min = 0;
780 max = descr->nb_items;
781 while (min != max)
783 INT index = (min + max) / 2;
784 const char *p = descr->items[index].str;
785 if (*p == '[') /* drive or directory */
787 if (*str != '[') res = -1;
788 else if (p[1] == '-') /* drive */
790 if (str[1] == '-') res = str[2] - p[2];
791 else res = -1;
793 else /* directory */
795 if (str[1] == '-') res = 1;
796 else res = lstrcmpiA( str, p );
799 else /* filename */
801 if (*str == '[') res = 1;
802 else res = lstrcmpiA( str, p );
804 if (!res) return index;
805 if (res < 0) max = index;
806 else min = index + 1;
808 return max;
812 /***********************************************************************
813 * LISTBOX_FindString
815 * Find the item beginning with a given string.
817 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
818 LPCSTR str, BOOL exact )
820 INT i;
821 LB_ITEMDATA *item;
823 if (start >= descr->nb_items) start = -1;
824 item = descr->items + start + 1;
825 if (HAS_STRINGS(descr))
827 if (!str || ! str[0] ) return LB_ERR;
828 if (exact)
830 for (i = start + 1; i < descr->nb_items; i++, item++)
831 if (!lstrcmpiA( str, item->str )) return i;
832 for (i = 0, item = descr->items; i <= start; i++, item++)
833 if (!lstrcmpiA( str, item->str )) return i;
835 else
837 /* Special case for drives and directories: ignore prefix */
838 #define CHECK_DRIVE(item) \
839 if ((item)->str[0] == '[') \
841 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
842 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
843 return i; \
846 INT len = strlen(str);
847 for (i = start + 1; i < descr->nb_items; i++, item++)
849 if (!lstrncmpiA( str, item->str, len )) return i;
850 CHECK_DRIVE(item);
852 for (i = 0, item = descr->items; i <= start; i++, item++)
854 if (!lstrncmpiA( str, item->str, len )) return i;
855 CHECK_DRIVE(item);
857 #undef CHECK_DRIVE
860 else
862 if (exact && (descr->style & LBS_SORT))
863 /* If sorted, use a WM_COMPAREITEM binary search */
864 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
866 /* Otherwise use a linear search */
867 for (i = start + 1; i < descr->nb_items; i++, item++)
868 if (item->data == (DWORD)str) return i;
869 for (i = 0, item = descr->items; i <= start; i++, item++)
870 if (item->data == (DWORD)str) return i;
872 return LB_ERR;
876 /***********************************************************************
877 * LISTBOX_GetSelCount
879 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
881 INT i, count;
882 LB_ITEMDATA *item = descr->items;
884 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
885 for (i = count = 0; i < descr->nb_items; i++, item++)
886 if (item->selected) count++;
887 return count;
891 /***********************************************************************
892 * LISTBOX_GetSelItems16
894 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
895 LPINT16 array )
897 INT i, count;
898 LB_ITEMDATA *item = descr->items;
900 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
901 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
902 if (item->selected) array[count++] = (INT16)i;
903 return count;
907 /***********************************************************************
908 * LISTBOX_GetSelItems32
910 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
911 LPINT array )
913 INT i, count;
914 LB_ITEMDATA *item = descr->items;
916 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
917 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
918 if (item->selected) array[count++] = i;
919 return count;
923 /***********************************************************************
924 * LISTBOX_Paint
926 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
928 INT i, col_pos = descr->page_size - 1;
929 RECT rect;
930 RECT focusRect = {-1, -1, -1, -1};
931 HFONT oldFont = 0;
932 HBRUSH hbrush, oldBrush = 0;
933 INT focusItem;
935 if (descr->style & LBS_NOREDRAW) return 0;
937 SetRect( &rect, 0, 0, descr->width, descr->height );
938 if (descr->style & LBS_MULTICOLUMN)
939 rect.right = rect.left + descr->column_width;
940 else if (descr->horz_pos)
942 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
943 rect.right += descr->horz_pos;
946 if (descr->font) oldFont = SelectObject( hdc, descr->font );
947 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
948 hdc, (LPARAM)wnd->hwndSelf );
949 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
950 if (wnd->dwStyle & WS_DISABLED)
951 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
953 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
954 (descr->in_focus))
956 /* Special case for empty listbox: paint focus rect */
957 rect.bottom = rect.top + descr->item_height;
958 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
959 ODA_FOCUS );
960 rect.top = rect.bottom;
963 /* Paint all the item, regarding the selection
964 Focus state will be painted after */
965 focusItem = descr->focus_item;
966 descr->focus_item = -1;
968 for (i = descr->top_item; i < descr->nb_items; i++)
970 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
971 rect.bottom = rect.top + descr->item_height;
972 else
973 rect.bottom = rect.top + descr->items[i].height;
975 if (i == focusItem)
977 /* keep the focus rect, to paint the focus item after */
978 focusRect.left = rect.left;
979 focusRect.right = rect.right;
980 focusRect.top = rect.top;
981 focusRect.bottom = rect.bottom;
983 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
984 rect.top = rect.bottom;
986 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
988 if (!IS_OWNERDRAW(descr))
990 /* Clear the bottom of the column */
991 if (rect.top < descr->height)
993 rect.bottom = descr->height;
994 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
995 &rect, NULL, 0, NULL );
999 /* Go to the next column */
1000 rect.left += descr->column_width;
1001 rect.right += descr->column_width;
1002 rect.top = 0;
1003 col_pos = descr->page_size - 1;
1005 else
1007 col_pos--;
1008 if (rect.top >= descr->height) break;
1012 /* Paint the focus item now */
1013 descr->focus_item = focusItem;
1014 if (focusRect.top != focusRect.bottom && descr->caret_on)
1015 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS );
1017 if (!IS_OWNERDRAW(descr))
1019 /* Clear the remainder of the client area */
1020 if (rect.top < descr->height)
1022 rect.bottom = descr->height;
1023 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1024 &rect, NULL, 0, NULL );
1026 if (rect.right < descr->width)
1028 rect.left = rect.right;
1029 rect.right = descr->width;
1030 rect.top = 0;
1031 rect.bottom = descr->height;
1032 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1033 &rect, NULL, 0, NULL );
1036 if (oldFont) SelectObject( hdc, oldFont );
1037 if (oldBrush) SelectObject( hdc, oldBrush );
1038 return 0;
1042 /***********************************************************************
1043 * LISTBOX_InvalidateItems
1045 * Invalidate all items from a given item. If the specified item is not
1046 * visible, nothing happens.
1048 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1050 RECT rect;
1052 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1054 if (descr->style & LBS_NOREDRAW)
1056 descr->style |= LBS_DISPLAYCHANGED;
1057 return;
1059 rect.bottom = descr->height;
1060 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1061 if (descr->style & LBS_MULTICOLUMN)
1063 /* Repaint the other columns */
1064 rect.left = rect.right;
1065 rect.right = descr->width;
1066 rect.top = 0;
1067 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1073 /***********************************************************************
1074 * LISTBOX_GetItemHeight
1076 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1078 if (descr->style & LBS_OWNERDRAWVARIABLE)
1080 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1081 return descr->items[index].height;
1083 else return descr->item_height;
1087 /***********************************************************************
1088 * LISTBOX_SetItemHeight
1090 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1091 UINT height )
1093 if (!height) height = 1;
1095 if (descr->style & LBS_OWNERDRAWVARIABLE)
1097 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1098 TRACE("[%04x]: item %d height = %d\n",
1099 wnd->hwndSelf, index, height );
1100 descr->items[index].height = height;
1101 LISTBOX_UpdateScroll( wnd, descr );
1102 LISTBOX_InvalidateItems( wnd, descr, index );
1104 else if (height != descr->item_height)
1106 TRACE("[%04x]: new height = %d\n",
1107 wnd->hwndSelf, height );
1108 descr->item_height = height;
1109 LISTBOX_UpdatePage( wnd, descr );
1110 LISTBOX_UpdateScroll( wnd, descr );
1111 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1113 return LB_OKAY;
1117 /***********************************************************************
1118 * LISTBOX_SetHorizontalPos
1120 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1122 INT diff;
1124 if (pos > descr->horz_extent - descr->width)
1125 pos = descr->horz_extent - descr->width;
1126 if (pos < 0) pos = 0;
1127 if (!(diff = descr->horz_pos - pos)) return;
1128 TRACE("[%04x]: new horz pos = %d\n",
1129 wnd->hwndSelf, pos );
1130 descr->horz_pos = pos;
1131 LISTBOX_UpdateScroll( wnd, descr );
1132 if (abs(diff) < descr->width)
1133 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1134 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1135 else
1136 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1140 /***********************************************************************
1141 * LISTBOX_SetHorizontalExtent
1143 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1144 UINT extent )
1146 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1147 return LB_OKAY;
1148 if (extent <= 0) extent = 1;
1149 if (extent == descr->horz_extent) return LB_OKAY;
1150 TRACE("[%04x]: new horz extent = %d\n",
1151 wnd->hwndSelf, extent );
1152 descr->horz_extent = extent;
1153 if (descr->horz_pos > extent - descr->width)
1154 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1155 else
1156 LISTBOX_UpdateScroll( wnd, descr );
1157 return LB_OKAY;
1161 /***********************************************************************
1162 * LISTBOX_SetColumnWidth
1164 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1166 if (width == descr->column_width) return LB_OKAY;
1167 TRACE("[%04x]: new column width = %d\n",
1168 wnd->hwndSelf, width );
1169 descr->column_width = width;
1170 LISTBOX_UpdatePage( wnd, descr );
1171 return LB_OKAY;
1175 /***********************************************************************
1176 * LISTBOX_SetFont
1178 * Returns the item height.
1180 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1182 HDC hdc;
1183 HFONT oldFont = 0;
1184 TEXTMETRICA tm;
1186 descr->font = font;
1188 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1190 ERR("unable to get DC.\n" );
1191 return 16;
1193 if (font) oldFont = SelectObject( hdc, font );
1194 GetTextMetricsA( hdc, &tm );
1195 if (oldFont) SelectObject( hdc, oldFont );
1196 ReleaseDC( wnd->hwndSelf, hdc );
1197 if (!IS_OWNERDRAW(descr))
1198 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1199 return tm.tmHeight ;
1203 /***********************************************************************
1204 * LISTBOX_MakeItemVisible
1206 * Make sure that a given item is partially or fully visible.
1208 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1209 BOOL fully )
1211 INT top;
1213 if (index <= descr->top_item) top = index;
1214 else if (descr->style & LBS_MULTICOLUMN)
1216 INT cols = descr->width;
1217 if (!fully) cols += descr->column_width - 1;
1218 if (cols >= descr->column_width) cols /= descr->column_width;
1219 else cols = 1;
1220 if (index < descr->top_item + (descr->page_size * cols)) return;
1221 top = index - descr->page_size * (cols - 1);
1223 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1225 INT height = fully ? descr->items[index].height : 1;
1226 for (top = index; top > descr->top_item; top--)
1227 if ((height += descr->items[top-1].height) > descr->height) break;
1229 else
1231 if (index < descr->top_item + descr->page_size) return;
1232 if (!fully && (index == descr->top_item + descr->page_size) &&
1233 (descr->height > (descr->page_size * descr->item_height))) return;
1234 top = index - descr->page_size + 1;
1236 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1239 /***********************************************************************
1240 * LISTBOX_SetCaretIndex
1242 * NOTES
1243 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1246 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1247 BOOL fully_visible )
1249 INT oldfocus = descr->focus_item;
1251 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1252 if (index == oldfocus) return LB_OKAY;
1253 descr->focus_item = index;
1254 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1255 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1257 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1258 if (descr->caret_on && (descr->in_focus))
1259 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1261 return LB_OKAY;
1265 /***********************************************************************
1266 * LISTBOX_SelectItemRange
1268 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1270 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1271 INT last, BOOL on )
1273 INT i;
1275 /* A few sanity checks */
1277 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1278 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1279 if (last == -1) last = descr->nb_items - 1;
1280 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1281 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1282 /* selected_item reflects last selected/unselected item on multiple sel */
1283 descr->selected_item = last;
1285 if (on) /* Turn selection on */
1287 for (i = first; i <= last; i++)
1289 if (descr->items[i].selected) continue;
1290 descr->items[i].selected = TRUE;
1291 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1293 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1295 else /* Turn selection off */
1297 for (i = first; i <= last; i++)
1299 if (!descr->items[i].selected) continue;
1300 descr->items[i].selected = FALSE;
1301 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1304 return LB_OKAY;
1307 /***********************************************************************
1308 * LISTBOX_SetSelection
1310 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1311 BOOL on, BOOL send_notify )
1313 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1315 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1316 if (descr->style & LBS_MULTIPLESEL)
1318 if (index == -1) /* Select all items */
1319 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1320 else /* Only one item */
1321 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1323 else
1325 INT oldsel = descr->selected_item;
1326 if (index == oldsel) return LB_OKAY;
1327 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1328 if (index != -1) descr->items[index].selected = TRUE;
1329 descr->selected_item = index;
1330 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1331 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1332 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1333 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1334 else
1335 if( descr->lphc ) /* set selection change flag for parent combo */
1336 descr->lphc->wState |= CBF_SELCHANGE;
1338 return LB_OKAY;
1342 /***********************************************************************
1343 * LISTBOX_MoveCaret
1345 * Change the caret position and extend the selection to the new caret.
1347 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1348 BOOL fully_visible )
1350 INT oldfocus = descr->focus_item;
1352 if ((index < 0) || (index >= descr->nb_items))
1353 return;
1355 /* Important, repaint needs to be done in this order if
1356 you want to mimic Windows behavior:
1357 1. Remove the focus and paint the item
1358 2. Remove the selection and paint the item(s)
1359 3. Set the selection and repaint the item(s)
1360 4. Set the focus to 'index' and repaint the item */
1362 /* 1. remove the focus and repaint the item */
1363 descr->focus_item = -1;
1364 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1365 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1367 /* 2. then turn off the previous selection */
1368 /* 3. repaint the new selected item */
1369 if (descr->style & LBS_EXTENDEDSEL)
1371 if (descr->anchor_item != -1)
1373 INT first = min( index, descr->anchor_item );
1374 INT last = max( index, descr->anchor_item );
1375 if (first > 0)
1376 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1377 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1378 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1381 else if (!(descr->style & LBS_MULTIPLESEL))
1383 /* Set selection to new caret item */
1384 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1387 /* 4. repaint the new item with the focus */
1388 descr->focus_item = index;
1389 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1390 if (descr->caret_on && (descr->in_focus))
1391 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1395 /***********************************************************************
1396 * LISTBOX_InsertItem
1398 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1399 LPSTR str, DWORD data )
1401 LB_ITEMDATA *item;
1402 INT max_items;
1403 INT oldfocus = descr->focus_item;
1405 if (index == -1) index = descr->nb_items;
1406 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1407 if (!descr->items) max_items = 0;
1408 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1409 if (descr->nb_items == max_items)
1411 /* We need to grow the array */
1412 max_items += LB_ARRAY_GRANULARITY;
1413 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1414 max_items * sizeof(LB_ITEMDATA) )))
1416 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1417 return LB_ERRSPACE;
1419 descr->items = item;
1422 /* Insert the item structure */
1424 item = &descr->items[index];
1425 if (index < descr->nb_items)
1426 RtlMoveMemory( item + 1, item,
1427 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1428 item->str = str;
1429 item->data = data;
1430 item->height = 0;
1431 item->selected = FALSE;
1432 descr->nb_items++;
1434 /* Get item height */
1436 if (descr->style & LBS_OWNERDRAWVARIABLE)
1438 MEASUREITEMSTRUCT mis;
1440 mis.CtlType = ODT_LISTBOX;
1441 mis.CtlID = wnd->wIDmenu;
1442 mis.itemID = index;
1443 mis.itemData = descr->items[index].data;
1444 mis.itemHeight = descr->item_height;
1445 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1446 item->height = mis.itemHeight ? mis.itemHeight : 1;
1447 TRACE("[%04x]: measure item %d (%s) = %d\n",
1448 wnd->hwndSelf, index, str ? str : "", item->height );
1451 /* Repaint the items */
1453 LISTBOX_UpdateScroll( wnd, descr );
1454 LISTBOX_InvalidateItems( wnd, descr, index );
1456 /* Move selection and focused item */
1457 /* If listbox was empty, set focus to the first item */
1458 if (descr->nb_items == 1)
1459 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1460 /* single select don't change selection index in win31 */
1461 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1463 descr->selected_item++;
1464 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1466 else
1468 if (index <= descr->selected_item)
1470 descr->selected_item++;
1471 descr->focus_item = oldfocus; /* focus not changed */
1474 return LB_OKAY;
1478 /***********************************************************************
1479 * LISTBOX_InsertString
1481 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1482 LPCSTR str )
1484 LPSTR new_str = NULL;
1485 DWORD data = 0;
1486 LRESULT ret;
1488 if (HAS_STRINGS(descr))
1490 if (!str) str="";
1491 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1493 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1494 return LB_ERRSPACE;
1497 else data = (DWORD)str;
1499 if (index == -1) index = descr->nb_items;
1500 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1502 if (new_str) HeapFree( descr->heap, 0, new_str );
1503 return ret;
1506 TRACE("[%04x]: added item %d '%s'\n",
1507 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1508 return index;
1512 /***********************************************************************
1513 * LISTBOX_DeleteItem
1515 * Delete the content of an item. 'index' must be a valid index.
1517 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1519 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1520 * while Win95 sends it for all items with user data.
1521 * It's probably better to send it too often than not
1522 * often enough, so this is what we do here.
1524 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1526 DELETEITEMSTRUCT dis;
1528 dis.CtlType = ODT_LISTBOX;
1529 dis.CtlID = wnd->wIDmenu;
1530 dis.itemID = index;
1531 dis.hwndItem = wnd->hwndSelf;
1532 dis.itemData = descr->items[index].data;
1533 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1535 if (HAS_STRINGS(descr) && descr->items[index].str)
1536 HeapFree( descr->heap, 0, descr->items[index].str );
1540 /***********************************************************************
1541 * LISTBOX_RemoveItem
1543 * Remove an item from the listbox and delete its content.
1545 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1547 LB_ITEMDATA *item;
1548 INT max_items;
1550 if (index == -1) index = descr->nb_items - 1;
1551 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1553 /* We need to invalidate the original rect instead of the updated one. */
1554 LISTBOX_InvalidateItems( wnd, descr, index );
1556 LISTBOX_DeleteItem( wnd, descr, index );
1558 /* Remove the item */
1560 item = &descr->items[index];
1561 if (index < descr->nb_items-1)
1562 RtlMoveMemory( item, item + 1,
1563 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1564 descr->nb_items--;
1565 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1567 /* Shrink the item array if possible */
1569 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1570 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1572 max_items -= LB_ARRAY_GRANULARITY;
1573 item = HeapReAlloc( descr->heap, 0, descr->items,
1574 max_items * sizeof(LB_ITEMDATA) );
1575 if (item) descr->items = item;
1577 /* Repaint the items */
1579 LISTBOX_UpdateScroll( wnd, descr );
1580 /* if we removed the scrollbar, reset the top of the list
1581 (correct for owner-drawn ???) */
1582 if (descr->nb_items == descr->page_size)
1583 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1585 /* Move selection and focused item */
1586 if (!IS_MULTISELECT(descr))
1588 if (index == descr->selected_item)
1589 descr->selected_item = -1;
1590 else if (index < descr->selected_item)
1592 descr->selected_item--;
1593 if (ISWIN31) /* win 31 do not change the selected item number */
1594 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1598 if (descr->focus_item >= descr->nb_items)
1600 descr->focus_item = descr->nb_items - 1;
1601 if (descr->focus_item < 0) descr->focus_item = 0;
1603 return LB_OKAY;
1607 /***********************************************************************
1608 * LISTBOX_ResetContent
1610 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1612 INT i;
1614 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1615 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1616 descr->nb_items = 0;
1617 descr->top_item = 0;
1618 descr->selected_item = -1;
1619 descr->focus_item = 0;
1620 descr->anchor_item = -1;
1621 descr->items = NULL;
1622 LISTBOX_UpdateScroll( wnd, descr );
1623 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1627 /***********************************************************************
1628 * LISTBOX_SetCount
1630 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1632 LRESULT ret;
1634 if (HAS_STRINGS(descr)) return LB_ERR;
1635 /* FIXME: this is far from optimal... */
1636 if (count > descr->nb_items)
1638 while (count > descr->nb_items)
1639 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1640 return ret;
1642 else if (count < descr->nb_items)
1644 while (count < descr->nb_items)
1645 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1646 return ret;
1648 return LB_OKAY;
1652 /***********************************************************************
1653 * LISTBOX_Directory
1655 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1656 LPCSTR filespec, BOOL long_names )
1658 HANDLE handle;
1659 LRESULT ret = LB_OKAY;
1660 WIN32_FIND_DATAA entry;
1661 int pos;
1663 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1665 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1667 else
1671 char buffer[270];
1672 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1674 if (!(attrib & DDL_DIRECTORY) ||
1675 !strcmp( entry.cAlternateFileName, "." )) continue;
1676 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1677 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1679 else /* not a directory */
1681 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1682 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1684 if ((attrib & DDL_EXCLUSIVE) &&
1685 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1686 continue;
1687 #undef ATTRIBS
1688 if (long_names) strcpy( buffer, entry.cFileName );
1689 else strcpy( buffer, entry.cAlternateFileName );
1691 if (!long_names) CharLowerA( buffer );
1692 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1693 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1694 break;
1695 } while (FindNextFileA( handle, &entry ));
1696 FindClose( handle );
1699 if ((ret >= 0) && (attrib & DDL_DRIVES))
1701 char buffer[] = "[-a-]";
1702 int drive;
1703 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1705 if (!DRIVE_IsValid(drive)) continue;
1706 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1707 break;
1710 return ret;
1714 /***********************************************************************
1715 * LISTBOX_HandleVScroll
1717 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1718 WPARAM wParam, LPARAM lParam )
1720 SCROLLINFO info;
1722 if (descr->style & LBS_MULTICOLUMN) return 0;
1723 switch(LOWORD(wParam))
1725 case SB_LINEUP:
1726 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1727 break;
1728 case SB_LINEDOWN:
1729 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1730 break;
1731 case SB_PAGEUP:
1732 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1733 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1734 break;
1735 case SB_PAGEDOWN:
1736 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1737 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1738 break;
1739 case SB_THUMBPOSITION:
1740 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1741 break;
1742 case SB_THUMBTRACK:
1743 info.cbSize = sizeof(info);
1744 info.fMask = SIF_TRACKPOS;
1745 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1746 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1747 break;
1748 case SB_TOP:
1749 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1750 break;
1751 case SB_BOTTOM:
1752 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1753 break;
1755 return 0;
1759 /***********************************************************************
1760 * LISTBOX_HandleHScroll
1762 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1763 WPARAM wParam, LPARAM lParam )
1765 SCROLLINFO info;
1766 INT page;
1768 if (descr->style & LBS_MULTICOLUMN)
1770 switch(LOWORD(wParam))
1772 case SB_LINELEFT:
1773 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1774 TRUE );
1775 break;
1776 case SB_LINERIGHT:
1777 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1778 TRUE );
1779 break;
1780 case SB_PAGELEFT:
1781 page = descr->width / descr->column_width;
1782 if (page < 1) page = 1;
1783 LISTBOX_SetTopItem( wnd, descr,
1784 descr->top_item - page * descr->page_size, TRUE );
1785 break;
1786 case SB_PAGERIGHT:
1787 page = descr->width / descr->column_width;
1788 if (page < 1) page = 1;
1789 LISTBOX_SetTopItem( wnd, descr,
1790 descr->top_item + page * descr->page_size, TRUE );
1791 break;
1792 case SB_THUMBPOSITION:
1793 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1794 TRUE );
1795 break;
1796 case SB_THUMBTRACK:
1797 info.cbSize = sizeof(info);
1798 info.fMask = SIF_TRACKPOS;
1799 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1800 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1801 TRUE );
1802 break;
1803 case SB_LEFT:
1804 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1805 break;
1806 case SB_RIGHT:
1807 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1808 break;
1811 else if (descr->horz_extent)
1813 switch(LOWORD(wParam))
1815 case SB_LINELEFT:
1816 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1817 break;
1818 case SB_LINERIGHT:
1819 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1820 break;
1821 case SB_PAGELEFT:
1822 LISTBOX_SetHorizontalPos( wnd, descr,
1823 descr->horz_pos - descr->width );
1824 break;
1825 case SB_PAGERIGHT:
1826 LISTBOX_SetHorizontalPos( wnd, descr,
1827 descr->horz_pos + descr->width );
1828 break;
1829 case SB_THUMBPOSITION:
1830 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1831 break;
1832 case SB_THUMBTRACK:
1833 info.cbSize = sizeof(info);
1834 info.fMask = SIF_TRACKPOS;
1835 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1836 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1837 break;
1838 case SB_LEFT:
1839 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1840 break;
1841 case SB_RIGHT:
1842 LISTBOX_SetHorizontalPos( wnd, descr,
1843 descr->horz_extent - descr->width );
1844 break;
1847 return 0;
1850 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1852 short gcWheelDelta = 0;
1853 UINT pulScrollLines = 3;
1855 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1857 gcWheelDelta -= (short) HIWORD(wParam);
1859 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1861 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1862 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1863 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1865 return 0;
1868 /***********************************************************************
1869 * LISTBOX_HandleLButtonDown
1871 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1872 WPARAM wParam, INT x, INT y )
1874 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1875 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1876 wnd->hwndSelf, x, y, index );
1877 if (!descr->caret_on && (descr->in_focus)) return 0;
1879 if (!descr->in_focus)
1881 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1882 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1883 : descr->lphc->self->hwndSelf );
1886 if (index != -1)
1888 if (descr->style & LBS_EXTENDEDSEL)
1890 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1891 if (wParam & MK_CONTROL)
1893 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1894 LISTBOX_SetSelection( wnd, descr, index,
1895 !descr->items[index].selected,
1896 (descr->style & LBS_NOTIFY) != 0);
1898 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1900 else
1902 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1903 LISTBOX_SetSelection( wnd, descr, index,
1904 (!(descr->style & LBS_MULTIPLESEL) ||
1905 !descr->items[index].selected),
1906 (descr->style & LBS_NOTIFY) != 0 );
1910 descr->captured = TRUE;
1911 SetCapture( wnd->hwndSelf );
1912 if (index != -1 && !descr->lphc)
1914 if (descr->style & LBS_NOTIFY )
1915 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1916 MAKELPARAM( x, y ) );
1917 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1919 POINT pt;
1921 pt.x = x;
1922 pt.y = y;
1924 if (DragDetect( wnd->hwndSelf, pt ))
1925 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1928 return 0;
1932 /*************************************************************************
1933 * LISTBOX_HandleLButtonDownCombo [Internal]
1935 * Process LButtonDown message for the ComboListBox
1937 * PARAMS
1938 * pWnd [I] The windows internal structure
1939 * pDescr [I] The ListBox internal structure
1940 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1941 * x [I] X Mouse Coordinate
1942 * y [I] Y Mouse Coordinate
1944 * RETURNS
1945 * 0 since we are processing the WM_LBUTTONDOWN Message
1947 * NOTES
1948 * This function is only to be used when a ListBox is a ComboListBox
1951 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1952 UINT msg, WPARAM wParam, INT x, INT y)
1954 RECT clientRect, screenRect;
1955 POINT mousePos;
1957 mousePos.x = x;
1958 mousePos.y = y;
1960 GetClientRect(pWnd->hwndSelf, &clientRect);
1962 if(PtInRect(&clientRect, mousePos))
1964 /* MousePos is in client, resume normal processing */
1965 if (msg == WM_LBUTTONDOWN)
1967 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1968 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1970 else if (pDescr->style & LBS_NOTIFY)
1971 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1972 return 0;
1974 else
1976 POINT screenMousePos;
1977 HWND hWndOldCapture;
1979 /* Check the Non-Client Area */
1980 screenMousePos = mousePos;
1981 hWndOldCapture = GetCapture();
1982 ReleaseCapture();
1983 GetWindowRect(pWnd->hwndSelf, &screenRect);
1984 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1986 if(!PtInRect(&screenRect, screenMousePos))
1988 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
1989 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
1990 return 0;
1992 else
1994 /* Check to see the NC is a scrollbar */
1995 INT nHitTestType=0;
1996 /* Check Vertical scroll bar */
1997 if (pWnd->dwStyle & WS_VSCROLL)
1999 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2000 if (PtInRect( &clientRect, mousePos ))
2002 nHitTestType = HTVSCROLL;
2005 /* Check horizontal scroll bar */
2006 if (pWnd->dwStyle & WS_HSCROLL)
2008 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2009 if (PtInRect( &clientRect, mousePos ))
2011 nHitTestType = HTHSCROLL;
2014 /* Windows sends this message when a scrollbar is clicked
2017 if(nHitTestType != 0)
2019 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2020 MAKELONG(screenMousePos.x, screenMousePos.y));
2022 /* Resume the Capture after scrolling is complete
2024 if(hWndOldCapture != 0)
2026 SetCapture(hWndOldCapture);
2030 return 0;
2033 /***********************************************************************
2034 * LISTBOX_HandleLButtonUp
2036 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2038 if (LISTBOX_Timer != LB_TIMER_NONE)
2039 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2040 LISTBOX_Timer = LB_TIMER_NONE;
2041 if (descr->captured)
2043 descr->captured = FALSE;
2044 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2045 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2046 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2048 return 0;
2052 /***********************************************************************
2053 * LISTBOX_HandleTimer
2055 * Handle scrolling upon a timer event.
2056 * Return TRUE if scrolling should continue.
2058 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2059 INT index, TIMER_DIRECTION dir )
2061 switch(dir)
2063 case LB_TIMER_UP:
2064 if (descr->top_item) index = descr->top_item - 1;
2065 else index = 0;
2066 break;
2067 case LB_TIMER_LEFT:
2068 if (descr->top_item) index -= descr->page_size;
2069 break;
2070 case LB_TIMER_DOWN:
2071 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2072 if (index == descr->focus_item) index++;
2073 if (index >= descr->nb_items) index = descr->nb_items - 1;
2074 break;
2075 case LB_TIMER_RIGHT:
2076 if (index + descr->page_size < descr->nb_items)
2077 index += descr->page_size;
2078 break;
2079 case LB_TIMER_NONE:
2080 break;
2082 if (index == descr->focus_item) return FALSE;
2083 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2084 return TRUE;
2088 /***********************************************************************
2089 * LISTBOX_HandleSystemTimer
2091 * WM_SYSTIMER handler.
2093 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2095 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2097 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2098 LISTBOX_Timer = LB_TIMER_NONE;
2100 return 0;
2104 /***********************************************************************
2105 * LISTBOX_HandleMouseMove
2107 * WM_MOUSEMOVE handler.
2109 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2110 INT x, INT y )
2112 INT index;
2113 TIMER_DIRECTION dir = LB_TIMER_NONE;
2115 if (!descr->captured) return;
2117 if (descr->style & LBS_MULTICOLUMN)
2119 if (y < 0) y = 0;
2120 else if (y >= descr->item_height * descr->page_size)
2121 y = descr->item_height * descr->page_size - 1;
2123 if (x < 0)
2125 dir = LB_TIMER_LEFT;
2126 x = 0;
2128 else if (x >= descr->width)
2130 dir = LB_TIMER_RIGHT;
2131 x = descr->width - 1;
2134 else
2136 if (y < 0) dir = LB_TIMER_UP; /* above */
2137 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2140 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2141 if (index == -1) index = descr->focus_item;
2142 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2144 /* Start/stop the system timer */
2146 if (dir != LB_TIMER_NONE)
2147 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2148 else if (LISTBOX_Timer != LB_TIMER_NONE)
2149 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2150 LISTBOX_Timer = dir;
2154 /***********************************************************************
2155 * LISTBOX_HandleKeyDown
2157 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2159 INT caret = -1;
2160 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2161 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2162 bForceSelection = FALSE; /* only for single select list */
2164 if (descr->style & LBS_WANTKEYBOARDINPUT)
2166 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2167 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2168 wnd->hwndSelf );
2169 if (caret == -2) return 0;
2171 if (caret == -1) switch(wParam)
2173 case VK_LEFT:
2174 if (descr->style & LBS_MULTICOLUMN)
2176 bForceSelection = FALSE;
2177 if (descr->focus_item >= descr->page_size)
2178 caret = descr->focus_item - descr->page_size;
2179 break;
2181 /* fall through */
2182 case VK_UP:
2183 caret = descr->focus_item - 1;
2184 if (caret < 0) caret = 0;
2185 break;
2186 case VK_RIGHT:
2187 if (descr->style & LBS_MULTICOLUMN)
2189 bForceSelection = FALSE;
2190 if (descr->focus_item + descr->page_size < descr->nb_items)
2191 caret = descr->focus_item + descr->page_size;
2192 break;
2194 /* fall through */
2195 case VK_DOWN:
2196 caret = descr->focus_item + 1;
2197 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2198 break;
2200 case VK_PRIOR:
2201 if (descr->style & LBS_MULTICOLUMN)
2203 INT page = descr->width / descr->column_width;
2204 if (page < 1) page = 1;
2205 caret = descr->focus_item - (page * descr->page_size) + 1;
2207 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2208 if (caret < 0) caret = 0;
2209 break;
2210 case VK_NEXT:
2211 if (descr->style & LBS_MULTICOLUMN)
2213 INT page = descr->width / descr->column_width;
2214 if (page < 1) page = 1;
2215 caret = descr->focus_item + (page * descr->page_size) - 1;
2217 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2218 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2219 break;
2220 case VK_HOME:
2221 caret = 0;
2222 break;
2223 case VK_END:
2224 caret = descr->nb_items - 1;
2225 break;
2226 case VK_SPACE:
2227 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2228 else if (descr->style & LBS_MULTIPLESEL)
2230 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2231 !descr->items[descr->focus_item].selected,
2232 (descr->style & LBS_NOTIFY) != 0 );
2234 break;
2235 default:
2236 bForceSelection = FALSE;
2238 if (bForceSelection) /* focused item is used instead of key */
2239 caret = descr->focus_item;
2240 if (caret >= 0)
2242 if ((descr->style & LBS_EXTENDEDSEL) &&
2243 !(GetKeyState( VK_SHIFT ) & 0x8000))
2244 descr->anchor_item = caret;
2245 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2246 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2247 if (descr->style & LBS_NOTIFY)
2249 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2251 /* make sure that combo parent doesn't hide us */
2252 descr->lphc->wState |= CBF_NOROLLUP;
2254 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2257 return 0;
2261 /***********************************************************************
2262 * LISTBOX_HandleChar
2264 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2265 WPARAM wParam )
2267 INT caret = -1;
2268 char str[2];
2270 str[0] = wParam & 0xff;
2271 str[1] = '\0';
2273 if (descr->style & LBS_WANTKEYBOARDINPUT)
2275 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2276 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2277 wnd->hwndSelf );
2278 if (caret == -2) return 0;
2280 if (caret == -1)
2281 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2282 if (caret != -1)
2284 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2285 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2286 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2287 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2288 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2290 return 0;
2294 /***********************************************************************
2295 * LISTBOX_Create
2297 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2299 LB_DESCR *descr;
2300 MEASUREITEMSTRUCT mis;
2301 RECT rect;
2303 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2304 return FALSE;
2305 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2307 HeapFree( GetProcessHeap(), 0, descr );
2308 return FALSE;
2310 GetClientRect( wnd->hwndSelf, &rect );
2311 descr->owner = GetParent( wnd->hwndSelf );
2312 descr->style = wnd->dwStyle;
2313 descr->width = rect.right - rect.left;
2314 descr->height = rect.bottom - rect.top;
2315 descr->items = NULL;
2316 descr->nb_items = 0;
2317 descr->top_item = 0;
2318 descr->selected_item = -1;
2319 descr->focus_item = 0;
2320 descr->anchor_item = -1;
2321 descr->item_height = 1;
2322 descr->page_size = 1;
2323 descr->column_width = 150;
2324 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2325 descr->horz_pos = 0;
2326 descr->nb_tabs = 0;
2327 descr->tabs = NULL;
2328 descr->caret_on = lphc ? FALSE : TRUE;
2329 descr->in_focus = FALSE;
2330 descr->captured = FALSE;
2331 descr->font = 0;
2332 descr->locale = 0; /* FIXME */
2333 descr->lphc = lphc;
2335 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2336 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2338 /* Win95 document "List Box Differences" from MSDN:
2339 If a list box in a version 3.x application has either the
2340 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2341 horizontal and vertical scroll bars.
2343 descr->style |= WS_VSCROLL | WS_HSCROLL;
2346 if( lphc )
2348 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2349 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2350 descr->owner = lphc->self->hwndSelf;
2353 *(LB_DESCR **)wnd->wExtra = descr;
2355 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2357 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2358 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2359 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2360 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2362 if (descr->style & LBS_OWNERDRAWFIXED)
2364 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2366 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2367 descr->item_height = lphc->fixedOwnerDrawHeight;
2369 else
2371 mis.CtlType = ODT_LISTBOX;
2372 mis.CtlID = wnd->wIDmenu;
2373 mis.itemID = -1;
2374 mis.itemWidth = 0;
2375 mis.itemData = 0;
2376 mis.itemHeight = descr->item_height;
2377 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2378 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2382 return TRUE;
2386 /***********************************************************************
2387 * LISTBOX_Destroy
2389 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2391 LISTBOX_ResetContent( wnd, descr );
2392 HeapDestroy( descr->heap );
2393 HeapFree( GetProcessHeap(), 0, descr );
2394 wnd->wExtra[0] = 0;
2395 return TRUE;
2399 /***********************************************************************
2400 * ListBoxWndProc
2402 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2403 WPARAM wParam, LPARAM lParam )
2405 LRESULT ret;
2406 LB_DESCR *descr;
2407 HWND hwnd = wnd->hwndSelf;
2409 if (!wnd) return 0;
2410 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2412 switch (msg)
2414 case WM_CREATE:
2416 if (!LISTBOX_Create( wnd, NULL ))
2417 return -1;
2418 TRACE("creating wnd=%04x descr=%p\n",
2419 hwnd, *(LB_DESCR **)wnd->wExtra );
2420 return 0;
2422 case WM_NCCREATE:
2425 * When a listbox is not in a combobox and the look
2426 * is win95, the WS_BORDER style is replaced with
2427 * the WS_EX_CLIENTEDGE style.
2429 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2430 (wnd->dwStyle & WS_BORDER) )
2432 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2433 wnd->dwStyle &= ~ WS_BORDER;
2438 /* Ignore all other messages before we get a WM_CREATE */
2439 return DefWindowProcA( hwnd, msg, wParam, lParam );
2442 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2443 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2444 switch(msg)
2446 case LB_RESETCONTENT16:
2447 case LB_RESETCONTENT:
2448 LISTBOX_ResetContent( wnd, descr );
2449 return 0;
2451 case LB_ADDSTRING16:
2452 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2453 /* fall through */
2454 case LB_ADDSTRING:
2455 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2456 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2458 case LB_INSERTSTRING16:
2459 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2460 wParam = (INT)(INT16)wParam;
2461 /* fall through */
2462 case LB_INSERTSTRING:
2463 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2465 case LB_ADDFILE16:
2466 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2467 /* fall through */
2468 case LB_ADDFILE:
2469 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2470 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2472 case LB_DELETESTRING16:
2473 case LB_DELETESTRING:
2474 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2475 return descr->nb_items;
2476 else
2477 return LB_ERR;
2479 case LB_GETITEMDATA16:
2480 case LB_GETITEMDATA:
2481 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2482 return LB_ERR;
2483 return descr->items[wParam].data;
2485 case LB_SETITEMDATA16:
2486 case LB_SETITEMDATA:
2487 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2488 return LB_ERR;
2489 descr->items[wParam].data = (DWORD)lParam;
2490 return LB_OKAY;
2492 case LB_GETCOUNT16:
2493 case LB_GETCOUNT:
2494 return descr->nb_items;
2496 case LB_GETTEXT16:
2497 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2498 /* fall through */
2499 case LB_GETTEXT:
2500 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2502 case LB_GETTEXTLEN16:
2503 /* fall through */
2504 case LB_GETTEXTLEN:
2505 if (wParam >= descr->nb_items)
2506 return LB_ERR;
2507 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2508 : sizeof(DWORD));
2510 case LB_GETCURSEL16:
2511 case LB_GETCURSEL:
2512 if (descr->nb_items==0)
2513 return LB_ERR;
2514 if (!IS_MULTISELECT(descr))
2515 return descr->selected_item;
2516 /* else */
2517 if (descr->selected_item!=-1)
2518 return descr->selected_item;
2519 /* else */
2520 return descr->focus_item;
2521 /* otherwise, if the user tries to move the selection with the */
2522 /* arrow keys, we will give the application something to choke on */
2523 case LB_GETTOPINDEX16:
2524 case LB_GETTOPINDEX:
2525 return descr->top_item;
2527 case LB_GETITEMHEIGHT16:
2528 case LB_GETITEMHEIGHT:
2529 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2531 case LB_SETITEMHEIGHT16:
2532 lParam = LOWORD(lParam);
2533 /* fall through */
2534 case LB_SETITEMHEIGHT:
2535 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2537 case LB_ITEMFROMPOINT:
2539 POINT pt;
2540 RECT rect;
2542 pt.x = LOWORD(lParam);
2543 pt.y = HIWORD(lParam);
2544 rect.left = 0;
2545 rect.top = 0;
2546 rect.right = descr->width;
2547 rect.bottom = descr->height;
2549 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2550 !PtInRect( &rect, pt ) );
2553 case LB_SETCARETINDEX16:
2554 case LB_SETCARETINDEX:
2555 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2556 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2557 return LB_ERR;
2558 else if (ISWIN31)
2559 return wParam;
2560 else
2561 return LB_OKAY;
2563 case LB_GETCARETINDEX16:
2564 case LB_GETCARETINDEX:
2565 return descr->focus_item;
2567 case LB_SETTOPINDEX16:
2568 case LB_SETTOPINDEX:
2569 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2571 case LB_SETCOLUMNWIDTH16:
2572 case LB_SETCOLUMNWIDTH:
2573 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2575 case LB_GETITEMRECT16:
2577 RECT rect;
2578 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2579 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2581 return ret;
2583 case LB_GETITEMRECT:
2584 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2586 case LB_FINDSTRING16:
2587 wParam = (INT)(INT16)wParam;
2588 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2589 /* fall through */
2590 case LB_FINDSTRING:
2591 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2593 case LB_FINDSTRINGEXACT16:
2594 wParam = (INT)(INT16)wParam;
2595 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2596 /* fall through */
2597 case LB_FINDSTRINGEXACT:
2598 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2600 case LB_SELECTSTRING16:
2601 wParam = (INT)(INT16)wParam;
2602 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2603 /* fall through */
2604 case LB_SELECTSTRING:
2606 INT index = LISTBOX_FindString( wnd, descr, wParam,
2607 (LPCSTR)lParam, FALSE );
2608 if (index == LB_ERR)
2609 return LB_ERR;
2610 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2611 return index;
2614 case LB_GETSEL16:
2615 wParam = (INT)(INT16)wParam;
2616 /* fall through */
2617 case LB_GETSEL:
2618 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2619 return LB_ERR;
2620 return descr->items[wParam].selected;
2622 case LB_SETSEL16:
2623 lParam = (INT)(INT16)lParam;
2624 /* fall through */
2625 case LB_SETSEL:
2626 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2628 case LB_SETCURSEL16:
2629 wParam = (INT)(INT16)wParam;
2630 /* fall through */
2631 case LB_SETCURSEL:
2632 if (IS_MULTISELECT(descr)) return LB_ERR;
2633 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2634 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2636 case LB_GETSELCOUNT16:
2637 case LB_GETSELCOUNT:
2638 return LISTBOX_GetSelCount( wnd, descr );
2640 case LB_GETSELITEMS16:
2641 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2642 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2644 case LB_GETSELITEMS:
2645 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2647 case LB_SELITEMRANGE16:
2648 case LB_SELITEMRANGE:
2649 if (LOWORD(lParam) <= HIWORD(lParam))
2650 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2651 HIWORD(lParam), wParam );
2652 else
2653 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2654 LOWORD(lParam), wParam );
2656 case LB_SELITEMRANGEEX16:
2657 case LB_SELITEMRANGEEX:
2658 if ((INT)lParam >= (INT)wParam)
2659 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2660 else
2661 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2663 case LB_GETHORIZONTALEXTENT16:
2664 case LB_GETHORIZONTALEXTENT:
2665 return descr->horz_extent;
2667 case LB_SETHORIZONTALEXTENT16:
2668 case LB_SETHORIZONTALEXTENT:
2669 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2671 case LB_GETANCHORINDEX16:
2672 case LB_GETANCHORINDEX:
2673 return descr->anchor_item;
2675 case LB_SETANCHORINDEX16:
2676 wParam = (INT)(INT16)wParam;
2677 /* fall through */
2678 case LB_SETANCHORINDEX:
2679 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2680 return LB_ERR;
2681 descr->anchor_item = (INT)wParam;
2682 return LB_OKAY;
2684 case LB_DIR16:
2685 return LISTBOX_Directory( wnd, descr, wParam,
2686 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2688 case LB_DIR:
2689 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2691 case LB_GETLOCALE:
2692 return descr->locale;
2694 case LB_SETLOCALE:
2695 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2696 return LB_OKAY;
2698 case LB_INITSTORAGE:
2699 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2701 case LB_SETCOUNT:
2702 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2704 case LB_SETTABSTOPS16:
2705 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2706 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2708 case LB_SETTABSTOPS:
2709 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2711 case LB_CARETON16:
2712 case LB_CARETON:
2713 if (descr->caret_on)
2714 return LB_OKAY;
2715 descr->caret_on = TRUE;
2716 if ((descr->focus_item != -1) && (descr->in_focus))
2717 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2718 return LB_OKAY;
2720 case LB_CARETOFF16:
2721 case LB_CARETOFF:
2722 if (!descr->caret_on)
2723 return LB_OKAY;
2724 descr->caret_on = FALSE;
2725 if ((descr->focus_item != -1) && (descr->in_focus))
2726 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2727 return LB_OKAY;
2729 case WM_DESTROY:
2730 return LISTBOX_Destroy( wnd, descr );
2732 case WM_ENABLE:
2733 InvalidateRect( hwnd, NULL, TRUE );
2734 return 0;
2736 case WM_SETREDRAW:
2737 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2738 return 0;
2740 case WM_GETDLGCODE:
2741 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2743 case WM_PAINT:
2745 PAINTSTRUCT ps;
2746 HDC hdc = ( wParam ) ? ((HDC)wParam)
2747 : BeginPaint( hwnd, &ps );
2748 ret = LISTBOX_Paint( wnd, descr, hdc );
2749 if( !wParam ) EndPaint( hwnd, &ps );
2751 return ret;
2752 case WM_SIZE:
2753 LISTBOX_UpdateSize( wnd, descr );
2754 return 0;
2755 case WM_GETFONT:
2756 return descr->font;
2757 case WM_SETFONT:
2758 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2759 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2760 return 0;
2761 case WM_SETFOCUS:
2762 descr->in_focus = TRUE;
2763 descr->caret_on = TRUE;
2764 if (descr->focus_item != -1)
2765 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2766 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2767 return 0;
2768 case WM_KILLFOCUS:
2769 descr->in_focus = FALSE;
2770 if ((descr->focus_item != -1) && descr->caret_on)
2771 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2772 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2773 return 0;
2774 case WM_HSCROLL:
2775 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2776 case WM_VSCROLL:
2777 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2778 case WM_MOUSEACTIVATE:
2779 return MA_NOACTIVATE;
2780 case WM_MOUSEWHEEL:
2781 if (wParam & (MK_SHIFT | MK_CONTROL))
2782 return DefWindowProcA( hwnd, msg, wParam, lParam );
2783 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2784 case WM_LBUTTONDOWN:
2785 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2786 (INT16)LOWORD(lParam),
2787 (INT16)HIWORD(lParam) );
2788 case WM_LBUTTONDBLCLK:
2789 if (descr->style & LBS_NOTIFY)
2790 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2791 return 0;
2792 case WM_MOUSEMOVE:
2793 if (GetCapture() == hwnd)
2794 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2795 (INT16)HIWORD(lParam) );
2796 return 0;
2797 case WM_LBUTTONUP:
2798 return LISTBOX_HandleLButtonUp( wnd, descr );
2799 case WM_KEYDOWN:
2800 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2801 case WM_CHAR:
2802 return LISTBOX_HandleChar( wnd, descr, wParam );
2803 case WM_SYSTIMER:
2804 return LISTBOX_HandleSystemTimer( wnd, descr );
2805 case WM_ERASEBKGND:
2806 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2808 RECT rect;
2809 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2810 wParam, (LPARAM)wnd->hwndSelf );
2811 GetClientRect(hwnd, &rect);
2812 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2814 return 1;
2815 case WM_DROPFILES:
2816 if( !descr->lphc )
2817 return SendMessageA( descr->owner, msg, wParam, lParam );
2818 break;
2820 case WM_DROPOBJECT:
2821 case WM_QUERYDROPOBJECT:
2822 case WM_DRAGSELECT:
2823 case WM_DRAGMOVE:
2824 if( !descr->lphc )
2826 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2827 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2828 dragInfo->pt.y );
2829 return SendMessageA( descr->owner, msg, wParam, lParam );
2831 break;
2833 default:
2834 if ((msg >= WM_USER) && (msg < 0xc000))
2835 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2836 hwnd, msg, wParam, lParam );
2837 return DefWindowProcA( hwnd, msg, wParam, lParam );
2839 return 0;
2842 /***********************************************************************
2843 * ListBoxWndProc
2845 * This is just a wrapper for the real wndproc, it only does window locking
2846 * and unlocking.
2848 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2849 WPARAM wParam, LPARAM lParam )
2851 WND* wndPtr = WIN_FindWndPtr( hwnd );
2852 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2854 WIN_ReleaseWndPtr(wndPtr);
2855 return res;
2858 /***********************************************************************
2859 * COMBO_Directory
2861 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2863 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2865 if( wnd )
2867 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2868 if( descr )
2870 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2872 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2873 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2874 WIN_ReleaseWndPtr(wnd);
2875 return lRet;
2877 WIN_ReleaseWndPtr(wnd);
2879 return CB_ERR;
2882 /***********************************************************************
2883 * ComboLBWndProc_locked
2885 * The real combo listbox wndproc, but called with locked WND struct.
2887 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2888 WPARAM wParam, LPARAM lParam )
2890 LRESULT lRet = 0;
2891 HWND hwnd = wnd->hwndSelf;
2893 if (wnd)
2895 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2897 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2898 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2900 if( descr || msg == WM_CREATE )
2902 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2904 switch( msg )
2906 case WM_CREATE:
2907 #define lpcs ((LPCREATESTRUCTA)lParam)
2908 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2909 (UINT)lpcs->lpCreateParams);
2911 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2912 #undef lpcs
2913 return LISTBOX_Create( wnd, lphc );
2914 case WM_MOUSEMOVE:
2915 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2916 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2918 POINT mousePos;
2919 BOOL captured;
2920 RECT clientRect;
2922 mousePos.x = (INT16)LOWORD(lParam);
2923 mousePos.y = (INT16)HIWORD(lParam);
2926 * If we are in a dropdown combobox, we simulate that
2927 * the mouse is captured to show the tracking of the item.
2929 GetClientRect(hwnd, &clientRect);
2931 if (PtInRect( &clientRect, mousePos ))
2933 captured = descr->captured;
2934 descr->captured = TRUE;
2936 LISTBOX_HandleMouseMove( wnd, descr,
2937 mousePos.x, mousePos.y);
2939 descr->captured = captured;
2942 else
2944 LISTBOX_HandleMouseMove( wnd, descr,
2945 mousePos.x, mousePos.y);
2948 return 0;
2951 else
2954 * If we are in Win3.1 look, go with the default behavior.
2956 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2958 case WM_LBUTTONUP:
2959 if (TWEAK_WineLook > WIN31_LOOK)
2961 POINT mousePos;
2962 RECT clientRect;
2965 * If the mouse button "up" is not in the listbox,
2966 * we make sure there is no selection by re-selecting the
2967 * item that was selected when the listbox was made visible.
2969 mousePos.x = (INT16)LOWORD(lParam);
2970 mousePos.y = (INT16)HIWORD(lParam);
2972 GetClientRect(hwnd, &clientRect);
2975 * When the user clicks outside the combobox and the focus
2976 * is lost, the owning combobox will send a fake buttonup with
2977 * 0xFFFFFFF as the mouse location, we must also revert the
2978 * selection to the original selection.
2980 if ( (lParam == 0xFFFFFFFF) ||
2981 (!PtInRect( &clientRect, mousePos )) )
2983 LISTBOX_MoveCaret( wnd,
2984 descr,
2985 lphc->droppedIndex,
2986 FALSE );
2989 return LISTBOX_HandleLButtonUp( wnd, descr );
2990 case WM_LBUTTONDBLCLK:
2991 case WM_LBUTTONDOWN:
2992 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
2993 (INT16)LOWORD(lParam),
2994 (INT16)HIWORD(lParam) );
2995 case WM_MOUSEACTIVATE:
2996 return MA_NOACTIVATE;
2997 case WM_NCACTIVATE:
2998 return FALSE;
2999 case WM_KEYDOWN:
3000 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3002 /* for some reason(?) Windows makes it possible to
3003 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3005 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3006 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3007 && (wParam == VK_DOWN || wParam == VK_UP)) )
3009 COMBO_FlipListbox( lphc, FALSE, FALSE );
3010 return 0;
3013 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3015 case LB_SETCURSEL16:
3016 case LB_SETCURSEL:
3017 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
3018 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3019 return lRet;
3020 case WM_NCDESTROY:
3021 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3022 lphc->hWndLBox = 0;
3023 /* fall through */
3025 default:
3026 return ListBoxWndProc( hwnd, msg, wParam, lParam );
3029 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3031 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3033 return lRet;
3036 /***********************************************************************
3037 * ComboLBWndProc
3039 * NOTE: in Windows, winproc address of the ComboLBox is the same
3040 * as that of the Listbox.
3042 * This is just a wrapper for the real wndproc, it only does window locking
3043 * and unlocking.
3045 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3046 WPARAM wParam, LPARAM lParam )
3048 WND *wnd = WIN_FindWndPtr( hwnd );
3049 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3051 WIN_ReleaseWndPtr(wnd);
3052 return res;