Added middle mouse button handling.
[wine/multimedia.git] / controls / listbox.c
blob5ebd4981d7d50bc4690291bd7055c31d0bf5f24f
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;
353 if(descr->item_height != 0)
354 remaining = descr->height % descr->item_height;
355 else
356 remaining = 0;
357 if ((descr->height > descr->item_height) && remaining)
359 if (!(wnd->flags & WIN_ISWIN32))
360 { /* give a margin for error to 16 bits programs - if we need
361 less than the height of the nonclient area, round to the
362 *next* number of items */
363 int ncheight = wnd->rectWindow.bottom - wnd->rectWindow.top - descr->height;
364 if ((descr->item_height - remaining) <= ncheight)
365 remaining = remaining - descr->item_height;
367 TRACE("[%04x]: changing height %d -> %d\n",
368 wnd->hwndSelf, descr->height,
369 descr->height - remaining );
370 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
371 wnd->rectWindow.right - wnd->rectWindow.left,
372 wnd->rectWindow.bottom - wnd->rectWindow.top - remaining,
373 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
374 return;
377 TRACE("[%04x]: new size = %d,%d\n",
378 wnd->hwndSelf, descr->width, descr->height );
379 LISTBOX_UpdatePage( wnd, descr );
380 LISTBOX_UpdateScroll( wnd, descr );
384 /***********************************************************************
385 * LISTBOX_GetItemRect
387 * Get the rectangle enclosing an item, in listbox client coordinates.
388 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
390 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
391 RECT *rect )
393 /* Index <= 0 is legal even on empty listboxes */
394 if (index && (index >= descr->nb_items)) return -1;
395 SetRect( rect, 0, 0, descr->width, descr->height );
396 if (descr->style & LBS_MULTICOLUMN)
398 INT col = (index / descr->page_size) -
399 (descr->top_item / descr->page_size);
400 rect->left += col * descr->column_width;
401 rect->right = rect->left + descr->column_width;
402 rect->top += (index % descr->page_size) * descr->item_height;
403 rect->bottom = rect->top + descr->item_height;
405 else if (descr->style & LBS_OWNERDRAWVARIABLE)
407 INT i;
408 rect->right += descr->horz_pos;
409 if ((index >= 0) && (index < descr->nb_items))
411 if (index < descr->top_item)
413 for (i = descr->top_item-1; i >= index; i--)
414 rect->top -= descr->items[i].height;
416 else
418 for (i = descr->top_item; i < index; i++)
419 rect->top += descr->items[i].height;
421 rect->bottom = rect->top + descr->items[index].height;
425 else
427 rect->top += (index - descr->top_item) * descr->item_height;
428 rect->bottom = rect->top + descr->item_height;
429 rect->right += descr->horz_pos;
432 return ((rect->left < descr->width) && (rect->right > 0) &&
433 (rect->top < descr->height) && (rect->bottom > 0));
437 /***********************************************************************
438 * LISTBOX_GetItemFromPoint
440 * Return the item nearest from point (x,y) (in client coordinates).
442 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
443 INT x, INT y )
445 INT index = descr->top_item;
447 if (!descr->nb_items) return -1; /* No items */
448 if (descr->style & LBS_OWNERDRAWVARIABLE)
450 INT pos = 0;
451 if (y >= 0)
453 while (index < descr->nb_items)
455 if ((pos += descr->items[index].height) > y) break;
456 index++;
459 else
461 while (index > 0)
463 index--;
464 if ((pos -= descr->items[index].height) <= y) break;
468 else if (descr->style & LBS_MULTICOLUMN)
470 if (y >= descr->item_height * descr->page_size) return -1;
471 if (y >= 0) index += y / descr->item_height;
472 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
473 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
475 else
477 index += (y / descr->item_height);
479 if (index < 0) return 0;
480 if (index >= descr->nb_items) return -1;
481 return index;
485 /***********************************************************************
486 * LISTBOX_PaintItem
488 * Paint an item.
490 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
491 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
493 LB_ITEMDATA *item = NULL;
494 if (index < descr->nb_items) item = &descr->items[index];
496 if (IS_OWNERDRAW(descr))
498 DRAWITEMSTRUCT dis;
499 RECT r;
500 HRGN hrgn;
502 if (!item)
504 if (action == ODA_FOCUS)
505 DrawFocusRect( hdc, rect );
506 else
507 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
508 return;
511 /* some programs mess with the clipping region when
512 drawing the item, *and* restore the previous region
513 after they are done, so a region has better to exist
514 else everything ends clipped */
515 GetClientRect(wnd->hwndSelf, &r);
516 hrgn = CreateRectRgnIndirect(&r);
517 SelectClipRgn( hdc, hrgn);
518 DeleteObject( hrgn );
520 dis.CtlType = ODT_LISTBOX;
521 dis.CtlID = wnd->wIDmenu;
522 dis.hwndItem = wnd->hwndSelf;
523 dis.itemAction = action;
524 dis.hDC = hdc;
525 dis.itemID = index;
526 dis.itemState = 0;
527 if (item && item->selected) dis.itemState |= ODS_SELECTED;
528 if (!ignoreFocus && (descr->focus_item == index) &&
529 (descr->caret_on) &&
530 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
531 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
532 dis.itemData = item ? item->data : 0;
533 dis.rcItem = *rect;
534 TRACE("[%04x]: drawitem %d (%s) action=%02x "
535 "state=%02x rect=%d,%d-%d,%d\n",
536 wnd->hwndSelf, index, item ? item->str : "", action,
537 dis.itemState, rect->left, rect->top,
538 rect->right, rect->bottom );
539 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
541 else
543 COLORREF oldText = 0, oldBk = 0;
545 if (action == ODA_FOCUS)
547 DrawFocusRect( hdc, rect );
548 return;
550 if (item && item->selected)
552 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
553 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
556 TRACE("[%04x]: painting %d (%s) action=%02x "
557 "rect=%d,%d-%d,%d\n",
558 wnd->hwndSelf, index, item ? item->str : "", action,
559 rect->left, rect->top, rect->right, rect->bottom );
560 if (!item)
561 ExtTextOutA( hdc, rect->left + 1, rect->top,
562 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
563 else if (!(descr->style & LBS_USETABSTOPS))
564 ExtTextOutA( hdc, rect->left + 1, rect->top,
565 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
566 strlen(item->str), NULL );
567 else
569 /* Output empty string to paint background in the full width. */
570 ExtTextOutA( hdc, rect->left + 1, rect->top,
571 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
572 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
573 item->str, strlen(item->str),
574 descr->nb_tabs, descr->tabs, 0);
576 if (item && item->selected)
578 SetBkColor( hdc, oldBk );
579 SetTextColor( hdc, oldText );
581 if (!ignoreFocus && (descr->focus_item == index) &&
582 (descr->caret_on) &&
583 (descr->in_focus)) DrawFocusRect( hdc, rect );
588 /***********************************************************************
589 * LISTBOX_SetRedraw
591 * Change the redraw flag.
593 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
595 if (on)
597 if (!(descr->style & LBS_NOREDRAW)) return;
598 descr->style &= ~LBS_NOREDRAW;
599 if (descr->style & LBS_DISPLAYCHANGED)
600 { /* page was changed while setredraw false, refresh automatically */
601 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
602 if ((descr->top_item + descr->page_size) > descr->nb_items)
603 { /* reset top of page if less than number of items/page */
604 descr->top_item = descr->nb_items - descr->page_size;
605 if (descr->top_item < 0) descr->top_item = 0;
607 descr->style &= ~LBS_DISPLAYCHANGED;
609 LISTBOX_UpdateScroll( wnd, descr );
611 else descr->style |= LBS_NOREDRAW;
615 /***********************************************************************
616 * LISTBOX_RepaintItem
618 * Repaint a single item synchronously.
620 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
621 UINT action )
623 HDC hdc;
624 RECT rect;
625 HFONT oldFont = 0;
626 HBRUSH hbrush, oldBrush = 0;
628 /* Do not repaint the item if the item is not visible */
629 if (!IsWindowVisible(wnd->hwndSelf)) return;
630 if (descr->style & LBS_NOREDRAW)
632 descr->style |= LBS_DISPLAYCHANGED;
633 return;
635 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
636 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
637 if (descr->font) oldFont = SelectObject( hdc, descr->font );
638 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
639 hdc, (LPARAM)wnd->hwndSelf );
640 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
641 if (wnd->dwStyle & WS_DISABLED)
642 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
643 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
644 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action, FALSE );
645 if (oldFont) SelectObject( hdc, oldFont );
646 if (oldBrush) SelectObject( hdc, oldBrush );
647 ReleaseDC( wnd->hwndSelf, hdc );
651 /***********************************************************************
652 * LISTBOX_InitStorage
654 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
655 DWORD bytes )
657 LB_ITEMDATA *item;
659 nb_items += LB_ARRAY_GRANULARITY - 1;
660 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
661 if (descr->items)
662 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
663 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
664 nb_items * sizeof(LB_ITEMDATA) )))
666 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
667 return LB_ERRSPACE;
669 descr->items = item;
670 return LB_OKAY;
674 /***********************************************************************
675 * LISTBOX_SetTabStops
677 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
678 LPINT tabs, BOOL short_ints )
680 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
681 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
682 if (!(descr->nb_tabs = count))
684 descr->tabs = NULL;
685 return TRUE;
687 /* FIXME: count = 1 */
688 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
689 descr->nb_tabs * sizeof(INT) )))
690 return FALSE;
691 if (short_ints)
693 INT i;
694 LPINT16 p = (LPINT16)tabs;
696 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
697 for (i = 0; i < descr->nb_tabs; i++) {
698 descr->tabs[i] = *p++<<1; /* FIXME */
699 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
701 if (TRACE_ON(listbox)) DPRINTF("\n");
703 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
704 /* FIXME: repaint the window? */
705 return TRUE;
709 /***********************************************************************
710 * LISTBOX_GetText
712 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
713 LPSTR buffer )
715 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
716 if (HAS_STRINGS(descr))
718 if (!buffer)
719 return strlen(descr->items[index].str);
720 strcpy( buffer, descr->items[index].str );
721 return strlen(buffer);
722 } else {
723 if (buffer)
724 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
725 return sizeof(DWORD);
730 /***********************************************************************
731 * LISTBOX_FindStringPos
733 * Find the nearest string located before a given string in sort order.
734 * If 'exact' is TRUE, return an error if we don't get an exact match.
736 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
737 BOOL exact )
739 INT index, min, max, res = -1;
741 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
742 min = 0;
743 max = descr->nb_items;
744 while (min != max)
746 index = (min + max) / 2;
747 if (HAS_STRINGS(descr))
748 res = lstrcmpiA( descr->items[index].str, str );
749 else
751 COMPAREITEMSTRUCT cis;
753 cis.CtlType = ODT_LISTBOX;
754 cis.CtlID = wnd->wIDmenu;
755 cis.hwndItem = wnd->hwndSelf;
756 cis.itemID1 = index;
757 cis.itemData1 = descr->items[index].data;
758 cis.itemID2 = -1;
759 cis.itemData2 = (DWORD)str;
760 cis.dwLocaleId = descr->locale;
761 res = SendMessageA( descr->owner, WM_COMPAREITEM,
762 wnd->wIDmenu, (LPARAM)&cis );
764 if (!res) return index;
765 if (res > 0) max = index;
766 else min = index + 1;
768 return exact ? -1 : max;
772 /***********************************************************************
773 * LISTBOX_FindFileStrPos
775 * Find the nearest string located before a given string in directory
776 * sort order (i.e. first files, then directories, then drives).
778 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
780 INT min, max, res = -1;
782 if (!HAS_STRINGS(descr))
783 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
784 min = 0;
785 max = descr->nb_items;
786 while (min != max)
788 INT index = (min + max) / 2;
789 const char *p = descr->items[index].str;
790 if (*p == '[') /* drive or directory */
792 if (*str != '[') res = -1;
793 else if (p[1] == '-') /* drive */
795 if (str[1] == '-') res = str[2] - p[2];
796 else res = -1;
798 else /* directory */
800 if (str[1] == '-') res = 1;
801 else res = lstrcmpiA( str, p );
804 else /* filename */
806 if (*str == '[') res = 1;
807 else res = lstrcmpiA( str, p );
809 if (!res) return index;
810 if (res < 0) max = index;
811 else min = index + 1;
813 return max;
817 /***********************************************************************
818 * LISTBOX_FindString
820 * Find the item beginning with a given string.
822 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
823 LPCSTR str, BOOL exact )
825 INT i;
826 LB_ITEMDATA *item;
828 if (start >= descr->nb_items) start = -1;
829 item = descr->items + start + 1;
830 if (HAS_STRINGS(descr))
832 if (!str || ! str[0] ) return LB_ERR;
833 if (exact)
835 for (i = start + 1; i < descr->nb_items; i++, item++)
836 if (!lstrcmpiA( str, item->str )) return i;
837 for (i = 0, item = descr->items; i <= start; i++, item++)
838 if (!lstrcmpiA( str, item->str )) return i;
840 else
842 /* Special case for drives and directories: ignore prefix */
843 #define CHECK_DRIVE(item) \
844 if ((item)->str[0] == '[') \
846 if (!strncasecmp( str, (item)->str+1, len )) return i; \
847 if (((item)->str[1] == '-') && !strncasecmp(str,(item)->str+2,len)) \
848 return i; \
851 INT len = strlen(str);
852 for (i = start + 1; i < descr->nb_items; i++, item++)
854 if (!strncasecmp( str, item->str, len )) return i;
855 CHECK_DRIVE(item);
857 for (i = 0, item = descr->items; i <= start; i++, item++)
859 if (!strncasecmp( str, item->str, len )) return i;
860 CHECK_DRIVE(item);
862 #undef CHECK_DRIVE
865 else
867 if (exact && (descr->style & LBS_SORT))
868 /* If sorted, use a WM_COMPAREITEM binary search */
869 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
871 /* Otherwise use a linear search */
872 for (i = start + 1; i < descr->nb_items; i++, item++)
873 if (item->data == (DWORD)str) return i;
874 for (i = 0, item = descr->items; i <= start; i++, item++)
875 if (item->data == (DWORD)str) return i;
877 return LB_ERR;
881 /***********************************************************************
882 * LISTBOX_GetSelCount
884 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
886 INT i, count;
887 LB_ITEMDATA *item = descr->items;
889 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
890 for (i = count = 0; i < descr->nb_items; i++, item++)
891 if (item->selected) count++;
892 return count;
896 /***********************************************************************
897 * LISTBOX_GetSelItems16
899 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
900 LPINT16 array )
902 INT i, count;
903 LB_ITEMDATA *item = descr->items;
905 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
906 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
907 if (item->selected) array[count++] = (INT16)i;
908 return count;
912 /***********************************************************************
913 * LISTBOX_GetSelItems32
915 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
916 LPINT array )
918 INT i, count;
919 LB_ITEMDATA *item = descr->items;
921 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
922 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
923 if (item->selected) array[count++] = i;
924 return count;
928 /***********************************************************************
929 * LISTBOX_Paint
931 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
933 INT i, col_pos = descr->page_size - 1;
934 RECT rect;
935 RECT focusRect = {-1, -1, -1, -1};
936 HFONT oldFont = 0;
937 HBRUSH hbrush, oldBrush = 0;
939 if (descr->style & LBS_NOREDRAW) return 0;
941 SetRect( &rect, 0, 0, descr->width, descr->height );
942 if (descr->style & LBS_MULTICOLUMN)
943 rect.right = rect.left + descr->column_width;
944 else if (descr->horz_pos)
946 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
947 rect.right += descr->horz_pos;
950 if (descr->font) oldFont = SelectObject( hdc, descr->font );
951 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
952 hdc, (LPARAM)wnd->hwndSelf );
953 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
954 if (wnd->dwStyle & WS_DISABLED)
955 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
957 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
958 (descr->in_focus))
960 /* Special case for empty listbox: paint focus rect */
961 rect.bottom = rect.top + descr->item_height;
962 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
963 ODA_FOCUS, FALSE );
964 rect.top = rect.bottom;
967 /* Paint all the item, regarding the selection
968 Focus state will be painted after */
970 for (i = descr->top_item; i < descr->nb_items; i++)
972 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
973 rect.bottom = rect.top + descr->item_height;
974 else
975 rect.bottom = rect.top + descr->items[i].height;
977 if (i == descr->focus_item)
979 /* keep the focus rect, to paint the focus item after */
980 focusRect.left = rect.left;
981 focusRect.right = rect.right;
982 focusRect.top = rect.top;
983 focusRect.bottom = rect.bottom;
985 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
986 rect.top = rect.bottom;
988 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
990 if (!IS_OWNERDRAW(descr))
992 /* Clear the bottom of the column */
993 if (rect.top < descr->height)
995 rect.bottom = descr->height;
996 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
997 &rect, NULL, 0, NULL );
1001 /* Go to the next column */
1002 rect.left += descr->column_width;
1003 rect.right += descr->column_width;
1004 rect.top = 0;
1005 col_pos = descr->page_size - 1;
1007 else
1009 col_pos--;
1010 if (rect.top >= descr->height) break;
1014 /* Paint the focus item now */
1015 if (focusRect.top != focusRect.bottom && descr->caret_on)
1016 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1018 if (!IS_OWNERDRAW(descr))
1020 /* Clear the remainder of the client area */
1021 if (rect.top < descr->height)
1023 rect.bottom = descr->height;
1024 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1025 &rect, NULL, 0, NULL );
1027 if (rect.right < descr->width)
1029 rect.left = rect.right;
1030 rect.right = descr->width;
1031 rect.top = 0;
1032 rect.bottom = descr->height;
1033 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1034 &rect, NULL, 0, NULL );
1037 if (oldFont) SelectObject( hdc, oldFont );
1038 if (oldBrush) SelectObject( hdc, oldBrush );
1039 return 0;
1043 /***********************************************************************
1044 * LISTBOX_InvalidateItems
1046 * Invalidate all items from a given item. If the specified item is not
1047 * visible, nothing happens.
1049 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1051 RECT rect;
1053 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1055 if (descr->style & LBS_NOREDRAW)
1057 descr->style |= LBS_DISPLAYCHANGED;
1058 return;
1060 rect.bottom = descr->height;
1061 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1062 if (descr->style & LBS_MULTICOLUMN)
1064 /* Repaint the other columns */
1065 rect.left = rect.right;
1066 rect.right = descr->width;
1067 rect.top = 0;
1068 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1074 /***********************************************************************
1075 * LISTBOX_GetItemHeight
1077 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1079 if (descr->style & LBS_OWNERDRAWVARIABLE)
1081 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1082 return descr->items[index].height;
1084 else return descr->item_height;
1088 /***********************************************************************
1089 * LISTBOX_SetItemHeight
1091 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1092 UINT height )
1094 if (!height) height = 1;
1096 if (descr->style & LBS_OWNERDRAWVARIABLE)
1098 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1099 TRACE("[%04x]: item %d height = %d\n",
1100 wnd->hwndSelf, index, height );
1101 descr->items[index].height = height;
1102 LISTBOX_UpdateScroll( wnd, descr );
1103 LISTBOX_InvalidateItems( wnd, descr, index );
1105 else if (height != descr->item_height)
1107 TRACE("[%04x]: new height = %d\n",
1108 wnd->hwndSelf, height );
1109 descr->item_height = height;
1110 LISTBOX_UpdatePage( wnd, descr );
1111 LISTBOX_UpdateScroll( wnd, descr );
1112 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1114 return LB_OKAY;
1118 /***********************************************************************
1119 * LISTBOX_SetHorizontalPos
1121 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1123 INT diff;
1125 if (pos > descr->horz_extent - descr->width)
1126 pos = descr->horz_extent - descr->width;
1127 if (pos < 0) pos = 0;
1128 if (!(diff = descr->horz_pos - pos)) return;
1129 TRACE("[%04x]: new horz pos = %d\n",
1130 wnd->hwndSelf, pos );
1131 descr->horz_pos = pos;
1132 LISTBOX_UpdateScroll( wnd, descr );
1133 if (abs(diff) < descr->width)
1134 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1135 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1136 else
1137 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1141 /***********************************************************************
1142 * LISTBOX_SetHorizontalExtent
1144 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1145 UINT extent )
1147 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1148 return LB_OKAY;
1149 if (extent <= 0) extent = 1;
1150 if (extent == descr->horz_extent) return LB_OKAY;
1151 TRACE("[%04x]: new horz extent = %d\n",
1152 wnd->hwndSelf, extent );
1153 descr->horz_extent = extent;
1154 if (descr->horz_pos > extent - descr->width)
1155 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1156 else
1157 LISTBOX_UpdateScroll( wnd, descr );
1158 return LB_OKAY;
1162 /***********************************************************************
1163 * LISTBOX_SetColumnWidth
1165 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1167 if (width == descr->column_width) return LB_OKAY;
1168 TRACE("[%04x]: new column width = %d\n",
1169 wnd->hwndSelf, width );
1170 descr->column_width = width;
1171 LISTBOX_UpdatePage( wnd, descr );
1172 return LB_OKAY;
1176 /***********************************************************************
1177 * LISTBOX_SetFont
1179 * Returns the item height.
1181 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1183 HDC hdc;
1184 HFONT oldFont = 0;
1185 TEXTMETRICA tm;
1187 descr->font = font;
1189 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1191 ERR("unable to get DC.\n" );
1192 return 16;
1194 if (font) oldFont = SelectObject( hdc, font );
1195 GetTextMetricsA( hdc, &tm );
1196 if (oldFont) SelectObject( hdc, oldFont );
1197 ReleaseDC( wnd->hwndSelf, hdc );
1198 if (!IS_OWNERDRAW(descr))
1199 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1200 return tm.tmHeight ;
1204 /***********************************************************************
1205 * LISTBOX_MakeItemVisible
1207 * Make sure that a given item is partially or fully visible.
1209 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1210 BOOL fully )
1212 INT top;
1214 if (index <= descr->top_item) top = index;
1215 else if (descr->style & LBS_MULTICOLUMN)
1217 INT cols = descr->width;
1218 if (!fully) cols += descr->column_width - 1;
1219 if (cols >= descr->column_width) cols /= descr->column_width;
1220 else cols = 1;
1221 if (index < descr->top_item + (descr->page_size * cols)) return;
1222 top = index - descr->page_size * (cols - 1);
1224 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1226 INT height = fully ? descr->items[index].height : 1;
1227 for (top = index; top > descr->top_item; top--)
1228 if ((height += descr->items[top-1].height) > descr->height) break;
1230 else
1232 if (index < descr->top_item + descr->page_size) return;
1233 if (!fully && (index == descr->top_item + descr->page_size) &&
1234 (descr->height > (descr->page_size * descr->item_height))) return;
1235 top = index - descr->page_size + 1;
1237 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1240 /***********************************************************************
1241 * LISTBOX_SetCaretIndex
1243 * NOTES
1244 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1247 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1248 BOOL fully_visible )
1250 INT oldfocus = descr->focus_item;
1252 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1253 if (index == oldfocus) return LB_OKAY;
1254 descr->focus_item = index;
1255 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1256 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1258 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1259 if (descr->caret_on && (descr->in_focus))
1260 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1262 return LB_OKAY;
1266 /***********************************************************************
1267 * LISTBOX_SelectItemRange
1269 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1271 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1272 INT last, BOOL on )
1274 INT i;
1276 /* A few sanity checks */
1278 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1279 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1280 if (last == -1) last = descr->nb_items - 1;
1281 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1282 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1283 /* selected_item reflects last selected/unselected item on multiple sel */
1284 descr->selected_item = last;
1286 if (on) /* Turn selection on */
1288 for (i = first; i <= last; i++)
1290 if (descr->items[i].selected) continue;
1291 descr->items[i].selected = TRUE;
1292 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1294 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1296 else /* Turn selection off */
1298 for (i = first; i <= last; i++)
1300 if (!descr->items[i].selected) continue;
1301 descr->items[i].selected = FALSE;
1302 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1305 return LB_OKAY;
1308 /***********************************************************************
1309 * LISTBOX_SetSelection
1311 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1312 BOOL on, BOOL send_notify )
1314 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1316 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1317 if (descr->style & LBS_MULTIPLESEL)
1319 if (index == -1) /* Select all items */
1320 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1321 else /* Only one item */
1322 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1324 else
1326 INT oldsel = descr->selected_item;
1327 if (index == oldsel) return LB_OKAY;
1328 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1329 if (index != -1) descr->items[index].selected = TRUE;
1330 descr->selected_item = index;
1331 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1332 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1333 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1334 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1335 else
1336 if( descr->lphc ) /* set selection change flag for parent combo */
1337 descr->lphc->wState |= CBF_SELCHANGE;
1339 return LB_OKAY;
1343 /***********************************************************************
1344 * LISTBOX_MoveCaret
1346 * Change the caret position and extend the selection to the new caret.
1348 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1349 BOOL fully_visible )
1351 INT oldfocus = descr->focus_item;
1353 if ((index < 0) || (index >= descr->nb_items))
1354 return;
1356 /* Important, repaint needs to be done in this order if
1357 you want to mimic Windows behavior:
1358 1. Remove the focus and paint the item
1359 2. Remove the selection and paint the item(s)
1360 3. Set the selection and repaint the item(s)
1361 4. Set the focus to 'index' and repaint the item */
1363 /* 1. remove the focus and repaint the item */
1364 descr->focus_item = -1;
1365 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1366 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1368 /* 2. then turn off the previous selection */
1369 /* 3. repaint the new selected item */
1370 if (descr->style & LBS_EXTENDEDSEL)
1372 if (descr->anchor_item != -1)
1374 INT first = min( index, descr->anchor_item );
1375 INT last = max( index, descr->anchor_item );
1376 if (first > 0)
1377 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1378 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1379 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1382 else if (!(descr->style & LBS_MULTIPLESEL))
1384 /* Set selection to new caret item */
1385 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1388 /* 4. repaint the new item with the focus */
1389 descr->focus_item = index;
1390 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1391 if (descr->caret_on && (descr->in_focus))
1392 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1396 /***********************************************************************
1397 * LISTBOX_InsertItem
1399 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1400 LPSTR str, DWORD data )
1402 LB_ITEMDATA *item;
1403 INT max_items;
1404 INT oldfocus = descr->focus_item;
1406 if (index == -1) index = descr->nb_items;
1407 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1408 if (!descr->items) max_items = 0;
1409 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1410 if (descr->nb_items == max_items)
1412 /* We need to grow the array */
1413 max_items += LB_ARRAY_GRANULARITY;
1414 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1415 max_items * sizeof(LB_ITEMDATA) )))
1417 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1418 return LB_ERRSPACE;
1420 descr->items = item;
1423 /* Insert the item structure */
1425 item = &descr->items[index];
1426 if (index < descr->nb_items)
1427 RtlMoveMemory( item + 1, item,
1428 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1429 item->str = str;
1430 item->data = data;
1431 item->height = 0;
1432 item->selected = FALSE;
1433 descr->nb_items++;
1435 /* Get item height */
1437 if (descr->style & LBS_OWNERDRAWVARIABLE)
1439 MEASUREITEMSTRUCT mis;
1441 mis.CtlType = ODT_LISTBOX;
1442 mis.CtlID = wnd->wIDmenu;
1443 mis.itemID = index;
1444 mis.itemData = descr->items[index].data;
1445 mis.itemHeight = descr->item_height;
1446 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1447 item->height = mis.itemHeight ? mis.itemHeight : 1;
1448 TRACE("[%04x]: measure item %d (%s) = %d\n",
1449 wnd->hwndSelf, index, str ? str : "", item->height );
1452 /* Repaint the items */
1454 LISTBOX_UpdateScroll( wnd, descr );
1455 LISTBOX_InvalidateItems( wnd, descr, index );
1457 /* Move selection and focused item */
1458 /* If listbox was empty, set focus to the first item */
1459 if (descr->nb_items == 1)
1460 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1461 /* single select don't change selection index in win31 */
1462 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1464 descr->selected_item++;
1465 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1467 else
1469 if (index <= descr->selected_item)
1471 descr->selected_item++;
1472 descr->focus_item = oldfocus; /* focus not changed */
1475 return LB_OKAY;
1479 /***********************************************************************
1480 * LISTBOX_InsertString
1482 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1483 LPCSTR str )
1485 LPSTR new_str = NULL;
1486 DWORD data = 0;
1487 LRESULT ret;
1489 if (HAS_STRINGS(descr))
1491 if (!str) str="";
1492 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1494 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1495 return LB_ERRSPACE;
1498 else data = (DWORD)str;
1500 if (index == -1) index = descr->nb_items;
1501 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1503 if (new_str) HeapFree( descr->heap, 0, new_str );
1504 return ret;
1507 TRACE("[%04x]: added item %d '%s'\n",
1508 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1509 return index;
1513 /***********************************************************************
1514 * LISTBOX_DeleteItem
1516 * Delete the content of an item. 'index' must be a valid index.
1518 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1520 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1521 * while Win95 sends it for all items with user data.
1522 * It's probably better to send it too often than not
1523 * often enough, so this is what we do here.
1525 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1527 DELETEITEMSTRUCT dis;
1529 dis.CtlType = ODT_LISTBOX;
1530 dis.CtlID = wnd->wIDmenu;
1531 dis.itemID = index;
1532 dis.hwndItem = wnd->hwndSelf;
1533 dis.itemData = descr->items[index].data;
1534 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1536 if (HAS_STRINGS(descr) && descr->items[index].str)
1537 HeapFree( descr->heap, 0, descr->items[index].str );
1541 /***********************************************************************
1542 * LISTBOX_RemoveItem
1544 * Remove an item from the listbox and delete its content.
1546 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1548 LB_ITEMDATA *item;
1549 INT max_items;
1551 if (index == -1) index = descr->nb_items - 1;
1552 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1554 /* We need to invalidate the original rect instead of the updated one. */
1555 LISTBOX_InvalidateItems( wnd, descr, index );
1557 LISTBOX_DeleteItem( wnd, descr, index );
1559 /* Remove the item */
1561 item = &descr->items[index];
1562 if (index < descr->nb_items-1)
1563 RtlMoveMemory( item, item + 1,
1564 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1565 descr->nb_items--;
1566 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1568 /* Shrink the item array if possible */
1570 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1571 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1573 max_items -= LB_ARRAY_GRANULARITY;
1574 item = HeapReAlloc( descr->heap, 0, descr->items,
1575 max_items * sizeof(LB_ITEMDATA) );
1576 if (item) descr->items = item;
1578 /* Repaint the items */
1580 LISTBOX_UpdateScroll( wnd, descr );
1581 /* if we removed the scrollbar, reset the top of the list
1582 (correct for owner-drawn ???) */
1583 if (descr->nb_items == descr->page_size)
1584 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1586 /* Move selection and focused item */
1587 if (!IS_MULTISELECT(descr))
1589 if (index == descr->selected_item)
1590 descr->selected_item = -1;
1591 else if (index < descr->selected_item)
1593 descr->selected_item--;
1594 if (ISWIN31) /* win 31 do not change the selected item number */
1595 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1599 if (descr->focus_item >= descr->nb_items)
1601 descr->focus_item = descr->nb_items - 1;
1602 if (descr->focus_item < 0) descr->focus_item = 0;
1604 return LB_OKAY;
1608 /***********************************************************************
1609 * LISTBOX_ResetContent
1611 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1613 INT i;
1615 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1616 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1617 descr->nb_items = 0;
1618 descr->top_item = 0;
1619 descr->selected_item = -1;
1620 descr->focus_item = 0;
1621 descr->anchor_item = -1;
1622 descr->items = NULL;
1623 LISTBOX_UpdateScroll( wnd, descr );
1624 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1628 /***********************************************************************
1629 * LISTBOX_SetCount
1631 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1633 LRESULT ret;
1635 if (HAS_STRINGS(descr)) return LB_ERR;
1636 /* FIXME: this is far from optimal... */
1637 if (count > descr->nb_items)
1639 while (count > descr->nb_items)
1640 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1641 return ret;
1643 else if (count < descr->nb_items)
1645 while (count < descr->nb_items)
1646 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1647 return ret;
1649 return LB_OKAY;
1653 /***********************************************************************
1654 * LISTBOX_Directory
1656 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1657 LPCSTR filespec, BOOL long_names )
1659 HANDLE handle;
1660 LRESULT ret = LB_OKAY;
1661 WIN32_FIND_DATAA entry;
1662 int pos;
1664 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1666 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1668 else
1672 char buffer[270];
1673 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1675 if (!(attrib & DDL_DIRECTORY) ||
1676 !strcmp( entry.cAlternateFileName, "." )) continue;
1677 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1678 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1680 else /* not a directory */
1682 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1683 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1685 if ((attrib & DDL_EXCLUSIVE) &&
1686 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1687 continue;
1688 #undef ATTRIBS
1689 if (long_names) strcpy( buffer, entry.cFileName );
1690 else strcpy( buffer, entry.cAlternateFileName );
1692 if (!long_names) CharLowerA( buffer );
1693 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1694 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1695 break;
1696 } while (FindNextFileA( handle, &entry ));
1697 FindClose( handle );
1700 if ((ret >= 0) && (attrib & DDL_DRIVES))
1702 char buffer[] = "[-a-]";
1703 int drive;
1704 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1706 if (!DRIVE_IsValid(drive)) continue;
1707 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1708 break;
1711 return ret;
1715 /***********************************************************************
1716 * LISTBOX_HandleVScroll
1718 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1719 WPARAM wParam, LPARAM lParam )
1721 SCROLLINFO info;
1723 if (descr->style & LBS_MULTICOLUMN) return 0;
1724 switch(LOWORD(wParam))
1726 case SB_LINEUP:
1727 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1728 break;
1729 case SB_LINEDOWN:
1730 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1731 break;
1732 case SB_PAGEUP:
1733 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1734 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1735 break;
1736 case SB_PAGEDOWN:
1737 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1738 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1739 break;
1740 case SB_THUMBPOSITION:
1741 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1742 break;
1743 case SB_THUMBTRACK:
1744 info.cbSize = sizeof(info);
1745 info.fMask = SIF_TRACKPOS;
1746 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1747 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1748 break;
1749 case SB_TOP:
1750 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1751 break;
1752 case SB_BOTTOM:
1753 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1754 break;
1756 return 0;
1760 /***********************************************************************
1761 * LISTBOX_HandleHScroll
1763 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1764 WPARAM wParam, LPARAM lParam )
1766 SCROLLINFO info;
1767 INT page;
1769 if (descr->style & LBS_MULTICOLUMN)
1771 switch(LOWORD(wParam))
1773 case SB_LINELEFT:
1774 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1775 TRUE );
1776 break;
1777 case SB_LINERIGHT:
1778 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1779 TRUE );
1780 break;
1781 case SB_PAGELEFT:
1782 page = descr->width / descr->column_width;
1783 if (page < 1) page = 1;
1784 LISTBOX_SetTopItem( wnd, descr,
1785 descr->top_item - page * descr->page_size, TRUE );
1786 break;
1787 case SB_PAGERIGHT:
1788 page = descr->width / descr->column_width;
1789 if (page < 1) page = 1;
1790 LISTBOX_SetTopItem( wnd, descr,
1791 descr->top_item + page * descr->page_size, TRUE );
1792 break;
1793 case SB_THUMBPOSITION:
1794 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1795 TRUE );
1796 break;
1797 case SB_THUMBTRACK:
1798 info.cbSize = sizeof(info);
1799 info.fMask = SIF_TRACKPOS;
1800 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1801 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1802 TRUE );
1803 break;
1804 case SB_LEFT:
1805 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1806 break;
1807 case SB_RIGHT:
1808 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1809 break;
1812 else if (descr->horz_extent)
1814 switch(LOWORD(wParam))
1816 case SB_LINELEFT:
1817 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1818 break;
1819 case SB_LINERIGHT:
1820 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1821 break;
1822 case SB_PAGELEFT:
1823 LISTBOX_SetHorizontalPos( wnd, descr,
1824 descr->horz_pos - descr->width );
1825 break;
1826 case SB_PAGERIGHT:
1827 LISTBOX_SetHorizontalPos( wnd, descr,
1828 descr->horz_pos + descr->width );
1829 break;
1830 case SB_THUMBPOSITION:
1831 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1832 break;
1833 case SB_THUMBTRACK:
1834 info.cbSize = sizeof(info);
1835 info.fMask = SIF_TRACKPOS;
1836 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1837 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1838 break;
1839 case SB_LEFT:
1840 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1841 break;
1842 case SB_RIGHT:
1843 LISTBOX_SetHorizontalPos( wnd, descr,
1844 descr->horz_extent - descr->width );
1845 break;
1848 return 0;
1851 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1853 short gcWheelDelta = 0;
1854 UINT pulScrollLines = 3;
1856 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1858 gcWheelDelta -= (short) HIWORD(wParam);
1860 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1862 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1863 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1864 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1866 return 0;
1869 /***********************************************************************
1870 * LISTBOX_HandleLButtonDown
1872 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1873 WPARAM wParam, INT x, INT y )
1875 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1876 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1877 wnd->hwndSelf, x, y, index );
1878 if (!descr->caret_on && (descr->in_focus)) return 0;
1880 if (!descr->in_focus)
1882 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1883 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1884 : descr->lphc->self->hwndSelf );
1887 if (index != -1)
1889 if (descr->style & LBS_EXTENDEDSEL)
1891 /* we should perhaps make sure that all items are deselected
1892 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1893 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1894 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1897 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1898 if (wParam & MK_CONTROL)
1900 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1901 LISTBOX_SetSelection( wnd, descr, index,
1902 !descr->items[index].selected,
1903 (descr->style & LBS_NOTIFY) != 0);
1905 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1907 else
1909 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1910 LISTBOX_SetSelection( wnd, descr, index,
1911 (!(descr->style & LBS_MULTIPLESEL) ||
1912 !descr->items[index].selected),
1913 (descr->style & LBS_NOTIFY) != 0 );
1917 descr->captured = TRUE;
1918 SetCapture( wnd->hwndSelf );
1919 if (index != -1 && !descr->lphc)
1921 if (descr->style & LBS_NOTIFY )
1922 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1923 MAKELPARAM( x, y ) );
1924 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1926 POINT pt;
1928 pt.x = x;
1929 pt.y = y;
1931 if (DragDetect( wnd->hwndSelf, pt ))
1932 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1935 return 0;
1939 /*************************************************************************
1940 * LISTBOX_HandleLButtonDownCombo [Internal]
1942 * Process LButtonDown message for the ComboListBox
1944 * PARAMS
1945 * pWnd [I] The windows internal structure
1946 * pDescr [I] The ListBox internal structure
1947 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1948 * x [I] X Mouse Coordinate
1949 * y [I] Y Mouse Coordinate
1951 * RETURNS
1952 * 0 since we are processing the WM_LBUTTONDOWN Message
1954 * NOTES
1955 * This function is only to be used when a ListBox is a ComboListBox
1958 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1959 UINT msg, WPARAM wParam, INT x, INT y)
1961 RECT clientRect, screenRect;
1962 POINT mousePos;
1964 mousePos.x = x;
1965 mousePos.y = y;
1967 GetClientRect(pWnd->hwndSelf, &clientRect);
1969 if(PtInRect(&clientRect, mousePos))
1971 /* MousePos is in client, resume normal processing */
1972 if (msg == WM_LBUTTONDOWN)
1974 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1975 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1977 else if (pDescr->style & LBS_NOTIFY)
1978 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1979 return 0;
1981 else
1983 POINT screenMousePos;
1984 HWND hWndOldCapture;
1986 /* Check the Non-Client Area */
1987 screenMousePos = mousePos;
1988 hWndOldCapture = GetCapture();
1989 ReleaseCapture();
1990 GetWindowRect(pWnd->hwndSelf, &screenRect);
1991 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1993 if(!PtInRect(&screenRect, screenMousePos))
1995 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
1996 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
1997 return 0;
1999 else
2001 /* Check to see the NC is a scrollbar */
2002 INT nHitTestType=0;
2003 /* Check Vertical scroll bar */
2004 if (pWnd->dwStyle & WS_VSCROLL)
2006 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2007 if (PtInRect( &clientRect, mousePos ))
2009 nHitTestType = HTVSCROLL;
2012 /* Check horizontal scroll bar */
2013 if (pWnd->dwStyle & WS_HSCROLL)
2015 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2016 if (PtInRect( &clientRect, mousePos ))
2018 nHitTestType = HTHSCROLL;
2021 /* Windows sends this message when a scrollbar is clicked
2024 if(nHitTestType != 0)
2026 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2027 MAKELONG(screenMousePos.x, screenMousePos.y));
2029 /* Resume the Capture after scrolling is complete
2031 if(hWndOldCapture != 0)
2033 SetCapture(hWndOldCapture);
2037 return 0;
2040 /***********************************************************************
2041 * LISTBOX_HandleLButtonUp
2043 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2045 if (LISTBOX_Timer != LB_TIMER_NONE)
2046 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2047 LISTBOX_Timer = LB_TIMER_NONE;
2048 if (descr->captured)
2050 descr->captured = FALSE;
2051 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2052 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2053 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2055 return 0;
2059 /***********************************************************************
2060 * LISTBOX_HandleTimer
2062 * Handle scrolling upon a timer event.
2063 * Return TRUE if scrolling should continue.
2065 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2066 INT index, TIMER_DIRECTION dir )
2068 switch(dir)
2070 case LB_TIMER_UP:
2071 if (descr->top_item) index = descr->top_item - 1;
2072 else index = 0;
2073 break;
2074 case LB_TIMER_LEFT:
2075 if (descr->top_item) index -= descr->page_size;
2076 break;
2077 case LB_TIMER_DOWN:
2078 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2079 if (index == descr->focus_item) index++;
2080 if (index >= descr->nb_items) index = descr->nb_items - 1;
2081 break;
2082 case LB_TIMER_RIGHT:
2083 if (index + descr->page_size < descr->nb_items)
2084 index += descr->page_size;
2085 break;
2086 case LB_TIMER_NONE:
2087 break;
2089 if (index == descr->focus_item) return FALSE;
2090 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2091 return TRUE;
2095 /***********************************************************************
2096 * LISTBOX_HandleSystemTimer
2098 * WM_SYSTIMER handler.
2100 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2102 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2104 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2105 LISTBOX_Timer = LB_TIMER_NONE;
2107 return 0;
2111 /***********************************************************************
2112 * LISTBOX_HandleMouseMove
2114 * WM_MOUSEMOVE handler.
2116 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2117 INT x, INT y )
2119 INT index;
2120 TIMER_DIRECTION dir = LB_TIMER_NONE;
2122 if (!descr->captured) return;
2124 if (descr->style & LBS_MULTICOLUMN)
2126 if (y < 0) y = 0;
2127 else if (y >= descr->item_height * descr->page_size)
2128 y = descr->item_height * descr->page_size - 1;
2130 if (x < 0)
2132 dir = LB_TIMER_LEFT;
2133 x = 0;
2135 else if (x >= descr->width)
2137 dir = LB_TIMER_RIGHT;
2138 x = descr->width - 1;
2141 else
2143 if (y < 0) dir = LB_TIMER_UP; /* above */
2144 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2147 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2148 if (index == -1) index = descr->focus_item;
2149 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2151 /* Start/stop the system timer */
2153 if (dir != LB_TIMER_NONE)
2154 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2155 else if (LISTBOX_Timer != LB_TIMER_NONE)
2156 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2157 LISTBOX_Timer = dir;
2161 /***********************************************************************
2162 * LISTBOX_HandleKeyDown
2164 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2166 INT caret = -1;
2167 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2168 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2169 bForceSelection = FALSE; /* only for single select list */
2171 if (descr->style & LBS_WANTKEYBOARDINPUT)
2173 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2174 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2175 wnd->hwndSelf );
2176 if (caret == -2) return 0;
2178 if (caret == -1) switch(wParam)
2180 case VK_LEFT:
2181 if (descr->style & LBS_MULTICOLUMN)
2183 bForceSelection = FALSE;
2184 if (descr->focus_item >= descr->page_size)
2185 caret = descr->focus_item - descr->page_size;
2186 break;
2188 /* fall through */
2189 case VK_UP:
2190 caret = descr->focus_item - 1;
2191 if (caret < 0) caret = 0;
2192 break;
2193 case VK_RIGHT:
2194 if (descr->style & LBS_MULTICOLUMN)
2196 bForceSelection = FALSE;
2197 if (descr->focus_item + descr->page_size < descr->nb_items)
2198 caret = descr->focus_item + descr->page_size;
2199 break;
2201 /* fall through */
2202 case VK_DOWN:
2203 caret = descr->focus_item + 1;
2204 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2205 break;
2207 case VK_PRIOR:
2208 if (descr->style & LBS_MULTICOLUMN)
2210 INT page = descr->width / descr->column_width;
2211 if (page < 1) page = 1;
2212 caret = descr->focus_item - (page * descr->page_size) + 1;
2214 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2215 if (caret < 0) caret = 0;
2216 break;
2217 case VK_NEXT:
2218 if (descr->style & LBS_MULTICOLUMN)
2220 INT page = descr->width / descr->column_width;
2221 if (page < 1) page = 1;
2222 caret = descr->focus_item + (page * descr->page_size) - 1;
2224 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2225 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2226 break;
2227 case VK_HOME:
2228 caret = 0;
2229 break;
2230 case VK_END:
2231 caret = descr->nb_items - 1;
2232 break;
2233 case VK_SPACE:
2234 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2235 else if (descr->style & LBS_MULTIPLESEL)
2237 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2238 !descr->items[descr->focus_item].selected,
2239 (descr->style & LBS_NOTIFY) != 0 );
2241 break;
2242 default:
2243 bForceSelection = FALSE;
2245 if (bForceSelection) /* focused item is used instead of key */
2246 caret = descr->focus_item;
2247 if (caret >= 0)
2249 if ((descr->style & LBS_EXTENDEDSEL) &&
2250 !(GetKeyState( VK_SHIFT ) & 0x8000))
2251 descr->anchor_item = caret;
2252 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2253 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2254 if (descr->style & LBS_NOTIFY)
2256 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2258 /* make sure that combo parent doesn't hide us */
2259 descr->lphc->wState |= CBF_NOROLLUP;
2261 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2264 return 0;
2268 /***********************************************************************
2269 * LISTBOX_HandleChar
2271 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2272 WPARAM wParam )
2274 INT caret = -1;
2275 char str[2];
2277 str[0] = wParam & 0xff;
2278 str[1] = '\0';
2280 if (descr->style & LBS_WANTKEYBOARDINPUT)
2282 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2283 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2284 wnd->hwndSelf );
2285 if (caret == -2) return 0;
2287 if (caret == -1)
2288 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2289 if (caret != -1)
2291 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2292 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2293 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2294 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2295 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2297 return 0;
2301 /***********************************************************************
2302 * LISTBOX_Create
2304 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2306 LB_DESCR *descr;
2307 MEASUREITEMSTRUCT mis;
2308 RECT rect;
2310 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2311 return FALSE;
2312 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2314 HeapFree( GetProcessHeap(), 0, descr );
2315 return FALSE;
2317 GetClientRect( wnd->hwndSelf, &rect );
2318 descr->owner = GetParent( wnd->hwndSelf );
2319 descr->style = wnd->dwStyle;
2320 descr->width = rect.right - rect.left;
2321 descr->height = rect.bottom - rect.top;
2322 descr->items = NULL;
2323 descr->nb_items = 0;
2324 descr->top_item = 0;
2325 descr->selected_item = -1;
2326 descr->focus_item = 0;
2327 descr->anchor_item = -1;
2328 descr->item_height = 1;
2329 descr->page_size = 1;
2330 descr->column_width = 150;
2331 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2332 descr->horz_pos = 0;
2333 descr->nb_tabs = 0;
2334 descr->tabs = NULL;
2335 descr->caret_on = lphc ? FALSE : TRUE;
2336 descr->in_focus = FALSE;
2337 descr->captured = FALSE;
2338 descr->font = 0;
2339 descr->locale = 0; /* FIXME */
2340 descr->lphc = lphc;
2342 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2343 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2345 /* Win95 document "List Box Differences" from MSDN:
2346 If a list box in a version 3.x application has either the
2347 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2348 horizontal and vertical scroll bars.
2350 descr->style |= WS_VSCROLL | WS_HSCROLL;
2353 if( lphc )
2355 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2356 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2357 descr->owner = lphc->self->hwndSelf;
2360 *(LB_DESCR **)wnd->wExtra = descr;
2362 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2364 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2365 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2366 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2367 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2369 if (descr->style & LBS_OWNERDRAWFIXED)
2371 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2373 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2374 descr->item_height = lphc->fixedOwnerDrawHeight;
2376 else
2378 mis.CtlType = ODT_LISTBOX;
2379 mis.CtlID = wnd->wIDmenu;
2380 mis.itemID = -1;
2381 mis.itemWidth = 0;
2382 mis.itemData = 0;
2383 mis.itemHeight = descr->item_height;
2384 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2385 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2389 return TRUE;
2393 /***********************************************************************
2394 * LISTBOX_Destroy
2396 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2398 LISTBOX_ResetContent( wnd, descr );
2399 HeapDestroy( descr->heap );
2400 HeapFree( GetProcessHeap(), 0, descr );
2401 wnd->wExtra[0] = 0;
2402 return TRUE;
2406 /***********************************************************************
2407 * ListBoxWndProc
2409 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2410 WPARAM wParam, LPARAM lParam )
2412 LRESULT ret;
2413 LB_DESCR *descr;
2414 HWND hwnd = wnd->hwndSelf;
2416 if (!wnd) return 0;
2417 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2419 switch (msg)
2421 case WM_CREATE:
2423 if (!LISTBOX_Create( wnd, NULL ))
2424 return -1;
2425 TRACE("creating wnd=%04x descr=%p\n",
2426 hwnd, *(LB_DESCR **)wnd->wExtra );
2427 return 0;
2429 case WM_NCCREATE:
2432 * When a listbox is not in a combobox and the look
2433 * is win95, the WS_BORDER style is replaced with
2434 * the WS_EX_CLIENTEDGE style.
2436 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2437 (wnd->dwStyle & WS_BORDER) )
2439 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2440 wnd->dwStyle &= ~ WS_BORDER;
2445 /* Ignore all other messages before we get a WM_CREATE */
2446 return DefWindowProcA( hwnd, msg, wParam, lParam );
2449 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2450 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2451 switch(msg)
2453 case LB_RESETCONTENT16:
2454 case LB_RESETCONTENT:
2455 LISTBOX_ResetContent( wnd, descr );
2456 return 0;
2458 case LB_ADDSTRING16:
2459 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2460 /* fall through */
2461 case LB_ADDSTRING:
2462 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2463 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2465 case LB_INSERTSTRING16:
2466 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2467 wParam = (INT)(INT16)wParam;
2468 /* fall through */
2469 case LB_INSERTSTRING:
2470 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2472 case LB_ADDFILE16:
2473 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2474 /* fall through */
2475 case LB_ADDFILE:
2476 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2477 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2479 case LB_DELETESTRING16:
2480 case LB_DELETESTRING:
2481 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2482 return descr->nb_items;
2483 else
2484 return LB_ERR;
2486 case LB_GETITEMDATA16:
2487 case LB_GETITEMDATA:
2488 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2489 return LB_ERR;
2490 return descr->items[wParam].data;
2492 case LB_SETITEMDATA16:
2493 case LB_SETITEMDATA:
2494 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2495 return LB_ERR;
2496 descr->items[wParam].data = (DWORD)lParam;
2497 return LB_OKAY;
2499 case LB_GETCOUNT16:
2500 case LB_GETCOUNT:
2501 return descr->nb_items;
2503 case LB_GETTEXT16:
2504 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2505 /* fall through */
2506 case LB_GETTEXT:
2507 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2509 case LB_GETTEXTLEN16:
2510 /* fall through */
2511 case LB_GETTEXTLEN:
2512 if (wParam >= descr->nb_items)
2513 return LB_ERR;
2514 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2515 : sizeof(DWORD));
2517 case LB_GETCURSEL16:
2518 case LB_GETCURSEL:
2519 if (descr->nb_items==0)
2520 return LB_ERR;
2521 if (!IS_MULTISELECT(descr))
2522 return descr->selected_item;
2523 /* else */
2524 if (descr->selected_item!=-1)
2525 return descr->selected_item;
2526 /* else */
2527 return descr->focus_item;
2528 /* otherwise, if the user tries to move the selection with the */
2529 /* arrow keys, we will give the application something to choke on */
2530 case LB_GETTOPINDEX16:
2531 case LB_GETTOPINDEX:
2532 return descr->top_item;
2534 case LB_GETITEMHEIGHT16:
2535 case LB_GETITEMHEIGHT:
2536 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2538 case LB_SETITEMHEIGHT16:
2539 lParam = LOWORD(lParam);
2540 /* fall through */
2541 case LB_SETITEMHEIGHT:
2542 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2544 case LB_ITEMFROMPOINT:
2546 POINT pt;
2547 RECT rect;
2549 pt.x = LOWORD(lParam);
2550 pt.y = HIWORD(lParam);
2551 rect.left = 0;
2552 rect.top = 0;
2553 rect.right = descr->width;
2554 rect.bottom = descr->height;
2556 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2557 !PtInRect( &rect, pt ) );
2560 case LB_SETCARETINDEX16:
2561 case LB_SETCARETINDEX:
2562 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2563 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2564 return LB_ERR;
2565 else if (ISWIN31)
2566 return wParam;
2567 else
2568 return LB_OKAY;
2570 case LB_GETCARETINDEX16:
2571 case LB_GETCARETINDEX:
2572 return descr->focus_item;
2574 case LB_SETTOPINDEX16:
2575 case LB_SETTOPINDEX:
2576 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2578 case LB_SETCOLUMNWIDTH16:
2579 case LB_SETCOLUMNWIDTH:
2580 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2582 case LB_GETITEMRECT16:
2584 RECT rect;
2585 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2586 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2588 return ret;
2590 case LB_GETITEMRECT:
2591 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2593 case LB_FINDSTRING16:
2594 wParam = (INT)(INT16)wParam;
2595 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2596 /* fall through */
2597 case LB_FINDSTRING:
2598 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2600 case LB_FINDSTRINGEXACT16:
2601 wParam = (INT)(INT16)wParam;
2602 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2603 /* fall through */
2604 case LB_FINDSTRINGEXACT:
2605 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2607 case LB_SELECTSTRING16:
2608 wParam = (INT)(INT16)wParam;
2609 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2610 /* fall through */
2611 case LB_SELECTSTRING:
2613 INT index = LISTBOX_FindString( wnd, descr, wParam,
2614 (LPCSTR)lParam, FALSE );
2615 if (index == LB_ERR)
2616 return LB_ERR;
2617 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2618 return index;
2621 case LB_GETSEL16:
2622 wParam = (INT)(INT16)wParam;
2623 /* fall through */
2624 case LB_GETSEL:
2625 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2626 return LB_ERR;
2627 return descr->items[wParam].selected;
2629 case LB_SETSEL16:
2630 lParam = (INT)(INT16)lParam;
2631 /* fall through */
2632 case LB_SETSEL:
2633 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2635 case LB_SETCURSEL16:
2636 wParam = (INT)(INT16)wParam;
2637 /* fall through */
2638 case LB_SETCURSEL:
2639 if (IS_MULTISELECT(descr)) return LB_ERR;
2640 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2641 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2643 case LB_GETSELCOUNT16:
2644 case LB_GETSELCOUNT:
2645 return LISTBOX_GetSelCount( wnd, descr );
2647 case LB_GETSELITEMS16:
2648 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2649 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2651 case LB_GETSELITEMS:
2652 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2654 case LB_SELITEMRANGE16:
2655 case LB_SELITEMRANGE:
2656 if (LOWORD(lParam) <= HIWORD(lParam))
2657 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2658 HIWORD(lParam), wParam );
2659 else
2660 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2661 LOWORD(lParam), wParam );
2663 case LB_SELITEMRANGEEX16:
2664 case LB_SELITEMRANGEEX:
2665 if ((INT)lParam >= (INT)wParam)
2666 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2667 else
2668 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2670 case LB_GETHORIZONTALEXTENT16:
2671 case LB_GETHORIZONTALEXTENT:
2672 return descr->horz_extent;
2674 case LB_SETHORIZONTALEXTENT16:
2675 case LB_SETHORIZONTALEXTENT:
2676 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2678 case LB_GETANCHORINDEX16:
2679 case LB_GETANCHORINDEX:
2680 return descr->anchor_item;
2682 case LB_SETANCHORINDEX16:
2683 wParam = (INT)(INT16)wParam;
2684 /* fall through */
2685 case LB_SETANCHORINDEX:
2686 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2687 return LB_ERR;
2688 descr->anchor_item = (INT)wParam;
2689 return LB_OKAY;
2691 case LB_DIR16:
2692 return LISTBOX_Directory( wnd, descr, wParam,
2693 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2695 case LB_DIR:
2696 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2698 case LB_GETLOCALE:
2699 return descr->locale;
2701 case LB_SETLOCALE:
2702 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2703 return LB_OKAY;
2705 case LB_INITSTORAGE:
2706 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2708 case LB_SETCOUNT:
2709 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2711 case LB_SETTABSTOPS16:
2712 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2713 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2715 case LB_SETTABSTOPS:
2716 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2718 case LB_CARETON16:
2719 case LB_CARETON:
2720 if (descr->caret_on)
2721 return LB_OKAY;
2722 descr->caret_on = TRUE;
2723 if ((descr->focus_item != -1) && (descr->in_focus))
2724 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2725 return LB_OKAY;
2727 case LB_CARETOFF16:
2728 case LB_CARETOFF:
2729 if (!descr->caret_on)
2730 return LB_OKAY;
2731 descr->caret_on = FALSE;
2732 if ((descr->focus_item != -1) && (descr->in_focus))
2733 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2734 return LB_OKAY;
2736 case WM_DESTROY:
2737 return LISTBOX_Destroy( wnd, descr );
2739 case WM_ENABLE:
2740 InvalidateRect( hwnd, NULL, TRUE );
2741 return 0;
2743 case WM_SETREDRAW:
2744 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2745 return 0;
2747 case WM_GETDLGCODE:
2748 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2750 case WM_PAINT:
2752 PAINTSTRUCT ps;
2753 HDC hdc = ( wParam ) ? ((HDC)wParam)
2754 : BeginPaint( hwnd, &ps );
2755 ret = LISTBOX_Paint( wnd, descr, hdc );
2756 if( !wParam ) EndPaint( hwnd, &ps );
2758 return ret;
2759 case WM_SIZE:
2760 LISTBOX_UpdateSize( wnd, descr );
2761 return 0;
2762 case WM_GETFONT:
2763 return descr->font;
2764 case WM_SETFONT:
2765 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2766 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2767 return 0;
2768 case WM_SETFOCUS:
2769 descr->in_focus = TRUE;
2770 descr->caret_on = TRUE;
2771 if (descr->focus_item != -1)
2772 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2773 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2774 return 0;
2775 case WM_KILLFOCUS:
2776 descr->in_focus = FALSE;
2777 if ((descr->focus_item != -1) && descr->caret_on)
2778 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2779 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2780 return 0;
2781 case WM_HSCROLL:
2782 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2783 case WM_VSCROLL:
2784 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2785 case WM_MOUSEACTIVATE:
2786 return MA_NOACTIVATE;
2787 case WM_MOUSEWHEEL:
2788 if (wParam & (MK_SHIFT | MK_CONTROL))
2789 return DefWindowProcA( hwnd, msg, wParam, lParam );
2790 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2791 case WM_LBUTTONDOWN:
2792 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2793 (INT16)LOWORD(lParam),
2794 (INT16)HIWORD(lParam) );
2795 case WM_LBUTTONDBLCLK:
2796 if (descr->style & LBS_NOTIFY)
2797 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2798 return 0;
2799 case WM_MOUSEMOVE:
2800 if (GetCapture() == hwnd)
2801 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2802 (INT16)HIWORD(lParam) );
2803 return 0;
2804 case WM_LBUTTONUP:
2805 return LISTBOX_HandleLButtonUp( wnd, descr );
2806 case WM_KEYDOWN:
2807 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2808 case WM_CHAR:
2809 return LISTBOX_HandleChar( wnd, descr, wParam );
2810 case WM_SYSTIMER:
2811 return LISTBOX_HandleSystemTimer( wnd, descr );
2812 case WM_ERASEBKGND:
2813 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2815 RECT rect;
2816 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2817 wParam, (LPARAM)wnd->hwndSelf );
2818 GetClientRect(hwnd, &rect);
2819 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2821 return 1;
2822 case WM_DROPFILES:
2823 if( !descr->lphc )
2824 return SendMessageA( descr->owner, msg, wParam, lParam );
2825 break;
2827 case WM_DROPOBJECT:
2828 case WM_QUERYDROPOBJECT:
2829 case WM_DRAGSELECT:
2830 case WM_DRAGMOVE:
2831 if( !descr->lphc )
2833 LPDRAGINFO16 dragInfo = (LPDRAGINFO16)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2834 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2835 dragInfo->pt.y );
2836 return SendMessageA( descr->owner, msg, wParam, lParam );
2838 break;
2840 default:
2841 if ((msg >= WM_USER) && (msg < 0xc000))
2842 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2843 hwnd, msg, wParam, lParam );
2844 return DefWindowProcA( hwnd, msg, wParam, lParam );
2846 return 0;
2849 /***********************************************************************
2850 * ListBoxWndProc
2852 * This is just a wrapper for the real wndproc, it only does window locking
2853 * and unlocking.
2855 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2856 WPARAM wParam, LPARAM lParam )
2858 WND* wndPtr = WIN_FindWndPtr( hwnd );
2859 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2861 WIN_ReleaseWndPtr(wndPtr);
2862 return res;
2865 /***********************************************************************
2866 * COMBO_Directory
2868 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2870 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2872 if( wnd )
2874 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2875 if( descr )
2877 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2879 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2880 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2881 WIN_ReleaseWndPtr(wnd);
2882 return lRet;
2884 WIN_ReleaseWndPtr(wnd);
2886 return CB_ERR;
2889 /***********************************************************************
2890 * ComboLBWndProc_locked
2892 * The real combo listbox wndproc, but called with locked WND struct.
2894 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2895 WPARAM wParam, LPARAM lParam )
2897 LRESULT lRet = 0;
2898 HWND hwnd = wnd->hwndSelf;
2900 if (wnd)
2902 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2904 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2905 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2907 if( descr || msg == WM_CREATE )
2909 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2911 switch( msg )
2913 case WM_CREATE:
2914 #define lpcs ((LPCREATESTRUCTA)lParam)
2915 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2916 (UINT)lpcs->lpCreateParams);
2918 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2919 #undef lpcs
2920 return LISTBOX_Create( wnd, lphc );
2921 case WM_MOUSEMOVE:
2922 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2923 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2925 POINT mousePos;
2926 BOOL captured;
2927 RECT clientRect;
2929 mousePos.x = (INT16)LOWORD(lParam);
2930 mousePos.y = (INT16)HIWORD(lParam);
2933 * If we are in a dropdown combobox, we simulate that
2934 * the mouse is captured to show the tracking of the item.
2936 GetClientRect(hwnd, &clientRect);
2938 if (PtInRect( &clientRect, mousePos ))
2940 captured = descr->captured;
2941 descr->captured = TRUE;
2943 LISTBOX_HandleMouseMove( wnd, descr,
2944 mousePos.x, mousePos.y);
2946 descr->captured = captured;
2949 else
2951 LISTBOX_HandleMouseMove( wnd, descr,
2952 mousePos.x, mousePos.y);
2955 return 0;
2958 else
2961 * If we are in Win3.1 look, go with the default behavior.
2963 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2965 case WM_LBUTTONUP:
2966 if (TWEAK_WineLook > WIN31_LOOK)
2968 POINT mousePos;
2969 RECT clientRect;
2972 * If the mouse button "up" is not in the listbox,
2973 * we make sure there is no selection by re-selecting the
2974 * item that was selected when the listbox was made visible.
2976 mousePos.x = (INT16)LOWORD(lParam);
2977 mousePos.y = (INT16)HIWORD(lParam);
2979 GetClientRect(hwnd, &clientRect);
2982 * When the user clicks outside the combobox and the focus
2983 * is lost, the owning combobox will send a fake buttonup with
2984 * 0xFFFFFFF as the mouse location, we must also revert the
2985 * selection to the original selection.
2987 if ( (lParam == 0xFFFFFFFF) ||
2988 (!PtInRect( &clientRect, mousePos )) )
2990 LISTBOX_MoveCaret( wnd,
2991 descr,
2992 lphc->droppedIndex,
2993 FALSE );
2996 return LISTBOX_HandleLButtonUp( wnd, descr );
2997 case WM_LBUTTONDBLCLK:
2998 case WM_LBUTTONDOWN:
2999 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
3000 (INT16)LOWORD(lParam),
3001 (INT16)HIWORD(lParam) );
3002 case WM_MOUSEACTIVATE:
3003 return MA_NOACTIVATE;
3004 case WM_NCACTIVATE:
3005 return FALSE;
3006 case WM_KEYDOWN:
3007 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3009 /* for some reason(?) Windows makes it possible to
3010 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3012 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3013 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3014 && (wParam == VK_DOWN || wParam == VK_UP)) )
3016 COMBO_FlipListbox( lphc, FALSE, FALSE );
3017 return 0;
3020 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3022 case LB_SETCURSEL16:
3023 case LB_SETCURSEL:
3024 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
3025 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3026 return lRet;
3027 case WM_NCDESTROY:
3028 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3029 lphc->hWndLBox = 0;
3030 /* fall through */
3032 default:
3033 return ListBoxWndProc( hwnd, msg, wParam, lParam );
3036 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3038 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3040 return lRet;
3043 /***********************************************************************
3044 * ComboLBWndProc
3046 * NOTE: in Windows, winproc address of the ComboLBox is the same
3047 * as that of the Listbox.
3049 * This is just a wrapper for the real wndproc, it only does window locking
3050 * and unlocking.
3052 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3053 WPARAM wParam, LPARAM lParam )
3055 WND *wnd = WIN_FindWndPtr( hwnd );
3056 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3058 WIN_ReleaseWndPtr(wnd);
3059 return res;