Added support for non-deletable system brushes and pens created by
[wine/hacks.git] / controls / listbox.c
blob742f6642f081adb348d3f6bdee1ce37561f68673
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 )
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 ((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 ((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 );
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 (!lstrncmpiA( str, (item)->str+1, len )) return i; \
847 if (((item)->str[1] == '-') && !lstrncmpiA(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 (!lstrncmpiA( str, item->str, len )) return i;
855 CHECK_DRIVE(item);
857 for (i = 0, item = descr->items; i <= start; i++, item++)
859 if (!lstrncmpiA( 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;
938 INT focusItem;
940 if (descr->style & LBS_NOREDRAW) return 0;
942 SetRect( &rect, 0, 0, descr->width, descr->height );
943 if (descr->style & LBS_MULTICOLUMN)
944 rect.right = rect.left + descr->column_width;
945 else if (descr->horz_pos)
947 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
948 rect.right += descr->horz_pos;
951 if (descr->font) oldFont = SelectObject( hdc, descr->font );
952 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
953 hdc, (LPARAM)wnd->hwndSelf );
954 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
955 if (wnd->dwStyle & WS_DISABLED)
956 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
958 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
959 (descr->in_focus))
961 /* Special case for empty listbox: paint focus rect */
962 rect.bottom = rect.top + descr->item_height;
963 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
964 ODA_FOCUS );
965 rect.top = rect.bottom;
968 /* Paint all the item, regarding the selection
969 Focus state will be painted after */
970 focusItem = descr->focus_item;
971 descr->focus_item = -1;
973 for (i = descr->top_item; i < descr->nb_items; i++)
975 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
976 rect.bottom = rect.top + descr->item_height;
977 else
978 rect.bottom = rect.top + descr->items[i].height;
980 if (i == focusItem)
982 /* keep the focus rect, to paint the focus item after */
983 focusRect.left = rect.left;
984 focusRect.right = rect.right;
985 focusRect.top = rect.top;
986 focusRect.bottom = rect.bottom;
988 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
989 rect.top = rect.bottom;
991 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
993 if (!IS_OWNERDRAW(descr))
995 /* Clear the bottom of the column */
996 if (rect.top < descr->height)
998 rect.bottom = descr->height;
999 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1000 &rect, NULL, 0, NULL );
1004 /* Go to the next column */
1005 rect.left += descr->column_width;
1006 rect.right += descr->column_width;
1007 rect.top = 0;
1008 col_pos = descr->page_size - 1;
1010 else
1012 col_pos--;
1013 if (rect.top >= descr->height) break;
1017 /* Paint the focus item now */
1018 descr->focus_item = focusItem;
1019 if (focusRect.top != focusRect.bottom && descr->caret_on)
1020 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS );
1022 if (!IS_OWNERDRAW(descr))
1024 /* Clear the remainder of the client area */
1025 if (rect.top < descr->height)
1027 rect.bottom = descr->height;
1028 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1029 &rect, NULL, 0, NULL );
1031 if (rect.right < descr->width)
1033 rect.left = rect.right;
1034 rect.right = descr->width;
1035 rect.top = 0;
1036 rect.bottom = descr->height;
1037 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1038 &rect, NULL, 0, NULL );
1041 if (oldFont) SelectObject( hdc, oldFont );
1042 if (oldBrush) SelectObject( hdc, oldBrush );
1043 return 0;
1047 /***********************************************************************
1048 * LISTBOX_InvalidateItems
1050 * Invalidate all items from a given item. If the specified item is not
1051 * visible, nothing happens.
1053 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1055 RECT rect;
1057 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1059 if (descr->style & LBS_NOREDRAW)
1061 descr->style |= LBS_DISPLAYCHANGED;
1062 return;
1064 rect.bottom = descr->height;
1065 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1066 if (descr->style & LBS_MULTICOLUMN)
1068 /* Repaint the other columns */
1069 rect.left = rect.right;
1070 rect.right = descr->width;
1071 rect.top = 0;
1072 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1078 /***********************************************************************
1079 * LISTBOX_GetItemHeight
1081 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1083 if (descr->style & LBS_OWNERDRAWVARIABLE)
1085 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1086 return descr->items[index].height;
1088 else return descr->item_height;
1092 /***********************************************************************
1093 * LISTBOX_SetItemHeight
1095 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1096 UINT height )
1098 if (!height) height = 1;
1100 if (descr->style & LBS_OWNERDRAWVARIABLE)
1102 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1103 TRACE("[%04x]: item %d height = %d\n",
1104 wnd->hwndSelf, index, height );
1105 descr->items[index].height = height;
1106 LISTBOX_UpdateScroll( wnd, descr );
1107 LISTBOX_InvalidateItems( wnd, descr, index );
1109 else if (height != descr->item_height)
1111 TRACE("[%04x]: new height = %d\n",
1112 wnd->hwndSelf, height );
1113 descr->item_height = height;
1114 LISTBOX_UpdatePage( wnd, descr );
1115 LISTBOX_UpdateScroll( wnd, descr );
1116 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1118 return LB_OKAY;
1122 /***********************************************************************
1123 * LISTBOX_SetHorizontalPos
1125 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1127 INT diff;
1129 if (pos > descr->horz_extent - descr->width)
1130 pos = descr->horz_extent - descr->width;
1131 if (pos < 0) pos = 0;
1132 if (!(diff = descr->horz_pos - pos)) return;
1133 TRACE("[%04x]: new horz pos = %d\n",
1134 wnd->hwndSelf, pos );
1135 descr->horz_pos = pos;
1136 LISTBOX_UpdateScroll( wnd, descr );
1137 if (abs(diff) < descr->width)
1138 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1139 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1140 else
1141 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1145 /***********************************************************************
1146 * LISTBOX_SetHorizontalExtent
1148 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1149 UINT extent )
1151 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1152 return LB_OKAY;
1153 if (extent <= 0) extent = 1;
1154 if (extent == descr->horz_extent) return LB_OKAY;
1155 TRACE("[%04x]: new horz extent = %d\n",
1156 wnd->hwndSelf, extent );
1157 descr->horz_extent = extent;
1158 if (descr->horz_pos > extent - descr->width)
1159 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1160 else
1161 LISTBOX_UpdateScroll( wnd, descr );
1162 return LB_OKAY;
1166 /***********************************************************************
1167 * LISTBOX_SetColumnWidth
1169 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1171 if (width == descr->column_width) return LB_OKAY;
1172 TRACE("[%04x]: new column width = %d\n",
1173 wnd->hwndSelf, width );
1174 descr->column_width = width;
1175 LISTBOX_UpdatePage( wnd, descr );
1176 return LB_OKAY;
1180 /***********************************************************************
1181 * LISTBOX_SetFont
1183 * Returns the item height.
1185 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1187 HDC hdc;
1188 HFONT oldFont = 0;
1189 TEXTMETRICA tm;
1191 descr->font = font;
1193 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1195 ERR("unable to get DC.\n" );
1196 return 16;
1198 if (font) oldFont = SelectObject( hdc, font );
1199 GetTextMetricsA( hdc, &tm );
1200 if (oldFont) SelectObject( hdc, oldFont );
1201 ReleaseDC( wnd->hwndSelf, hdc );
1202 if (!IS_OWNERDRAW(descr))
1203 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1204 return tm.tmHeight ;
1208 /***********************************************************************
1209 * LISTBOX_MakeItemVisible
1211 * Make sure that a given item is partially or fully visible.
1213 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1214 BOOL fully )
1216 INT top;
1218 if (index <= descr->top_item) top = index;
1219 else if (descr->style & LBS_MULTICOLUMN)
1221 INT cols = descr->width;
1222 if (!fully) cols += descr->column_width - 1;
1223 if (cols >= descr->column_width) cols /= descr->column_width;
1224 else cols = 1;
1225 if (index < descr->top_item + (descr->page_size * cols)) return;
1226 top = index - descr->page_size * (cols - 1);
1228 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1230 INT height = fully ? descr->items[index].height : 1;
1231 for (top = index; top > descr->top_item; top--)
1232 if ((height += descr->items[top-1].height) > descr->height) break;
1234 else
1236 if (index < descr->top_item + descr->page_size) return;
1237 if (!fully && (index == descr->top_item + descr->page_size) &&
1238 (descr->height > (descr->page_size * descr->item_height))) return;
1239 top = index - descr->page_size + 1;
1241 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1244 /***********************************************************************
1245 * LISTBOX_SetCaretIndex
1247 * NOTES
1248 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1251 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1252 BOOL fully_visible )
1254 INT oldfocus = descr->focus_item;
1256 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1257 if (index == oldfocus) return LB_OKAY;
1258 descr->focus_item = index;
1259 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1260 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1262 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1263 if (descr->caret_on && (descr->in_focus))
1264 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1266 return LB_OKAY;
1270 /***********************************************************************
1271 * LISTBOX_SelectItemRange
1273 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1275 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1276 INT last, BOOL on )
1278 INT i;
1280 /* A few sanity checks */
1282 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1283 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1284 if (last == -1) last = descr->nb_items - 1;
1285 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1286 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1287 /* selected_item reflects last selected/unselected item on multiple sel */
1288 descr->selected_item = last;
1290 if (on) /* Turn selection on */
1292 for (i = first; i <= last; i++)
1294 if (descr->items[i].selected) continue;
1295 descr->items[i].selected = TRUE;
1296 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1298 LISTBOX_SetCaretIndex( wnd, descr, last, TRUE );
1300 else /* Turn selection off */
1302 for (i = first; i <= last; i++)
1304 if (!descr->items[i].selected) continue;
1305 descr->items[i].selected = FALSE;
1306 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1309 return LB_OKAY;
1312 /***********************************************************************
1313 * LISTBOX_SetSelection
1315 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1316 BOOL on, BOOL send_notify )
1318 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1320 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1321 if (descr->style & LBS_MULTIPLESEL)
1323 if (index == -1) /* Select all items */
1324 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1325 else /* Only one item */
1326 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1328 else
1330 INT oldsel = descr->selected_item;
1331 if (index == oldsel) return LB_OKAY;
1332 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1333 if (index != -1) descr->items[index].selected = TRUE;
1334 descr->selected_item = index;
1335 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1336 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1337 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1338 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1339 else
1340 if( descr->lphc ) /* set selection change flag for parent combo */
1341 descr->lphc->wState |= CBF_SELCHANGE;
1343 return LB_OKAY;
1347 /***********************************************************************
1348 * LISTBOX_MoveCaret
1350 * Change the caret position and extend the selection to the new caret.
1352 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1353 BOOL fully_visible )
1355 INT oldfocus = descr->focus_item;
1357 if ((index < 0) || (index >= descr->nb_items))
1358 return;
1360 /* Important, repaint needs to be done in this order if
1361 you want to mimic Windows behavior:
1362 1. Remove the focus and paint the item
1363 2. Remove the selection and paint the item(s)
1364 3. Set the selection and repaint the item(s)
1365 4. Set the focus to 'index' and repaint the item */
1367 /* 1. remove the focus and repaint the item */
1368 descr->focus_item = -1;
1369 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1370 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1372 /* 2. then turn off the previous selection */
1373 /* 3. repaint the new selected item */
1374 if (descr->style & LBS_EXTENDEDSEL)
1376 if (descr->anchor_item != -1)
1378 INT first = min( index, descr->anchor_item );
1379 INT last = max( index, descr->anchor_item );
1380 if (first > 0)
1381 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1382 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1383 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1386 else if (!(descr->style & LBS_MULTIPLESEL))
1388 /* Set selection to new caret item */
1389 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1392 /* 4. repaint the new item with the focus */
1393 descr->focus_item = index;
1394 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1395 if (descr->caret_on && (descr->in_focus))
1396 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1400 /***********************************************************************
1401 * LISTBOX_InsertItem
1403 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1404 LPSTR str, DWORD data )
1406 LB_ITEMDATA *item;
1407 INT max_items;
1408 INT oldfocus = descr->focus_item;
1410 if (index == -1) index = descr->nb_items;
1411 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1412 if (!descr->items) max_items = 0;
1413 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1414 if (descr->nb_items == max_items)
1416 /* We need to grow the array */
1417 max_items += LB_ARRAY_GRANULARITY;
1418 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1419 max_items * sizeof(LB_ITEMDATA) )))
1421 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1422 return LB_ERRSPACE;
1424 descr->items = item;
1427 /* Insert the item structure */
1429 item = &descr->items[index];
1430 if (index < descr->nb_items)
1431 RtlMoveMemory( item + 1, item,
1432 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1433 item->str = str;
1434 item->data = data;
1435 item->height = 0;
1436 item->selected = FALSE;
1437 descr->nb_items++;
1439 /* Get item height */
1441 if (descr->style & LBS_OWNERDRAWVARIABLE)
1443 MEASUREITEMSTRUCT mis;
1445 mis.CtlType = ODT_LISTBOX;
1446 mis.CtlID = wnd->wIDmenu;
1447 mis.itemID = index;
1448 mis.itemData = descr->items[index].data;
1449 mis.itemHeight = descr->item_height;
1450 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1451 item->height = mis.itemHeight ? mis.itemHeight : 1;
1452 TRACE("[%04x]: measure item %d (%s) = %d\n",
1453 wnd->hwndSelf, index, str ? str : "", item->height );
1456 /* Repaint the items */
1458 LISTBOX_UpdateScroll( wnd, descr );
1459 LISTBOX_InvalidateItems( wnd, descr, index );
1461 /* Move selection and focused item */
1462 /* If listbox was empty, set focus to the first item */
1463 if (descr->nb_items == 1)
1464 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1465 /* single select don't change selection index in win31 */
1466 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1468 descr->selected_item++;
1469 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1471 else
1473 if (index <= descr->selected_item)
1475 descr->selected_item++;
1476 descr->focus_item = oldfocus; /* focus not changed */
1479 return LB_OKAY;
1483 /***********************************************************************
1484 * LISTBOX_InsertString
1486 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1487 LPCSTR str )
1489 LPSTR new_str = NULL;
1490 DWORD data = 0;
1491 LRESULT ret;
1493 if (HAS_STRINGS(descr))
1495 if (!str) str="";
1496 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1498 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1499 return LB_ERRSPACE;
1502 else data = (DWORD)str;
1504 if (index == -1) index = descr->nb_items;
1505 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1507 if (new_str) HeapFree( descr->heap, 0, new_str );
1508 return ret;
1511 TRACE("[%04x]: added item %d '%s'\n",
1512 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1513 return index;
1517 /***********************************************************************
1518 * LISTBOX_DeleteItem
1520 * Delete the content of an item. 'index' must be a valid index.
1522 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1524 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1525 * while Win95 sends it for all items with user data.
1526 * It's probably better to send it too often than not
1527 * often enough, so this is what we do here.
1529 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1531 DELETEITEMSTRUCT dis;
1533 dis.CtlType = ODT_LISTBOX;
1534 dis.CtlID = wnd->wIDmenu;
1535 dis.itemID = index;
1536 dis.hwndItem = wnd->hwndSelf;
1537 dis.itemData = descr->items[index].data;
1538 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1540 if (HAS_STRINGS(descr) && descr->items[index].str)
1541 HeapFree( descr->heap, 0, descr->items[index].str );
1545 /***********************************************************************
1546 * LISTBOX_RemoveItem
1548 * Remove an item from the listbox and delete its content.
1550 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1552 LB_ITEMDATA *item;
1553 INT max_items;
1555 if (index == -1) index = descr->nb_items - 1;
1556 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1558 /* We need to invalidate the original rect instead of the updated one. */
1559 LISTBOX_InvalidateItems( wnd, descr, index );
1561 LISTBOX_DeleteItem( wnd, descr, index );
1563 /* Remove the item */
1565 item = &descr->items[index];
1566 if (index < descr->nb_items-1)
1567 RtlMoveMemory( item, item + 1,
1568 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1569 descr->nb_items--;
1570 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1572 /* Shrink the item array if possible */
1574 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1575 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1577 max_items -= LB_ARRAY_GRANULARITY;
1578 item = HeapReAlloc( descr->heap, 0, descr->items,
1579 max_items * sizeof(LB_ITEMDATA) );
1580 if (item) descr->items = item;
1582 /* Repaint the items */
1584 LISTBOX_UpdateScroll( wnd, descr );
1585 /* if we removed the scrollbar, reset the top of the list
1586 (correct for owner-drawn ???) */
1587 if (descr->nb_items == descr->page_size)
1588 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1590 /* Move selection and focused item */
1591 if (!IS_MULTISELECT(descr))
1593 if (index == descr->selected_item)
1594 descr->selected_item = -1;
1595 else if (index < descr->selected_item)
1597 descr->selected_item--;
1598 if (ISWIN31) /* win 31 do not change the selected item number */
1599 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1603 if (descr->focus_item >= descr->nb_items)
1605 descr->focus_item = descr->nb_items - 1;
1606 if (descr->focus_item < 0) descr->focus_item = 0;
1608 return LB_OKAY;
1612 /***********************************************************************
1613 * LISTBOX_ResetContent
1615 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1617 INT i;
1619 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1620 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1621 descr->nb_items = 0;
1622 descr->top_item = 0;
1623 descr->selected_item = -1;
1624 descr->focus_item = 0;
1625 descr->anchor_item = -1;
1626 descr->items = NULL;
1627 LISTBOX_UpdateScroll( wnd, descr );
1628 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1632 /***********************************************************************
1633 * LISTBOX_SetCount
1635 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1637 LRESULT ret;
1639 if (HAS_STRINGS(descr)) return LB_ERR;
1640 /* FIXME: this is far from optimal... */
1641 if (count > descr->nb_items)
1643 while (count > descr->nb_items)
1644 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1645 return ret;
1647 else if (count < descr->nb_items)
1649 while (count < descr->nb_items)
1650 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1651 return ret;
1653 return LB_OKAY;
1657 /***********************************************************************
1658 * LISTBOX_Directory
1660 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1661 LPCSTR filespec, BOOL long_names )
1663 HANDLE handle;
1664 LRESULT ret = LB_OKAY;
1665 WIN32_FIND_DATAA entry;
1666 int pos;
1668 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1670 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1672 else
1676 char buffer[270];
1677 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1679 if (!(attrib & DDL_DIRECTORY) ||
1680 !strcmp( entry.cAlternateFileName, "." )) continue;
1681 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1682 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1684 else /* not a directory */
1686 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1687 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1689 if ((attrib & DDL_EXCLUSIVE) &&
1690 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1691 continue;
1692 #undef ATTRIBS
1693 if (long_names) strcpy( buffer, entry.cFileName );
1694 else strcpy( buffer, entry.cAlternateFileName );
1696 if (!long_names) CharLowerA( buffer );
1697 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1698 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1699 break;
1700 } while (FindNextFileA( handle, &entry ));
1701 FindClose( handle );
1704 if ((ret >= 0) && (attrib & DDL_DRIVES))
1706 char buffer[] = "[-a-]";
1707 int drive;
1708 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1710 if (!DRIVE_IsValid(drive)) continue;
1711 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1712 break;
1715 return ret;
1719 /***********************************************************************
1720 * LISTBOX_HandleVScroll
1722 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1723 WPARAM wParam, LPARAM lParam )
1725 SCROLLINFO info;
1727 if (descr->style & LBS_MULTICOLUMN) return 0;
1728 switch(LOWORD(wParam))
1730 case SB_LINEUP:
1731 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1732 break;
1733 case SB_LINEDOWN:
1734 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1735 break;
1736 case SB_PAGEUP:
1737 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1738 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1739 break;
1740 case SB_PAGEDOWN:
1741 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1742 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1743 break;
1744 case SB_THUMBPOSITION:
1745 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1746 break;
1747 case SB_THUMBTRACK:
1748 info.cbSize = sizeof(info);
1749 info.fMask = SIF_TRACKPOS;
1750 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1751 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1752 break;
1753 case SB_TOP:
1754 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1755 break;
1756 case SB_BOTTOM:
1757 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1758 break;
1760 return 0;
1764 /***********************************************************************
1765 * LISTBOX_HandleHScroll
1767 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1768 WPARAM wParam, LPARAM lParam )
1770 SCROLLINFO info;
1771 INT page;
1773 if (descr->style & LBS_MULTICOLUMN)
1775 switch(LOWORD(wParam))
1777 case SB_LINELEFT:
1778 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1779 TRUE );
1780 break;
1781 case SB_LINERIGHT:
1782 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1783 TRUE );
1784 break;
1785 case SB_PAGELEFT:
1786 page = descr->width / descr->column_width;
1787 if (page < 1) page = 1;
1788 LISTBOX_SetTopItem( wnd, descr,
1789 descr->top_item - page * descr->page_size, TRUE );
1790 break;
1791 case SB_PAGERIGHT:
1792 page = descr->width / descr->column_width;
1793 if (page < 1) page = 1;
1794 LISTBOX_SetTopItem( wnd, descr,
1795 descr->top_item + page * descr->page_size, TRUE );
1796 break;
1797 case SB_THUMBPOSITION:
1798 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1799 TRUE );
1800 break;
1801 case SB_THUMBTRACK:
1802 info.cbSize = sizeof(info);
1803 info.fMask = SIF_TRACKPOS;
1804 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1805 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1806 TRUE );
1807 break;
1808 case SB_LEFT:
1809 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1810 break;
1811 case SB_RIGHT:
1812 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1813 break;
1816 else if (descr->horz_extent)
1818 switch(LOWORD(wParam))
1820 case SB_LINELEFT:
1821 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1822 break;
1823 case SB_LINERIGHT:
1824 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1825 break;
1826 case SB_PAGELEFT:
1827 LISTBOX_SetHorizontalPos( wnd, descr,
1828 descr->horz_pos - descr->width );
1829 break;
1830 case SB_PAGERIGHT:
1831 LISTBOX_SetHorizontalPos( wnd, descr,
1832 descr->horz_pos + descr->width );
1833 break;
1834 case SB_THUMBPOSITION:
1835 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1836 break;
1837 case SB_THUMBTRACK:
1838 info.cbSize = sizeof(info);
1839 info.fMask = SIF_TRACKPOS;
1840 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1841 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1842 break;
1843 case SB_LEFT:
1844 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1845 break;
1846 case SB_RIGHT:
1847 LISTBOX_SetHorizontalPos( wnd, descr,
1848 descr->horz_extent - descr->width );
1849 break;
1852 return 0;
1855 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1857 short gcWheelDelta = 0;
1858 UINT pulScrollLines = 3;
1860 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1862 gcWheelDelta -= (short) HIWORD(wParam);
1864 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1866 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1867 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1868 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1870 return 0;
1873 /***********************************************************************
1874 * LISTBOX_HandleLButtonDown
1876 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1877 WPARAM wParam, INT x, INT y )
1879 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1880 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1881 wnd->hwndSelf, x, y, index );
1882 if (!descr->caret_on && (descr->in_focus)) return 0;
1884 if (!descr->in_focus)
1886 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1887 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1888 : descr->lphc->self->hwndSelf );
1891 if (index != -1)
1893 if (descr->style & LBS_EXTENDEDSEL)
1895 /* we should perhaps make sure that all items are deselected
1896 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1897 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1898 LISTBOX_SetSelection( wnd, descr, -1, FALSE, FALSE);
1901 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1902 if (wParam & MK_CONTROL)
1904 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1905 LISTBOX_SetSelection( wnd, descr, index,
1906 !descr->items[index].selected,
1907 (descr->style & LBS_NOTIFY) != 0);
1909 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1911 else
1913 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1914 LISTBOX_SetSelection( wnd, descr, index,
1915 (!(descr->style & LBS_MULTIPLESEL) ||
1916 !descr->items[index].selected),
1917 (descr->style & LBS_NOTIFY) != 0 );
1921 descr->captured = TRUE;
1922 SetCapture( wnd->hwndSelf );
1923 if (index != -1 && !descr->lphc)
1925 if (descr->style & LBS_NOTIFY )
1926 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1927 MAKELPARAM( x, y ) );
1928 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1930 POINT pt;
1932 pt.x = x;
1933 pt.y = y;
1935 if (DragDetect( wnd->hwndSelf, pt ))
1936 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1939 return 0;
1943 /*************************************************************************
1944 * LISTBOX_HandleLButtonDownCombo [Internal]
1946 * Process LButtonDown message for the ComboListBox
1948 * PARAMS
1949 * pWnd [I] The windows internal structure
1950 * pDescr [I] The ListBox internal structure
1951 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1952 * x [I] X Mouse Coordinate
1953 * y [I] Y Mouse Coordinate
1955 * RETURNS
1956 * 0 since we are processing the WM_LBUTTONDOWN Message
1958 * NOTES
1959 * This function is only to be used when a ListBox is a ComboListBox
1962 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1963 UINT msg, WPARAM wParam, INT x, INT y)
1965 RECT clientRect, screenRect;
1966 POINT mousePos;
1968 mousePos.x = x;
1969 mousePos.y = y;
1971 GetClientRect(pWnd->hwndSelf, &clientRect);
1973 if(PtInRect(&clientRect, mousePos))
1975 /* MousePos is in client, resume normal processing */
1976 if (msg == WM_LBUTTONDOWN)
1978 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1979 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1981 else if (pDescr->style & LBS_NOTIFY)
1982 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1983 return 0;
1985 else
1987 POINT screenMousePos;
1988 HWND hWndOldCapture;
1990 /* Check the Non-Client Area */
1991 screenMousePos = mousePos;
1992 hWndOldCapture = GetCapture();
1993 ReleaseCapture();
1994 GetWindowRect(pWnd->hwndSelf, &screenRect);
1995 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1997 if(!PtInRect(&screenRect, screenMousePos))
1999 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2000 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2001 return 0;
2003 else
2005 /* Check to see the NC is a scrollbar */
2006 INT nHitTestType=0;
2007 /* Check Vertical scroll bar */
2008 if (pWnd->dwStyle & WS_VSCROLL)
2010 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2011 if (PtInRect( &clientRect, mousePos ))
2013 nHitTestType = HTVSCROLL;
2016 /* Check horizontal scroll bar */
2017 if (pWnd->dwStyle & WS_HSCROLL)
2019 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2020 if (PtInRect( &clientRect, mousePos ))
2022 nHitTestType = HTHSCROLL;
2025 /* Windows sends this message when a scrollbar is clicked
2028 if(nHitTestType != 0)
2030 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2031 MAKELONG(screenMousePos.x, screenMousePos.y));
2033 /* Resume the Capture after scrolling is complete
2035 if(hWndOldCapture != 0)
2037 SetCapture(hWndOldCapture);
2041 return 0;
2044 /***********************************************************************
2045 * LISTBOX_HandleLButtonUp
2047 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2049 if (LISTBOX_Timer != LB_TIMER_NONE)
2050 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2051 LISTBOX_Timer = LB_TIMER_NONE;
2052 if (descr->captured)
2054 descr->captured = FALSE;
2055 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2056 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2057 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2059 return 0;
2063 /***********************************************************************
2064 * LISTBOX_HandleTimer
2066 * Handle scrolling upon a timer event.
2067 * Return TRUE if scrolling should continue.
2069 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2070 INT index, TIMER_DIRECTION dir )
2072 switch(dir)
2074 case LB_TIMER_UP:
2075 if (descr->top_item) index = descr->top_item - 1;
2076 else index = 0;
2077 break;
2078 case LB_TIMER_LEFT:
2079 if (descr->top_item) index -= descr->page_size;
2080 break;
2081 case LB_TIMER_DOWN:
2082 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2083 if (index == descr->focus_item) index++;
2084 if (index >= descr->nb_items) index = descr->nb_items - 1;
2085 break;
2086 case LB_TIMER_RIGHT:
2087 if (index + descr->page_size < descr->nb_items)
2088 index += descr->page_size;
2089 break;
2090 case LB_TIMER_NONE:
2091 break;
2093 if (index == descr->focus_item) return FALSE;
2094 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2095 return TRUE;
2099 /***********************************************************************
2100 * LISTBOX_HandleSystemTimer
2102 * WM_SYSTIMER handler.
2104 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2106 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2108 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2109 LISTBOX_Timer = LB_TIMER_NONE;
2111 return 0;
2115 /***********************************************************************
2116 * LISTBOX_HandleMouseMove
2118 * WM_MOUSEMOVE handler.
2120 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2121 INT x, INT y )
2123 INT index;
2124 TIMER_DIRECTION dir = LB_TIMER_NONE;
2126 if (!descr->captured) return;
2128 if (descr->style & LBS_MULTICOLUMN)
2130 if (y < 0) y = 0;
2131 else if (y >= descr->item_height * descr->page_size)
2132 y = descr->item_height * descr->page_size - 1;
2134 if (x < 0)
2136 dir = LB_TIMER_LEFT;
2137 x = 0;
2139 else if (x >= descr->width)
2141 dir = LB_TIMER_RIGHT;
2142 x = descr->width - 1;
2145 else
2147 if (y < 0) dir = LB_TIMER_UP; /* above */
2148 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2151 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2152 if (index == -1) index = descr->focus_item;
2153 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2155 /* Start/stop the system timer */
2157 if (dir != LB_TIMER_NONE)
2158 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2159 else if (LISTBOX_Timer != LB_TIMER_NONE)
2160 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2161 LISTBOX_Timer = dir;
2165 /***********************************************************************
2166 * LISTBOX_HandleKeyDown
2168 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2170 INT caret = -1;
2171 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2172 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2173 bForceSelection = FALSE; /* only for single select list */
2175 if (descr->style & LBS_WANTKEYBOARDINPUT)
2177 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2178 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2179 wnd->hwndSelf );
2180 if (caret == -2) return 0;
2182 if (caret == -1) switch(wParam)
2184 case VK_LEFT:
2185 if (descr->style & LBS_MULTICOLUMN)
2187 bForceSelection = FALSE;
2188 if (descr->focus_item >= descr->page_size)
2189 caret = descr->focus_item - descr->page_size;
2190 break;
2192 /* fall through */
2193 case VK_UP:
2194 caret = descr->focus_item - 1;
2195 if (caret < 0) caret = 0;
2196 break;
2197 case VK_RIGHT:
2198 if (descr->style & LBS_MULTICOLUMN)
2200 bForceSelection = FALSE;
2201 if (descr->focus_item + descr->page_size < descr->nb_items)
2202 caret = descr->focus_item + descr->page_size;
2203 break;
2205 /* fall through */
2206 case VK_DOWN:
2207 caret = descr->focus_item + 1;
2208 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2209 break;
2211 case VK_PRIOR:
2212 if (descr->style & LBS_MULTICOLUMN)
2214 INT page = descr->width / descr->column_width;
2215 if (page < 1) page = 1;
2216 caret = descr->focus_item - (page * descr->page_size) + 1;
2218 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2219 if (caret < 0) caret = 0;
2220 break;
2221 case VK_NEXT:
2222 if (descr->style & LBS_MULTICOLUMN)
2224 INT page = descr->width / descr->column_width;
2225 if (page < 1) page = 1;
2226 caret = descr->focus_item + (page * descr->page_size) - 1;
2228 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2229 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2230 break;
2231 case VK_HOME:
2232 caret = 0;
2233 break;
2234 case VK_END:
2235 caret = descr->nb_items - 1;
2236 break;
2237 case VK_SPACE:
2238 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2239 else if (descr->style & LBS_MULTIPLESEL)
2241 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2242 !descr->items[descr->focus_item].selected,
2243 (descr->style & LBS_NOTIFY) != 0 );
2245 break;
2246 default:
2247 bForceSelection = FALSE;
2249 if (bForceSelection) /* focused item is used instead of key */
2250 caret = descr->focus_item;
2251 if (caret >= 0)
2253 if ((descr->style & LBS_EXTENDEDSEL) &&
2254 !(GetKeyState( VK_SHIFT ) & 0x8000))
2255 descr->anchor_item = caret;
2256 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2257 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2258 if (descr->style & LBS_NOTIFY)
2260 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2262 /* make sure that combo parent doesn't hide us */
2263 descr->lphc->wState |= CBF_NOROLLUP;
2265 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2268 return 0;
2272 /***********************************************************************
2273 * LISTBOX_HandleChar
2275 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2276 WPARAM wParam )
2278 INT caret = -1;
2279 char str[2];
2281 str[0] = wParam & 0xff;
2282 str[1] = '\0';
2284 if (descr->style & LBS_WANTKEYBOARDINPUT)
2286 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2287 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2288 wnd->hwndSelf );
2289 if (caret == -2) return 0;
2291 if (caret == -1)
2292 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2293 if (caret != -1)
2295 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2296 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2297 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2298 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2299 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2301 return 0;
2305 /***********************************************************************
2306 * LISTBOX_Create
2308 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2310 LB_DESCR *descr;
2311 MEASUREITEMSTRUCT mis;
2312 RECT rect;
2314 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2315 return FALSE;
2316 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2318 HeapFree( GetProcessHeap(), 0, descr );
2319 return FALSE;
2321 GetClientRect( wnd->hwndSelf, &rect );
2322 descr->owner = GetParent( wnd->hwndSelf );
2323 descr->style = wnd->dwStyle;
2324 descr->width = rect.right - rect.left;
2325 descr->height = rect.bottom - rect.top;
2326 descr->items = NULL;
2327 descr->nb_items = 0;
2328 descr->top_item = 0;
2329 descr->selected_item = -1;
2330 descr->focus_item = 0;
2331 descr->anchor_item = -1;
2332 descr->item_height = 1;
2333 descr->page_size = 1;
2334 descr->column_width = 150;
2335 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2336 descr->horz_pos = 0;
2337 descr->nb_tabs = 0;
2338 descr->tabs = NULL;
2339 descr->caret_on = lphc ? FALSE : TRUE;
2340 descr->in_focus = FALSE;
2341 descr->captured = FALSE;
2342 descr->font = 0;
2343 descr->locale = 0; /* FIXME */
2344 descr->lphc = lphc;
2346 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2347 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2349 /* Win95 document "List Box Differences" from MSDN:
2350 If a list box in a version 3.x application has either the
2351 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2352 horizontal and vertical scroll bars.
2354 descr->style |= WS_VSCROLL | WS_HSCROLL;
2357 if( lphc )
2359 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2360 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2361 descr->owner = lphc->self->hwndSelf;
2364 *(LB_DESCR **)wnd->wExtra = descr;
2366 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2368 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2369 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2370 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2371 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2373 if (descr->style & LBS_OWNERDRAWFIXED)
2375 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2377 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2378 descr->item_height = lphc->fixedOwnerDrawHeight;
2380 else
2382 mis.CtlType = ODT_LISTBOX;
2383 mis.CtlID = wnd->wIDmenu;
2384 mis.itemID = -1;
2385 mis.itemWidth = 0;
2386 mis.itemData = 0;
2387 mis.itemHeight = descr->item_height;
2388 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2389 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2393 return TRUE;
2397 /***********************************************************************
2398 * LISTBOX_Destroy
2400 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2402 LISTBOX_ResetContent( wnd, descr );
2403 HeapDestroy( descr->heap );
2404 HeapFree( GetProcessHeap(), 0, descr );
2405 wnd->wExtra[0] = 0;
2406 return TRUE;
2410 /***********************************************************************
2411 * ListBoxWndProc
2413 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2414 WPARAM wParam, LPARAM lParam )
2416 LRESULT ret;
2417 LB_DESCR *descr;
2418 HWND hwnd = wnd->hwndSelf;
2420 if (!wnd) return 0;
2421 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2423 switch (msg)
2425 case WM_CREATE:
2427 if (!LISTBOX_Create( wnd, NULL ))
2428 return -1;
2429 TRACE("creating wnd=%04x descr=%p\n",
2430 hwnd, *(LB_DESCR **)wnd->wExtra );
2431 return 0;
2433 case WM_NCCREATE:
2436 * When a listbox is not in a combobox and the look
2437 * is win95, the WS_BORDER style is replaced with
2438 * the WS_EX_CLIENTEDGE style.
2440 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2441 (wnd->dwStyle & WS_BORDER) )
2443 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2444 wnd->dwStyle &= ~ WS_BORDER;
2449 /* Ignore all other messages before we get a WM_CREATE */
2450 return DefWindowProcA( hwnd, msg, wParam, lParam );
2453 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2454 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2455 switch(msg)
2457 case LB_RESETCONTENT16:
2458 case LB_RESETCONTENT:
2459 LISTBOX_ResetContent( wnd, descr );
2460 return 0;
2462 case LB_ADDSTRING16:
2463 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2464 /* fall through */
2465 case LB_ADDSTRING:
2466 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2467 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2469 case LB_INSERTSTRING16:
2470 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2471 wParam = (INT)(INT16)wParam;
2472 /* fall through */
2473 case LB_INSERTSTRING:
2474 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2476 case LB_ADDFILE16:
2477 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2478 /* fall through */
2479 case LB_ADDFILE:
2480 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2481 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2483 case LB_DELETESTRING16:
2484 case LB_DELETESTRING:
2485 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2486 return descr->nb_items;
2487 else
2488 return LB_ERR;
2490 case LB_GETITEMDATA16:
2491 case LB_GETITEMDATA:
2492 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2493 return LB_ERR;
2494 return descr->items[wParam].data;
2496 case LB_SETITEMDATA16:
2497 case LB_SETITEMDATA:
2498 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2499 return LB_ERR;
2500 descr->items[wParam].data = (DWORD)lParam;
2501 return LB_OKAY;
2503 case LB_GETCOUNT16:
2504 case LB_GETCOUNT:
2505 return descr->nb_items;
2507 case LB_GETTEXT16:
2508 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2509 /* fall through */
2510 case LB_GETTEXT:
2511 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2513 case LB_GETTEXTLEN16:
2514 /* fall through */
2515 case LB_GETTEXTLEN:
2516 if (wParam >= descr->nb_items)
2517 return LB_ERR;
2518 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2519 : sizeof(DWORD));
2521 case LB_GETCURSEL16:
2522 case LB_GETCURSEL:
2523 if (descr->nb_items==0)
2524 return LB_ERR;
2525 if (!IS_MULTISELECT(descr))
2526 return descr->selected_item;
2527 /* else */
2528 if (descr->selected_item!=-1)
2529 return descr->selected_item;
2530 /* else */
2531 return descr->focus_item;
2532 /* otherwise, if the user tries to move the selection with the */
2533 /* arrow keys, we will give the application something to choke on */
2534 case LB_GETTOPINDEX16:
2535 case LB_GETTOPINDEX:
2536 return descr->top_item;
2538 case LB_GETITEMHEIGHT16:
2539 case LB_GETITEMHEIGHT:
2540 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2542 case LB_SETITEMHEIGHT16:
2543 lParam = LOWORD(lParam);
2544 /* fall through */
2545 case LB_SETITEMHEIGHT:
2546 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2548 case LB_ITEMFROMPOINT:
2550 POINT pt;
2551 RECT rect;
2553 pt.x = LOWORD(lParam);
2554 pt.y = HIWORD(lParam);
2555 rect.left = 0;
2556 rect.top = 0;
2557 rect.right = descr->width;
2558 rect.bottom = descr->height;
2560 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2561 !PtInRect( &rect, pt ) );
2564 case LB_SETCARETINDEX16:
2565 case LB_SETCARETINDEX:
2566 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2567 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2568 return LB_ERR;
2569 else if (ISWIN31)
2570 return wParam;
2571 else
2572 return LB_OKAY;
2574 case LB_GETCARETINDEX16:
2575 case LB_GETCARETINDEX:
2576 return descr->focus_item;
2578 case LB_SETTOPINDEX16:
2579 case LB_SETTOPINDEX:
2580 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2582 case LB_SETCOLUMNWIDTH16:
2583 case LB_SETCOLUMNWIDTH:
2584 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2586 case LB_GETITEMRECT16:
2588 RECT rect;
2589 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2590 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2592 return ret;
2594 case LB_GETITEMRECT:
2595 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2597 case LB_FINDSTRING16:
2598 wParam = (INT)(INT16)wParam;
2599 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2600 /* fall through */
2601 case LB_FINDSTRING:
2602 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2604 case LB_FINDSTRINGEXACT16:
2605 wParam = (INT)(INT16)wParam;
2606 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2607 /* fall through */
2608 case LB_FINDSTRINGEXACT:
2609 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2611 case LB_SELECTSTRING16:
2612 wParam = (INT)(INT16)wParam;
2613 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2614 /* fall through */
2615 case LB_SELECTSTRING:
2617 INT index = LISTBOX_FindString( wnd, descr, wParam,
2618 (LPCSTR)lParam, FALSE );
2619 if (index == LB_ERR)
2620 return LB_ERR;
2621 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2622 return index;
2625 case LB_GETSEL16:
2626 wParam = (INT)(INT16)wParam;
2627 /* fall through */
2628 case LB_GETSEL:
2629 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2630 return LB_ERR;
2631 return descr->items[wParam].selected;
2633 case LB_SETSEL16:
2634 lParam = (INT)(INT16)lParam;
2635 /* fall through */
2636 case LB_SETSEL:
2637 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2639 case LB_SETCURSEL16:
2640 wParam = (INT)(INT16)wParam;
2641 /* fall through */
2642 case LB_SETCURSEL:
2643 if (IS_MULTISELECT(descr)) return LB_ERR;
2644 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2645 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2647 case LB_GETSELCOUNT16:
2648 case LB_GETSELCOUNT:
2649 return LISTBOX_GetSelCount( wnd, descr );
2651 case LB_GETSELITEMS16:
2652 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2653 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2655 case LB_GETSELITEMS:
2656 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2658 case LB_SELITEMRANGE16:
2659 case LB_SELITEMRANGE:
2660 if (LOWORD(lParam) <= HIWORD(lParam))
2661 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2662 HIWORD(lParam), wParam );
2663 else
2664 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2665 LOWORD(lParam), wParam );
2667 case LB_SELITEMRANGEEX16:
2668 case LB_SELITEMRANGEEX:
2669 if ((INT)lParam >= (INT)wParam)
2670 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2671 else
2672 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2674 case LB_GETHORIZONTALEXTENT16:
2675 case LB_GETHORIZONTALEXTENT:
2676 return descr->horz_extent;
2678 case LB_SETHORIZONTALEXTENT16:
2679 case LB_SETHORIZONTALEXTENT:
2680 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2682 case LB_GETANCHORINDEX16:
2683 case LB_GETANCHORINDEX:
2684 return descr->anchor_item;
2686 case LB_SETANCHORINDEX16:
2687 wParam = (INT)(INT16)wParam;
2688 /* fall through */
2689 case LB_SETANCHORINDEX:
2690 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2691 return LB_ERR;
2692 descr->anchor_item = (INT)wParam;
2693 return LB_OKAY;
2695 case LB_DIR16:
2696 return LISTBOX_Directory( wnd, descr, wParam,
2697 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2699 case LB_DIR:
2700 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2702 case LB_GETLOCALE:
2703 return descr->locale;
2705 case LB_SETLOCALE:
2706 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2707 return LB_OKAY;
2709 case LB_INITSTORAGE:
2710 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2712 case LB_SETCOUNT:
2713 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2715 case LB_SETTABSTOPS16:
2716 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2717 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2719 case LB_SETTABSTOPS:
2720 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2722 case LB_CARETON16:
2723 case LB_CARETON:
2724 if (descr->caret_on)
2725 return LB_OKAY;
2726 descr->caret_on = TRUE;
2727 if ((descr->focus_item != -1) && (descr->in_focus))
2728 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2729 return LB_OKAY;
2731 case LB_CARETOFF16:
2732 case LB_CARETOFF:
2733 if (!descr->caret_on)
2734 return LB_OKAY;
2735 descr->caret_on = FALSE;
2736 if ((descr->focus_item != -1) && (descr->in_focus))
2737 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2738 return LB_OKAY;
2740 case WM_DESTROY:
2741 return LISTBOX_Destroy( wnd, descr );
2743 case WM_ENABLE:
2744 InvalidateRect( hwnd, NULL, TRUE );
2745 return 0;
2747 case WM_SETREDRAW:
2748 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2749 return 0;
2751 case WM_GETDLGCODE:
2752 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2754 case WM_PAINT:
2756 PAINTSTRUCT ps;
2757 HDC hdc = ( wParam ) ? ((HDC)wParam)
2758 : BeginPaint( hwnd, &ps );
2759 ret = LISTBOX_Paint( wnd, descr, hdc );
2760 if( !wParam ) EndPaint( hwnd, &ps );
2762 return ret;
2763 case WM_SIZE:
2764 LISTBOX_UpdateSize( wnd, descr );
2765 return 0;
2766 case WM_GETFONT:
2767 return descr->font;
2768 case WM_SETFONT:
2769 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2770 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2771 return 0;
2772 case WM_SETFOCUS:
2773 descr->in_focus = TRUE;
2774 descr->caret_on = TRUE;
2775 if (descr->focus_item != -1)
2776 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2777 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2778 return 0;
2779 case WM_KILLFOCUS:
2780 descr->in_focus = FALSE;
2781 if ((descr->focus_item != -1) && descr->caret_on)
2782 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2783 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2784 return 0;
2785 case WM_HSCROLL:
2786 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2787 case WM_VSCROLL:
2788 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2789 case WM_MOUSEACTIVATE:
2790 return MA_NOACTIVATE;
2791 case WM_MOUSEWHEEL:
2792 if (wParam & (MK_SHIFT | MK_CONTROL))
2793 return DefWindowProcA( hwnd, msg, wParam, lParam );
2794 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2795 case WM_LBUTTONDOWN:
2796 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2797 (INT16)LOWORD(lParam),
2798 (INT16)HIWORD(lParam) );
2799 case WM_LBUTTONDBLCLK:
2800 if (descr->style & LBS_NOTIFY)
2801 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2802 return 0;
2803 case WM_MOUSEMOVE:
2804 if (GetCapture() == hwnd)
2805 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2806 (INT16)HIWORD(lParam) );
2807 return 0;
2808 case WM_LBUTTONUP:
2809 return LISTBOX_HandleLButtonUp( wnd, descr );
2810 case WM_KEYDOWN:
2811 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2812 case WM_CHAR:
2813 return LISTBOX_HandleChar( wnd, descr, wParam );
2814 case WM_SYSTIMER:
2815 return LISTBOX_HandleSystemTimer( wnd, descr );
2816 case WM_ERASEBKGND:
2817 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2819 RECT rect;
2820 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2821 wParam, (LPARAM)wnd->hwndSelf );
2822 GetClientRect(hwnd, &rect);
2823 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2825 return 1;
2826 case WM_DROPFILES:
2827 if( !descr->lphc )
2828 return SendMessageA( descr->owner, msg, wParam, lParam );
2829 break;
2831 case WM_DROPOBJECT:
2832 case WM_QUERYDROPOBJECT:
2833 case WM_DRAGSELECT:
2834 case WM_DRAGMOVE:
2835 if( !descr->lphc )
2837 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2838 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2839 dragInfo->pt.y );
2840 return SendMessageA( descr->owner, msg, wParam, lParam );
2842 break;
2844 default:
2845 if ((msg >= WM_USER) && (msg < 0xc000))
2846 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2847 hwnd, msg, wParam, lParam );
2848 return DefWindowProcA( hwnd, msg, wParam, lParam );
2850 return 0;
2853 /***********************************************************************
2854 * ListBoxWndProc
2856 * This is just a wrapper for the real wndproc, it only does window locking
2857 * and unlocking.
2859 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2860 WPARAM wParam, LPARAM lParam )
2862 WND* wndPtr = WIN_FindWndPtr( hwnd );
2863 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2865 WIN_ReleaseWndPtr(wndPtr);
2866 return res;
2869 /***********************************************************************
2870 * COMBO_Directory
2872 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2874 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2876 if( wnd )
2878 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2879 if( descr )
2881 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2883 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2884 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2885 WIN_ReleaseWndPtr(wnd);
2886 return lRet;
2888 WIN_ReleaseWndPtr(wnd);
2890 return CB_ERR;
2893 /***********************************************************************
2894 * ComboLBWndProc_locked
2896 * The real combo listbox wndproc, but called with locked WND struct.
2898 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2899 WPARAM wParam, LPARAM lParam )
2901 LRESULT lRet = 0;
2902 HWND hwnd = wnd->hwndSelf;
2904 if (wnd)
2906 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2908 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2909 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2911 if( descr || msg == WM_CREATE )
2913 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2915 switch( msg )
2917 case WM_CREATE:
2918 #define lpcs ((LPCREATESTRUCTA)lParam)
2919 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2920 (UINT)lpcs->lpCreateParams);
2922 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2923 #undef lpcs
2924 return LISTBOX_Create( wnd, lphc );
2925 case WM_MOUSEMOVE:
2926 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2927 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2929 POINT mousePos;
2930 BOOL captured;
2931 RECT clientRect;
2933 mousePos.x = (INT16)LOWORD(lParam);
2934 mousePos.y = (INT16)HIWORD(lParam);
2937 * If we are in a dropdown combobox, we simulate that
2938 * the mouse is captured to show the tracking of the item.
2940 GetClientRect(hwnd, &clientRect);
2942 if (PtInRect( &clientRect, mousePos ))
2944 captured = descr->captured;
2945 descr->captured = TRUE;
2947 LISTBOX_HandleMouseMove( wnd, descr,
2948 mousePos.x, mousePos.y);
2950 descr->captured = captured;
2953 else
2955 LISTBOX_HandleMouseMove( wnd, descr,
2956 mousePos.x, mousePos.y);
2959 return 0;
2962 else
2965 * If we are in Win3.1 look, go with the default behavior.
2967 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2969 case WM_LBUTTONUP:
2970 if (TWEAK_WineLook > WIN31_LOOK)
2972 POINT mousePos;
2973 RECT clientRect;
2976 * If the mouse button "up" is not in the listbox,
2977 * we make sure there is no selection by re-selecting the
2978 * item that was selected when the listbox was made visible.
2980 mousePos.x = (INT16)LOWORD(lParam);
2981 mousePos.y = (INT16)HIWORD(lParam);
2983 GetClientRect(hwnd, &clientRect);
2986 * When the user clicks outside the combobox and the focus
2987 * is lost, the owning combobox will send a fake buttonup with
2988 * 0xFFFFFFF as the mouse location, we must also revert the
2989 * selection to the original selection.
2991 if ( (lParam == 0xFFFFFFFF) ||
2992 (!PtInRect( &clientRect, mousePos )) )
2994 LISTBOX_MoveCaret( wnd,
2995 descr,
2996 lphc->droppedIndex,
2997 FALSE );
3000 return LISTBOX_HandleLButtonUp( wnd, descr );
3001 case WM_LBUTTONDBLCLK:
3002 case WM_LBUTTONDOWN:
3003 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
3004 (INT16)LOWORD(lParam),
3005 (INT16)HIWORD(lParam) );
3006 case WM_MOUSEACTIVATE:
3007 return MA_NOACTIVATE;
3008 case WM_NCACTIVATE:
3009 return FALSE;
3010 case WM_KEYDOWN:
3011 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3013 /* for some reason(?) Windows makes it possible to
3014 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3016 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3017 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3018 && (wParam == VK_DOWN || wParam == VK_UP)) )
3020 COMBO_FlipListbox( lphc, FALSE, FALSE );
3021 return 0;
3024 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3026 case LB_SETCURSEL16:
3027 case LB_SETCURSEL:
3028 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
3029 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3030 return lRet;
3031 case WM_NCDESTROY:
3032 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3033 lphc->hWndLBox = 0;
3034 /* fall through */
3036 default:
3037 return ListBoxWndProc( hwnd, msg, wParam, lParam );
3040 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3042 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3044 return lRet;
3047 /***********************************************************************
3048 * ComboLBWndProc
3050 * NOTE: in Windows, winproc address of the ComboLBox is the same
3051 * as that of the Listbox.
3053 * This is just a wrapper for the real wndproc, it only does window locking
3054 * and unlocking.
3056 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3057 WPARAM wParam, LPARAM lParam )
3059 WND *wnd = WIN_FindWndPtr( hwnd );
3060 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3062 WIN_ReleaseWndPtr(wnd);
3063 return res;