Set clipping region to client window in LISTBOX_Paint.
[wine.git] / controls / listbox.c
blob7e2cbdfb650cf9a842de9ed96fb09a969666dd77
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 if ((descr->height > descr->item_height) &&
352 (descr->height % descr->item_height))
354 TRACE("[%04x]: changing height %d -> %d\n",
355 wnd->hwndSelf, descr->height,
356 descr->height - descr->height%descr->item_height );
357 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
358 wnd->rectWindow.right - wnd->rectWindow.left,
359 wnd->rectWindow.bottom - wnd->rectWindow.top -
360 (descr->height % descr->item_height),
361 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
362 return;
365 TRACE("[%04x]: new size = %d,%d\n",
366 wnd->hwndSelf, descr->width, descr->height );
367 LISTBOX_UpdatePage( wnd, descr );
368 LISTBOX_UpdateScroll( wnd, descr );
372 /***********************************************************************
373 * LISTBOX_GetItemRect
375 * Get the rectangle enclosing an item, in listbox client coordinates.
376 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
378 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
379 RECT *rect )
381 /* Index <= 0 is legal even on empty listboxes */
382 if (index && (index >= descr->nb_items)) return -1;
383 SetRect( rect, 0, 0, descr->width, descr->height );
384 if (descr->style & LBS_MULTICOLUMN)
386 INT col = (index / descr->page_size) -
387 (descr->top_item / descr->page_size);
388 rect->left += col * descr->column_width;
389 rect->right = rect->left + descr->column_width;
390 rect->top += (index % descr->page_size) * descr->item_height;
391 rect->bottom = rect->top + descr->item_height;
393 else if (descr->style & LBS_OWNERDRAWVARIABLE)
395 INT i;
396 rect->right += descr->horz_pos;
397 if ((index >= 0) && (index < descr->nb_items))
399 if (index < descr->top_item)
401 for (i = descr->top_item-1; i >= index; i--)
402 rect->top -= descr->items[i].height;
404 else
406 for (i = descr->top_item; i < index; i++)
407 rect->top += descr->items[i].height;
409 rect->bottom = rect->top + descr->items[index].height;
413 else
415 rect->top += (index - descr->top_item) * descr->item_height;
416 rect->bottom = rect->top + descr->item_height;
417 rect->right += descr->horz_pos;
420 return ((rect->left < descr->width) && (rect->right > 0) &&
421 (rect->top < descr->height) && (rect->bottom > 0));
425 /***********************************************************************
426 * LISTBOX_GetItemFromPoint
428 * Return the item nearest from point (x,y) (in client coordinates).
430 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
431 INT x, INT y )
433 INT index = descr->top_item;
435 if (!descr->nb_items) return -1; /* No items */
436 if (descr->style & LBS_OWNERDRAWVARIABLE)
438 INT pos = 0;
439 if (y >= 0)
441 while (index < descr->nb_items)
443 if ((pos += descr->items[index].height) > y) break;
444 index++;
447 else
449 while (index > 0)
451 index--;
452 if ((pos -= descr->items[index].height) <= y) break;
456 else if (descr->style & LBS_MULTICOLUMN)
458 if (y >= descr->item_height * descr->page_size) return -1;
459 if (y >= 0) index += y / descr->item_height;
460 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
461 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
463 else
465 index += (y / descr->item_height);
467 if (index < 0) return 0;
468 if (index >= descr->nb_items) return -1;
469 return index;
473 /***********************************************************************
474 * LISTBOX_PaintItem
476 * Paint an item.
478 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
479 const RECT *rect, INT index, UINT action )
481 LB_ITEMDATA *item = NULL;
482 if (index < descr->nb_items) item = &descr->items[index];
484 if (IS_OWNERDRAW(descr))
486 DRAWITEMSTRUCT dis;
488 if (!item)
490 if (action == ODA_FOCUS)
491 DrawFocusRect( hdc, rect );
492 else
493 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
494 return;
496 dis.CtlType = ODT_LISTBOX;
497 dis.CtlID = wnd->wIDmenu;
498 dis.hwndItem = wnd->hwndSelf;
499 dis.itemAction = action;
500 dis.hDC = hdc;
501 dis.itemID = index;
502 dis.itemState = 0;
503 if (item && item->selected) dis.itemState |= ODS_SELECTED;
504 if ((descr->focus_item == index) &&
505 (descr->caret_on) &&
506 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
507 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
508 dis.itemData = item ? item->data : 0;
509 dis.rcItem = *rect;
510 TRACE("[%04x]: drawitem %d (%s) action=%02x "
511 "state=%02x rect=%d,%d-%d,%d\n",
512 wnd->hwndSelf, index, item ? item->str : "", action,
513 dis.itemState, rect->left, rect->top,
514 rect->right, rect->bottom );
515 SendMessageA(descr->owner, WM_DRAWITEM, wnd->wIDmenu, (LPARAM)&dis);
517 else
519 COLORREF oldText = 0, oldBk = 0;
521 if (action == ODA_FOCUS)
523 DrawFocusRect( hdc, rect );
524 return;
526 if (item && item->selected)
528 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
529 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
532 TRACE("[%04x]: painting %d (%s) action=%02x "
533 "rect=%d,%d-%d,%d\n",
534 wnd->hwndSelf, index, item ? item->str : "", action,
535 rect->left, rect->top, rect->right, rect->bottom );
536 if (!item)
537 ExtTextOutA( hdc, rect->left + 1, rect->top,
538 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
539 else if (!(descr->style & LBS_USETABSTOPS))
540 ExtTextOutA( hdc, rect->left + 1, rect->top,
541 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
542 strlen(item->str), NULL );
543 else
545 /* Output empty string to paint background in the full width. */
546 ExtTextOutA( hdc, rect->left + 1, rect->top,
547 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
548 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
549 item->str, strlen(item->str),
550 descr->nb_tabs, descr->tabs, 0);
552 if (item && item->selected)
554 SetBkColor( hdc, oldBk );
555 SetTextColor( hdc, oldText );
557 if ((descr->focus_item == index) &&
558 (descr->caret_on) &&
559 (descr->in_focus)) DrawFocusRect( hdc, rect );
564 /***********************************************************************
565 * LISTBOX_SetRedraw
567 * Change the redraw flag.
569 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
571 if (on)
573 if (!(descr->style & LBS_NOREDRAW)) return;
574 descr->style &= ~LBS_NOREDRAW;
575 if (descr->style & LBS_DISPLAYCHANGED)
576 { /* page was changed while setredraw false, refresh automatically */
577 InvalidateRect(wnd->hwndSelf, NULL, TRUE);
578 if ((descr->top_item + descr->page_size) > descr->nb_items)
579 { /* reset top of page if less than number of items/page */
580 descr->top_item = descr->nb_items - descr->page_size;
581 if (descr->top_item < 0) descr->top_item = 0;
583 descr->style &= ~LBS_DISPLAYCHANGED;
585 LISTBOX_UpdateScroll( wnd, descr );
587 else descr->style |= LBS_NOREDRAW;
591 /***********************************************************************
592 * LISTBOX_RepaintItem
594 * Repaint a single item synchronously.
596 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
597 UINT action )
599 HDC hdc;
600 RECT rect;
601 HFONT oldFont = 0;
602 HBRUSH hbrush, oldBrush = 0;
604 /* Do not repaint the item if the item is not visible */
605 if (!IsWindowVisible(wnd->hwndSelf)) return;
606 if (descr->style & LBS_NOREDRAW)
608 descr->style |= LBS_DISPLAYCHANGED;
609 return;
611 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
612 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
613 if (descr->font) oldFont = SelectObject( hdc, descr->font );
614 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
615 hdc, (LPARAM)wnd->hwndSelf );
616 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
617 if (wnd->dwStyle & WS_DISABLED)
618 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
619 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
620 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
621 if (oldFont) SelectObject( hdc, oldFont );
622 if (oldBrush) SelectObject( hdc, oldBrush );
623 ReleaseDC( wnd->hwndSelf, hdc );
627 /***********************************************************************
628 * LISTBOX_InitStorage
630 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
631 DWORD bytes )
633 LB_ITEMDATA *item;
635 nb_items += LB_ARRAY_GRANULARITY - 1;
636 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
637 if (descr->items)
638 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
639 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
640 nb_items * sizeof(LB_ITEMDATA) )))
642 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
643 return LB_ERRSPACE;
645 descr->items = item;
646 return LB_OKAY;
650 /***********************************************************************
651 * LISTBOX_SetTabStops
653 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
654 LPINT tabs, BOOL short_ints )
656 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
657 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
658 if (!(descr->nb_tabs = count))
660 descr->tabs = NULL;
661 return TRUE;
663 /* FIXME: count = 1 */
664 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
665 descr->nb_tabs * sizeof(INT) )))
666 return FALSE;
667 if (short_ints)
669 INT i;
670 LPINT16 p = (LPINT16)tabs;
672 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
673 for (i = 0; i < descr->nb_tabs; i++) {
674 descr->tabs[i] = *p++<<1; /* FIXME */
675 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
677 if (TRACE_ON(listbox)) DPRINTF("\n");
679 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
680 /* FIXME: repaint the window? */
681 return TRUE;
685 /***********************************************************************
686 * LISTBOX_GetText
688 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
689 LPSTR buffer )
691 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
692 if (HAS_STRINGS(descr))
694 if (!buffer)
695 return strlen(descr->items[index].str);
696 lstrcpyA( buffer, descr->items[index].str );
697 return strlen(buffer);
698 } else {
699 if (buffer)
700 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
701 return sizeof(DWORD);
706 /***********************************************************************
707 * LISTBOX_FindStringPos
709 * Find the nearest string located before a given string in sort order.
710 * If 'exact' is TRUE, return an error if we don't get an exact match.
712 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
713 BOOL exact )
715 INT index, min, max, res = -1;
717 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
718 min = 0;
719 max = descr->nb_items;
720 while (min != max)
722 index = (min + max) / 2;
723 if (HAS_STRINGS(descr))
724 res = lstrcmpiA( descr->items[index].str, str );
725 else
727 COMPAREITEMSTRUCT cis;
729 cis.CtlType = ODT_LISTBOX;
730 cis.CtlID = wnd->wIDmenu;
731 cis.hwndItem = wnd->hwndSelf;
732 cis.itemID1 = index;
733 cis.itemData1 = descr->items[index].data;
734 cis.itemID2 = -1;
735 cis.itemData2 = (DWORD)str;
736 cis.dwLocaleId = descr->locale;
737 res = SendMessageA( descr->owner, WM_COMPAREITEM,
738 wnd->wIDmenu, (LPARAM)&cis );
740 if (!res) return index;
741 if (res > 0) max = index;
742 else min = index + 1;
744 return exact ? -1 : max;
748 /***********************************************************************
749 * LISTBOX_FindFileStrPos
751 * Find the nearest string located before a given string in directory
752 * sort order (i.e. first files, then directories, then drives).
754 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
756 INT min, max, res = -1;
758 if (!HAS_STRINGS(descr))
759 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
760 min = 0;
761 max = descr->nb_items;
762 while (min != max)
764 INT index = (min + max) / 2;
765 const char *p = descr->items[index].str;
766 if (*p == '[') /* drive or directory */
768 if (*str != '[') res = -1;
769 else if (p[1] == '-') /* drive */
771 if (str[1] == '-') res = str[2] - p[2];
772 else res = -1;
774 else /* directory */
776 if (str[1] == '-') res = 1;
777 else res = lstrcmpiA( str, p );
780 else /* filename */
782 if (*str == '[') res = 1;
783 else res = lstrcmpiA( str, p );
785 if (!res) return index;
786 if (res < 0) max = index;
787 else min = index + 1;
789 return max;
793 /***********************************************************************
794 * LISTBOX_FindString
796 * Find the item beginning with a given string.
798 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
799 LPCSTR str, BOOL exact )
801 INT i;
802 LB_ITEMDATA *item;
804 if (start >= descr->nb_items) start = -1;
805 item = descr->items + start + 1;
806 if (HAS_STRINGS(descr))
808 if (!str || ! str[0] ) return LB_ERR;
809 if (exact)
811 for (i = start + 1; i < descr->nb_items; i++, item++)
812 if (!lstrcmpiA( str, item->str )) return i;
813 for (i = 0, item = descr->items; i <= start; i++, item++)
814 if (!lstrcmpiA( str, item->str )) return i;
816 else
818 /* Special case for drives and directories: ignore prefix */
819 #define CHECK_DRIVE(item) \
820 if ((item)->str[0] == '[') \
822 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
823 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
824 return i; \
827 INT len = strlen(str);
828 for (i = start + 1; i < descr->nb_items; i++, item++)
830 if (!lstrncmpiA( str, item->str, len )) return i;
831 CHECK_DRIVE(item);
833 for (i = 0, item = descr->items; i <= start; i++, item++)
835 if (!lstrncmpiA( str, item->str, len )) return i;
836 CHECK_DRIVE(item);
838 #undef CHECK_DRIVE
841 else
843 if (exact && (descr->style & LBS_SORT))
844 /* If sorted, use a WM_COMPAREITEM binary search */
845 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
847 /* Otherwise use a linear search */
848 for (i = start + 1; i < descr->nb_items; i++, item++)
849 if (item->data == (DWORD)str) return i;
850 for (i = 0, item = descr->items; i <= start; i++, item++)
851 if (item->data == (DWORD)str) return i;
853 return LB_ERR;
857 /***********************************************************************
858 * LISTBOX_GetSelCount
860 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
862 INT i, count;
863 LB_ITEMDATA *item = descr->items;
865 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
866 for (i = count = 0; i < descr->nb_items; i++, item++)
867 if (item->selected) count++;
868 return count;
872 /***********************************************************************
873 * LISTBOX_GetSelItems16
875 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
876 LPINT16 array )
878 INT i, count;
879 LB_ITEMDATA *item = descr->items;
881 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
882 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
883 if (item->selected) array[count++] = (INT16)i;
884 return count;
888 /***********************************************************************
889 * LISTBOX_GetSelItems32
891 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
892 LPINT array )
894 INT i, count;
895 LB_ITEMDATA *item = descr->items;
897 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
898 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
899 if (item->selected) array[count++] = i;
900 return count;
904 /***********************************************************************
905 * LISTBOX_Paint
907 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
909 INT i, col_pos = descr->page_size - 1;
910 RECT rect;
911 RECT focusRect = {-1, -1, -1, -1};
912 HFONT oldFont = 0;
913 HBRUSH hbrush, oldBrush = 0;
914 INT focusItem;
916 if (descr->style & LBS_NOREDRAW) return 0;
918 SetRect( &rect, 0, 0, descr->width, descr->height );
919 if (descr->style & LBS_MULTICOLUMN)
920 rect.right = rect.left + descr->column_width;
921 else if (descr->horz_pos)
923 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
924 rect.right += descr->horz_pos;
927 if (IS_OWNERDRAW(descr))
929 RECT r;
930 HRGN hrgn;
931 GetClientRect(wnd->hwndSelf, &r);
932 hrgn = CreateRectRgnIndirect(&r);
933 SelectClipRgn( hdc, hrgn);
934 DeleteObject( hrgn );
937 if (descr->font) oldFont = SelectObject( hdc, descr->font );
938 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
939 hdc, (LPARAM)wnd->hwndSelf );
940 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
941 if (wnd->dwStyle & WS_DISABLED)
942 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
944 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
945 (descr->in_focus))
947 /* Special case for empty listbox: paint focus rect */
948 rect.bottom = rect.top + descr->item_height;
949 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
950 ODA_FOCUS );
951 rect.top = rect.bottom;
954 /* Paint all the item, regarding the selection
955 Focus state will be painted after */
956 focusItem = descr->focus_item;
957 descr->focus_item = -1;
959 for (i = descr->top_item; i < descr->nb_items; i++)
961 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
962 rect.bottom = rect.top + descr->item_height;
963 else
964 rect.bottom = rect.top + descr->items[i].height;
966 if (i == focusItem)
968 /* keep the focus rect, to paint the focus item after */
969 focusRect.left = rect.left;
970 focusRect.right = rect.right;
971 focusRect.top = rect.top;
972 focusRect.bottom = rect.bottom;
974 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
975 rect.top = rect.bottom;
977 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
979 if (!IS_OWNERDRAW(descr))
981 /* Clear the bottom of the column */
982 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
983 if (rect.top < descr->height)
985 rect.bottom = descr->height;
986 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
987 &rect, NULL, 0, NULL );
991 /* Go to the next column */
992 rect.left += descr->column_width;
993 rect.right += descr->column_width;
994 rect.top = 0;
995 col_pos = descr->page_size - 1;
997 else
999 col_pos--;
1000 if (rect.top >= descr->height) break;
1004 /* Paint the focus item now */
1005 descr->focus_item = focusItem;
1006 if (focusRect.top != focusRect.bottom && descr->caret_on)
1007 LISTBOX_PaintItem( wnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS );
1009 if (!IS_OWNERDRAW(descr))
1011 /* Clear the remainder of the client area */
1012 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
1013 if (rect.top < descr->height)
1015 rect.bottom = descr->height;
1016 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1017 &rect, NULL, 0, NULL );
1019 if (rect.right < descr->width)
1021 rect.left = rect.right;
1022 rect.right = descr->width;
1023 rect.top = 0;
1024 rect.bottom = descr->height;
1025 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1026 &rect, NULL, 0, NULL );
1029 if (oldFont) SelectObject( hdc, oldFont );
1030 if (oldBrush) SelectObject( hdc, oldBrush );
1031 return 0;
1035 /***********************************************************************
1036 * LISTBOX_InvalidateItems
1038 * Invalidate all items from a given item. If the specified item is not
1039 * visible, nothing happens.
1041 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
1043 RECT rect;
1045 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1047 if (descr->style & LBS_NOREDRAW)
1049 descr->style |= LBS_DISPLAYCHANGED;
1050 return;
1052 rect.bottom = descr->height;
1053 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1054 if (descr->style & LBS_MULTICOLUMN)
1056 /* Repaint the other columns */
1057 rect.left = rect.right;
1058 rect.right = descr->width;
1059 rect.top = 0;
1060 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1066 /***********************************************************************
1067 * LISTBOX_GetItemHeight
1069 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1071 if (descr->style & LBS_OWNERDRAWVARIABLE)
1073 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1074 return descr->items[index].height;
1076 else return descr->item_height;
1080 /***********************************************************************
1081 * LISTBOX_SetItemHeight
1083 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1084 UINT height )
1086 if (!height) height = 1;
1088 if (descr->style & LBS_OWNERDRAWVARIABLE)
1090 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1091 TRACE("[%04x]: item %d height = %d\n",
1092 wnd->hwndSelf, index, height );
1093 descr->items[index].height = height;
1094 LISTBOX_UpdateScroll( wnd, descr );
1095 LISTBOX_InvalidateItems( wnd, descr, index );
1097 else if (height != descr->item_height)
1099 TRACE("[%04x]: new height = %d\n",
1100 wnd->hwndSelf, height );
1101 descr->item_height = height;
1102 LISTBOX_UpdatePage( wnd, descr );
1103 LISTBOX_UpdateScroll( wnd, descr );
1104 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1106 return LB_OKAY;
1110 /***********************************************************************
1111 * LISTBOX_SetHorizontalPos
1113 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1115 INT diff;
1117 if (pos > descr->horz_extent - descr->width)
1118 pos = descr->horz_extent - descr->width;
1119 if (pos < 0) pos = 0;
1120 if (!(diff = descr->horz_pos - pos)) return;
1121 TRACE("[%04x]: new horz pos = %d\n",
1122 wnd->hwndSelf, pos );
1123 descr->horz_pos = pos;
1124 LISTBOX_UpdateScroll( wnd, descr );
1125 if (abs(diff) < descr->width)
1126 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1127 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1128 else
1129 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1133 /***********************************************************************
1134 * LISTBOX_SetHorizontalExtent
1136 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1137 UINT extent )
1139 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1140 return LB_OKAY;
1141 if (extent <= 0) extent = 1;
1142 if (extent == descr->horz_extent) return LB_OKAY;
1143 TRACE("[%04x]: new horz extent = %d\n",
1144 wnd->hwndSelf, extent );
1145 descr->horz_extent = extent;
1146 if (descr->horz_pos > extent - descr->width)
1147 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1148 else
1149 LISTBOX_UpdateScroll( wnd, descr );
1150 return LB_OKAY;
1154 /***********************************************************************
1155 * LISTBOX_SetColumnWidth
1157 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1159 if (width == descr->column_width) return LB_OKAY;
1160 TRACE("[%04x]: new column width = %d\n",
1161 wnd->hwndSelf, width );
1162 descr->column_width = width;
1163 LISTBOX_UpdatePage( wnd, descr );
1164 return LB_OKAY;
1168 /***********************************************************************
1169 * LISTBOX_SetFont
1171 * Returns the item height.
1173 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1175 HDC hdc;
1176 HFONT oldFont = 0;
1177 TEXTMETRICA tm;
1179 descr->font = font;
1181 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1183 ERR("unable to get DC.\n" );
1184 return 16;
1186 if (font) oldFont = SelectObject( hdc, font );
1187 GetTextMetricsA( hdc, &tm );
1188 if (oldFont) SelectObject( hdc, oldFont );
1189 ReleaseDC( wnd->hwndSelf, hdc );
1190 if (!IS_OWNERDRAW(descr))
1191 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1192 return tm.tmHeight ;
1196 /***********************************************************************
1197 * LISTBOX_MakeItemVisible
1199 * Make sure that a given item is partially or fully visible.
1201 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1202 BOOL fully )
1204 INT top;
1206 if (index <= descr->top_item) top = index;
1207 else if (descr->style & LBS_MULTICOLUMN)
1209 INT cols = descr->width;
1210 if (!fully) cols += descr->column_width - 1;
1211 if (cols >= descr->column_width) cols /= descr->column_width;
1212 else cols = 1;
1213 if (index < descr->top_item + (descr->page_size * cols)) return;
1214 top = index - descr->page_size * (cols - 1);
1216 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1218 INT height = fully ? descr->items[index].height : 1;
1219 for (top = index; top > descr->top_item; top--)
1220 if ((height += descr->items[top-1].height) > descr->height) break;
1222 else
1224 if (index < descr->top_item + descr->page_size) return;
1225 if (!fully && (index == descr->top_item + descr->page_size) &&
1226 (descr->height > (descr->page_size * descr->item_height))) return;
1227 top = index - descr->page_size + 1;
1229 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1233 /***********************************************************************
1234 * LISTBOX_SelectItemRange
1236 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1238 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1239 INT last, BOOL on )
1241 INT i;
1243 /* A few sanity checks */
1245 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1246 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1247 if (last == -1) last = descr->nb_items - 1;
1248 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1249 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1250 /* selected_item reflects last selected/unselected item on multiple sel */
1251 descr->selected_item = last;
1253 if (on) /* Turn selection on */
1255 for (i = first; i <= last; i++)
1257 if (descr->items[i].selected) continue;
1258 descr->items[i].selected = TRUE;
1259 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1262 else /* Turn selection off */
1264 for (i = first; i <= last; i++)
1266 if (!descr->items[i].selected) continue;
1267 descr->items[i].selected = FALSE;
1268 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1271 return LB_OKAY;
1275 /***********************************************************************
1276 * LISTBOX_SetCaretIndex
1278 * NOTES
1279 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1282 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1283 BOOL fully_visible )
1285 INT oldfocus = descr->focus_item;
1287 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1288 if (index == oldfocus) return LB_OKAY;
1289 descr->focus_item = index;
1290 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1291 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1293 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1294 if (descr->caret_on && (descr->in_focus))
1295 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1297 return LB_OKAY;
1301 /***********************************************************************
1302 * LISTBOX_SetSelection
1304 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1305 BOOL on, BOOL send_notify )
1307 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1309 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1310 if (descr->style & LBS_MULTIPLESEL)
1312 if (index == -1) /* Select all items */
1313 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1314 else /* Only one item */
1315 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1317 else
1319 INT oldsel = descr->selected_item;
1320 if (index == oldsel) return LB_OKAY;
1321 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1322 if (index != -1) descr->items[index].selected = TRUE;
1323 descr->selected_item = index;
1324 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1325 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1326 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1327 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1328 else
1329 if( descr->lphc ) /* set selection change flag for parent combo */
1330 descr->lphc->wState |= CBF_SELCHANGE;
1332 return LB_OKAY;
1336 /***********************************************************************
1337 * LISTBOX_MoveCaret
1339 * Change the caret position and extend the selection to the new caret.
1341 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1342 BOOL fully_visible )
1344 INT oldfocus = descr->focus_item;
1346 if ((index < 0) || (index >= descr->nb_items))
1347 return;
1349 /* Important, repaint needs to be done in this order if
1350 you want to mimic Windows behavior:
1351 1. Remove the focus and paint the item
1352 2. Remove the selection and paint the item(s)
1353 3. Set the selection and repaint the item(s)
1354 4. Set the focus to 'index' and repaint the item */
1356 /* 1. remove the focus and repaint the item */
1357 descr->focus_item = -1;
1358 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1359 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1361 /* 2. then turn off the previous selection */
1362 /* 3. repaint the new selected item */
1363 if (descr->style & LBS_EXTENDEDSEL)
1365 if (descr->anchor_item != -1)
1367 INT first = min( index, descr->anchor_item );
1368 INT last = max( index, descr->anchor_item );
1369 if (first > 0)
1370 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1371 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1372 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1375 else if (!(descr->style & LBS_MULTIPLESEL))
1377 /* Set selection to new caret item */
1378 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1381 /* 4. repaint the new item with the focus */
1382 descr->focus_item = index;
1383 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1384 if (descr->caret_on && (descr->in_focus))
1385 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1389 /***********************************************************************
1390 * LISTBOX_InsertItem
1392 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1393 LPSTR str, DWORD data )
1395 LB_ITEMDATA *item;
1396 INT max_items;
1397 INT oldfocus = descr->focus_item;
1399 if (index == -1) index = descr->nb_items;
1400 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1401 if (!descr->items) max_items = 0;
1402 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1403 if (descr->nb_items == max_items)
1405 /* We need to grow the array */
1406 max_items += LB_ARRAY_GRANULARITY;
1407 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1408 max_items * sizeof(LB_ITEMDATA) )))
1410 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1411 return LB_ERRSPACE;
1413 descr->items = item;
1416 /* Insert the item structure */
1418 item = &descr->items[index];
1419 if (index < descr->nb_items)
1420 RtlMoveMemory( item + 1, item,
1421 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1422 item->str = str;
1423 item->data = data;
1424 item->height = 0;
1425 item->selected = FALSE;
1426 descr->nb_items++;
1428 /* Get item height */
1430 if (descr->style & LBS_OWNERDRAWVARIABLE)
1432 MEASUREITEMSTRUCT mis;
1434 mis.CtlType = ODT_LISTBOX;
1435 mis.CtlID = wnd->wIDmenu;
1436 mis.itemID = index;
1437 mis.itemData = descr->items[index].data;
1438 mis.itemHeight = descr->item_height;
1439 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
1440 item->height = mis.itemHeight ? mis.itemHeight : 1;
1441 TRACE("[%04x]: measure item %d (%s) = %d\n",
1442 wnd->hwndSelf, index, str ? str : "", item->height );
1445 /* Repaint the items */
1447 LISTBOX_UpdateScroll( wnd, descr );
1448 LISTBOX_InvalidateItems( wnd, descr, index );
1450 /* Move selection and focused item */
1451 /* If listbox was empty, set focus to the first item */
1452 if (descr->nb_items == 1)
1453 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1454 /* single select don't change selection index in win31 */
1455 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1457 descr->selected_item++;
1458 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1460 else
1462 if (index <= descr->selected_item)
1464 descr->selected_item++;
1465 descr->focus_item = oldfocus; /* focus not changed */
1468 return LB_OKAY;
1472 /***********************************************************************
1473 * LISTBOX_InsertString
1475 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1476 LPCSTR str )
1478 LPSTR new_str = NULL;
1479 DWORD data = 0;
1480 LRESULT ret;
1482 if (HAS_STRINGS(descr))
1484 if (!str) str="";
1485 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1487 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1488 return LB_ERRSPACE;
1491 else data = (DWORD)str;
1493 if (index == -1) index = descr->nb_items;
1494 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1496 if (new_str) HeapFree( descr->heap, 0, new_str );
1497 return ret;
1500 TRACE("[%04x]: added item %d '%s'\n",
1501 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1502 return index;
1506 /***********************************************************************
1507 * LISTBOX_DeleteItem
1509 * Delete the content of an item. 'index' must be a valid index.
1511 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1513 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1514 * while Win95 sends it for all items with user data.
1515 * It's probably better to send it too often than not
1516 * often enough, so this is what we do here.
1518 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1520 DELETEITEMSTRUCT dis;
1522 dis.CtlType = ODT_LISTBOX;
1523 dis.CtlID = wnd->wIDmenu;
1524 dis.itemID = index;
1525 dis.hwndItem = wnd->hwndSelf;
1526 dis.itemData = descr->items[index].data;
1527 SendMessageA( descr->owner, WM_DELETEITEM, wnd->wIDmenu, (LPARAM)&dis );
1529 if (HAS_STRINGS(descr) && descr->items[index].str)
1530 HeapFree( descr->heap, 0, descr->items[index].str );
1534 /***********************************************************************
1535 * LISTBOX_RemoveItem
1537 * Remove an item from the listbox and delete its content.
1539 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1541 LB_ITEMDATA *item;
1542 INT max_items;
1544 if (index == -1) index = descr->nb_items - 1;
1545 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1547 /* We need to invalidate the original rect instead of the updated one. */
1548 LISTBOX_InvalidateItems( wnd, descr, index );
1550 LISTBOX_DeleteItem( wnd, descr, index );
1552 /* Remove the item */
1554 item = &descr->items[index];
1555 if (index < descr->nb_items-1)
1556 RtlMoveMemory( item, item + 1,
1557 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1558 descr->nb_items--;
1559 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1561 /* Shrink the item array if possible */
1563 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1564 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1566 max_items -= LB_ARRAY_GRANULARITY;
1567 item = HeapReAlloc( descr->heap, 0, descr->items,
1568 max_items * sizeof(LB_ITEMDATA) );
1569 if (item) descr->items = item;
1571 /* Repaint the items */
1573 LISTBOX_UpdateScroll( wnd, descr );
1574 /* if we removed the scrollbar, reset the top of the list
1575 (correct for owner-drawn ???) */
1576 if (descr->nb_items == descr->page_size)
1577 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1579 /* Move selection and focused item */
1580 if (!IS_MULTISELECT(descr))
1582 if (index == descr->selected_item)
1583 descr->selected_item = -1;
1584 else if (index < descr->selected_item)
1586 descr->selected_item--;
1587 if (ISWIN31) /* win 31 do not change the selected item number */
1588 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1592 if (descr->focus_item >= descr->nb_items)
1594 descr->focus_item = descr->nb_items - 1;
1595 if (descr->focus_item < 0) descr->focus_item = 0;
1597 return LB_OKAY;
1601 /***********************************************************************
1602 * LISTBOX_ResetContent
1604 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1606 INT i;
1608 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1609 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1610 descr->nb_items = 0;
1611 descr->top_item = 0;
1612 descr->selected_item = -1;
1613 descr->focus_item = 0;
1614 descr->anchor_item = -1;
1615 descr->items = NULL;
1616 LISTBOX_UpdateScroll( wnd, descr );
1617 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1621 /***********************************************************************
1622 * LISTBOX_SetCount
1624 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1626 LRESULT ret;
1628 if (HAS_STRINGS(descr)) return LB_ERR;
1629 /* FIXME: this is far from optimal... */
1630 if (count > descr->nb_items)
1632 while (count > descr->nb_items)
1633 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1634 return ret;
1636 else if (count < descr->nb_items)
1638 while (count < descr->nb_items)
1639 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1640 return ret;
1642 return LB_OKAY;
1646 /***********************************************************************
1647 * LISTBOX_Directory
1649 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1650 LPCSTR filespec, BOOL long_names )
1652 HANDLE handle;
1653 LRESULT ret = LB_OKAY;
1654 WIN32_FIND_DATAA entry;
1655 int pos;
1657 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1659 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1661 else
1665 char buffer[270];
1666 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1668 if (!(attrib & DDL_DIRECTORY) ||
1669 !strcmp( entry.cAlternateFileName, "." )) continue;
1670 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1671 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1673 else /* not a directory */
1675 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1676 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1678 if ((attrib & DDL_EXCLUSIVE) &&
1679 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1680 continue;
1681 #undef ATTRIBS
1682 if (long_names) strcpy( buffer, entry.cFileName );
1683 else strcpy( buffer, entry.cAlternateFileName );
1685 if (!long_names) CharLowerA( buffer );
1686 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1687 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1688 break;
1689 } while (FindNextFileA( handle, &entry ));
1690 FindClose( handle );
1693 if ((ret >= 0) && (attrib & DDL_DRIVES))
1695 char buffer[] = "[-a-]";
1696 int drive;
1697 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1699 if (!DRIVE_IsValid(drive)) continue;
1700 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1701 break;
1704 return ret;
1708 /***********************************************************************
1709 * LISTBOX_HandleVScroll
1711 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1712 WPARAM wParam, LPARAM lParam )
1714 SCROLLINFO info;
1716 if (descr->style & LBS_MULTICOLUMN) return 0;
1717 switch(LOWORD(wParam))
1719 case SB_LINEUP:
1720 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1721 break;
1722 case SB_LINEDOWN:
1723 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1724 break;
1725 case SB_PAGEUP:
1726 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1727 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1728 break;
1729 case SB_PAGEDOWN:
1730 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1731 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1732 break;
1733 case SB_THUMBPOSITION:
1734 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1735 break;
1736 case SB_THUMBTRACK:
1737 info.cbSize = sizeof(info);
1738 info.fMask = SIF_TRACKPOS;
1739 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1740 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1741 break;
1742 case SB_TOP:
1743 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1744 break;
1745 case SB_BOTTOM:
1746 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1747 break;
1749 return 0;
1753 /***********************************************************************
1754 * LISTBOX_HandleHScroll
1756 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1757 WPARAM wParam, LPARAM lParam )
1759 SCROLLINFO info;
1760 INT page;
1762 if (descr->style & LBS_MULTICOLUMN)
1764 switch(LOWORD(wParam))
1766 case SB_LINELEFT:
1767 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1768 TRUE );
1769 break;
1770 case SB_LINERIGHT:
1771 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1772 TRUE );
1773 break;
1774 case SB_PAGELEFT:
1775 page = descr->width / descr->column_width;
1776 if (page < 1) page = 1;
1777 LISTBOX_SetTopItem( wnd, descr,
1778 descr->top_item - page * descr->page_size, TRUE );
1779 break;
1780 case SB_PAGERIGHT:
1781 page = descr->width / descr->column_width;
1782 if (page < 1) page = 1;
1783 LISTBOX_SetTopItem( wnd, descr,
1784 descr->top_item + page * descr->page_size, TRUE );
1785 break;
1786 case SB_THUMBPOSITION:
1787 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1788 TRUE );
1789 break;
1790 case SB_THUMBTRACK:
1791 info.cbSize = sizeof(info);
1792 info.fMask = SIF_TRACKPOS;
1793 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1794 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1795 TRUE );
1796 break;
1797 case SB_LEFT:
1798 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1799 break;
1800 case SB_RIGHT:
1801 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1802 break;
1805 else if (descr->horz_extent)
1807 switch(LOWORD(wParam))
1809 case SB_LINELEFT:
1810 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1811 break;
1812 case SB_LINERIGHT:
1813 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1814 break;
1815 case SB_PAGELEFT:
1816 LISTBOX_SetHorizontalPos( wnd, descr,
1817 descr->horz_pos - descr->width );
1818 break;
1819 case SB_PAGERIGHT:
1820 LISTBOX_SetHorizontalPos( wnd, descr,
1821 descr->horz_pos + descr->width );
1822 break;
1823 case SB_THUMBPOSITION:
1824 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1825 break;
1826 case SB_THUMBTRACK:
1827 info.cbSize = sizeof(info);
1828 info.fMask = SIF_TRACKPOS;
1829 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1830 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1831 break;
1832 case SB_LEFT:
1833 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1834 break;
1835 case SB_RIGHT:
1836 LISTBOX_SetHorizontalPos( wnd, descr,
1837 descr->horz_extent - descr->width );
1838 break;
1841 return 0;
1844 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1846 short gcWheelDelta = 0;
1847 UINT pulScrollLines = 3;
1849 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1851 gcWheelDelta -= (short) HIWORD(wParam);
1853 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1855 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1856 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1857 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1859 return 0;
1862 /***********************************************************************
1863 * LISTBOX_HandleLButtonDown
1865 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1866 WPARAM wParam, INT x, INT y )
1868 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1869 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1870 wnd->hwndSelf, x, y, index );
1871 if (!descr->caret_on && (descr->in_focus)) return 0;
1873 if (!descr->in_focus)
1875 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1876 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1877 : descr->lphc->self->hwndSelf );
1880 if (index != -1)
1882 if (descr->style & LBS_EXTENDEDSEL)
1884 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1885 if (wParam & MK_CONTROL)
1887 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1888 LISTBOX_SetSelection( wnd, descr, index,
1889 !descr->items[index].selected,
1890 (descr->style & LBS_NOTIFY) != 0);
1892 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1894 else
1896 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1897 LISTBOX_SetSelection( wnd, descr, index,
1898 (!(descr->style & LBS_MULTIPLESEL) ||
1899 !descr->items[index].selected),
1900 (descr->style & LBS_NOTIFY) != 0 );
1904 descr->captured = TRUE;
1905 SetCapture( wnd->hwndSelf );
1906 if (index != -1 && !descr->lphc)
1908 if (descr->style & LBS_NOTIFY )
1909 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1910 MAKELPARAM( x, y ) );
1911 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1913 POINT pt;
1915 pt.x = x;
1916 pt.y = y;
1918 if (DragDetect( wnd->hwndSelf, pt ))
1919 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1922 return 0;
1926 /*************************************************************************
1927 * LISTBOX_HandleLButtonDownCombo [Internal]
1929 * Process LButtonDown message for the ComboListBox
1931 * PARAMS
1932 * pWnd [I] The windows internal structure
1933 * pDescr [I] The ListBox internal structure
1934 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1935 * x [I] X Mouse Coordinate
1936 * y [I] Y Mouse Coordinate
1938 * RETURNS
1939 * 0 since we are processing the WM_LBUTTONDOWN Message
1941 * NOTES
1942 * This function is only to be used when a ListBox is a ComboListBox
1945 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1946 UINT msg, WPARAM wParam, INT x, INT y)
1948 RECT clientRect, screenRect;
1949 POINT mousePos;
1951 mousePos.x = x;
1952 mousePos.y = y;
1954 GetClientRect(pWnd->hwndSelf, &clientRect);
1956 if(PtInRect(&clientRect, mousePos))
1958 /* MousePos is in client, resume normal processing */
1959 if (msg == WM_LBUTTONDOWN)
1961 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
1962 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1964 else if (pDescr->style & LBS_NOTIFY)
1965 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1966 return 0;
1968 else
1970 POINT screenMousePos;
1971 HWND hWndOldCapture;
1973 /* Check the Non-Client Area */
1974 screenMousePos = mousePos;
1975 hWndOldCapture = GetCapture();
1976 ReleaseCapture();
1977 GetWindowRect(pWnd->hwndSelf, &screenRect);
1978 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1980 if(!PtInRect(&screenRect, screenMousePos))
1982 LISTBOX_SetSelection( pWnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
1983 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
1984 return 0;
1986 else
1988 /* Check to see the NC is a scrollbar */
1989 INT nHitTestType=0;
1990 /* Check Vertical scroll bar */
1991 if (pWnd->dwStyle & WS_VSCROLL)
1993 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1994 if (PtInRect( &clientRect, mousePos ))
1996 nHitTestType = HTVSCROLL;
1999 /* Check horizontal scroll bar */
2000 if (pWnd->dwStyle & WS_HSCROLL)
2002 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2003 if (PtInRect( &clientRect, mousePos ))
2005 nHitTestType = HTHSCROLL;
2008 /* Windows sends this message when a scrollbar is clicked
2011 if(nHitTestType != 0)
2013 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
2014 MAKELONG(screenMousePos.x, screenMousePos.y));
2016 /* Resume the Capture after scrolling is complete
2018 if(hWndOldCapture != 0)
2020 SetCapture(hWndOldCapture);
2024 return 0;
2027 /***********************************************************************
2028 * LISTBOX_HandleLButtonUp
2030 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
2032 if (LISTBOX_Timer != LB_TIMER_NONE)
2033 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2034 LISTBOX_Timer = LB_TIMER_NONE;
2035 if (descr->captured)
2037 descr->captured = FALSE;
2038 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
2039 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2040 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2042 return 0;
2046 /***********************************************************************
2047 * LISTBOX_HandleTimer
2049 * Handle scrolling upon a timer event.
2050 * Return TRUE if scrolling should continue.
2052 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
2053 INT index, TIMER_DIRECTION dir )
2055 switch(dir)
2057 case LB_TIMER_UP:
2058 if (descr->top_item) index = descr->top_item - 1;
2059 else index = 0;
2060 break;
2061 case LB_TIMER_LEFT:
2062 if (descr->top_item) index -= descr->page_size;
2063 break;
2064 case LB_TIMER_DOWN:
2065 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
2066 if (index == descr->focus_item) index++;
2067 if (index >= descr->nb_items) index = descr->nb_items - 1;
2068 break;
2069 case LB_TIMER_RIGHT:
2070 if (index + descr->page_size < descr->nb_items)
2071 index += descr->page_size;
2072 break;
2073 case LB_TIMER_NONE:
2074 break;
2076 if (index == descr->focus_item) return FALSE;
2077 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2078 return TRUE;
2082 /***********************************************************************
2083 * LISTBOX_HandleSystemTimer
2085 * WM_SYSTIMER handler.
2087 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2089 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2091 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2092 LISTBOX_Timer = LB_TIMER_NONE;
2094 return 0;
2098 /***********************************************************************
2099 * LISTBOX_HandleMouseMove
2101 * WM_MOUSEMOVE handler.
2103 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2104 INT x, INT y )
2106 INT index;
2107 TIMER_DIRECTION dir = LB_TIMER_NONE;
2109 if (!descr->captured) return;
2111 if (descr->style & LBS_MULTICOLUMN)
2113 if (y < 0) y = 0;
2114 else if (y >= descr->item_height * descr->page_size)
2115 y = descr->item_height * descr->page_size - 1;
2117 if (x < 0)
2119 dir = LB_TIMER_LEFT;
2120 x = 0;
2122 else if (x >= descr->width)
2124 dir = LB_TIMER_RIGHT;
2125 x = descr->width - 1;
2128 else
2130 if (y < 0) dir = LB_TIMER_UP; /* above */
2131 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2134 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2135 if (index == -1) index = descr->focus_item;
2136 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2138 /* Start/stop the system timer */
2140 if (dir != LB_TIMER_NONE)
2141 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2142 else if (LISTBOX_Timer != LB_TIMER_NONE)
2143 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2144 LISTBOX_Timer = dir;
2148 /***********************************************************************
2149 * LISTBOX_HandleKeyDown
2151 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2153 INT caret = -1;
2154 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2155 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2156 bForceSelection = FALSE; /* only for single select list */
2158 if (descr->style & LBS_WANTKEYBOARDINPUT)
2160 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2161 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2162 wnd->hwndSelf );
2163 if (caret == -2) return 0;
2165 if (caret == -1) switch(wParam)
2167 case VK_LEFT:
2168 if (descr->style & LBS_MULTICOLUMN)
2170 bForceSelection = FALSE;
2171 if (descr->focus_item >= descr->page_size)
2172 caret = descr->focus_item - descr->page_size;
2173 break;
2175 /* fall through */
2176 case VK_UP:
2177 caret = descr->focus_item - 1;
2178 if (caret < 0) caret = 0;
2179 break;
2180 case VK_RIGHT:
2181 if (descr->style & LBS_MULTICOLUMN)
2183 bForceSelection = FALSE;
2184 if (descr->focus_item + descr->page_size < descr->nb_items)
2185 caret = descr->focus_item + descr->page_size;
2186 break;
2188 /* fall through */
2189 case VK_DOWN:
2190 caret = descr->focus_item + 1;
2191 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2192 break;
2194 case VK_PRIOR:
2195 if (descr->style & LBS_MULTICOLUMN)
2197 INT page = descr->width / descr->column_width;
2198 if (page < 1) page = 1;
2199 caret = descr->focus_item - (page * descr->page_size) + 1;
2201 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2202 if (caret < 0) caret = 0;
2203 break;
2204 case VK_NEXT:
2205 if (descr->style & LBS_MULTICOLUMN)
2207 INT page = descr->width / descr->column_width;
2208 if (page < 1) page = 1;
2209 caret = descr->focus_item + (page * descr->page_size) - 1;
2211 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2212 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2213 break;
2214 case VK_HOME:
2215 caret = 0;
2216 break;
2217 case VK_END:
2218 caret = descr->nb_items - 1;
2219 break;
2220 case VK_SPACE:
2221 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2222 else if (descr->style & LBS_MULTIPLESEL)
2224 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2225 !descr->items[descr->focus_item].selected,
2226 (descr->style & LBS_NOTIFY) != 0 );
2228 break;
2229 default:
2230 bForceSelection = FALSE;
2232 if (bForceSelection) /* focused item is used instead of key */
2233 caret = descr->focus_item;
2234 if (caret >= 0)
2236 if ((descr->style & LBS_EXTENDEDSEL) &&
2237 !(GetKeyState( VK_SHIFT ) & 0x8000))
2238 descr->anchor_item = caret;
2239 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2240 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2241 if (descr->style & LBS_NOTIFY)
2243 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2245 /* make sure that combo parent doesn't hide us */
2246 descr->lphc->wState |= CBF_NOROLLUP;
2248 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2251 return 0;
2255 /***********************************************************************
2256 * LISTBOX_HandleChar
2258 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2259 WPARAM wParam )
2261 INT caret = -1;
2262 char str[2];
2264 str[0] = wParam & 0xff;
2265 str[1] = '\0';
2267 if (descr->style & LBS_WANTKEYBOARDINPUT)
2269 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2270 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2271 wnd->hwndSelf );
2272 if (caret == -2) return 0;
2274 if (caret == -1)
2275 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2276 if (caret != -1)
2278 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2279 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2280 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2281 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2282 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2284 return 0;
2288 /***********************************************************************
2289 * LISTBOX_Create
2291 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2293 LB_DESCR *descr;
2294 MEASUREITEMSTRUCT mis;
2295 RECT rect;
2297 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2298 return FALSE;
2299 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2301 HeapFree( GetProcessHeap(), 0, descr );
2302 return FALSE;
2304 GetClientRect( wnd->hwndSelf, &rect );
2305 descr->owner = GetParent( wnd->hwndSelf );
2306 descr->style = wnd->dwStyle;
2307 descr->width = rect.right - rect.left;
2308 descr->height = rect.bottom - rect.top;
2309 descr->items = NULL;
2310 descr->nb_items = 0;
2311 descr->top_item = 0;
2312 descr->selected_item = -1;
2313 descr->focus_item = 0;
2314 descr->anchor_item = -1;
2315 descr->item_height = 1;
2316 descr->page_size = 1;
2317 descr->column_width = 150;
2318 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2319 descr->horz_pos = 0;
2320 descr->nb_tabs = 0;
2321 descr->tabs = NULL;
2322 descr->caret_on = lphc ? FALSE : TRUE;
2323 descr->in_focus = FALSE;
2324 descr->captured = FALSE;
2325 descr->font = 0;
2326 descr->locale = 0; /* FIXME */
2327 descr->lphc = lphc;
2329 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2330 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2332 /* Win95 document "List Box Differences" from MSDN:
2333 If a list box in a version 3.x application has either the
2334 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2335 horizontal and vertical scroll bars.
2337 descr->style |= WS_VSCROLL | WS_HSCROLL;
2340 if( lphc )
2342 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2343 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2344 descr->owner = lphc->self->hwndSelf;
2347 *(LB_DESCR **)wnd->wExtra = descr;
2349 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2351 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2352 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2353 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2354 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2356 if (descr->style & LBS_OWNERDRAWFIXED)
2358 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2360 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2361 descr->item_height = lphc->fixedOwnerDrawHeight;
2363 else
2365 mis.CtlType = ODT_LISTBOX;
2366 mis.CtlID = wnd->wIDmenu;
2367 mis.itemID = -1;
2368 mis.itemWidth = 0;
2369 mis.itemData = 0;
2370 mis.itemHeight = descr->item_height;
2371 SendMessageA( descr->owner, WM_MEASUREITEM, wnd->wIDmenu, (LPARAM)&mis );
2372 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2376 return TRUE;
2380 /***********************************************************************
2381 * LISTBOX_Destroy
2383 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2385 LISTBOX_ResetContent( wnd, descr );
2386 HeapDestroy( descr->heap );
2387 HeapFree( GetProcessHeap(), 0, descr );
2388 wnd->wExtra[0] = 0;
2389 return TRUE;
2393 /***********************************************************************
2394 * ListBoxWndProc
2396 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2397 WPARAM wParam, LPARAM lParam )
2399 LRESULT ret;
2400 LB_DESCR *descr;
2401 HWND hwnd = wnd->hwndSelf;
2403 if (!wnd) return 0;
2404 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2406 switch (msg)
2408 case WM_CREATE:
2410 if (!LISTBOX_Create( wnd, NULL ))
2411 return -1;
2412 TRACE("creating wnd=%04x descr=%p\n",
2413 hwnd, *(LB_DESCR **)wnd->wExtra );
2414 return 0;
2416 case WM_NCCREATE:
2419 * When a listbox is not in a combobox and the look
2420 * is win95, the WS_BORDER style is replaced with
2421 * the WS_EX_CLIENTEDGE style.
2423 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2424 (wnd->dwStyle & WS_BORDER) )
2426 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2427 wnd->dwStyle &= ~ WS_BORDER;
2432 /* Ignore all other messages before we get a WM_CREATE */
2433 return DefWindowProcA( hwnd, msg, wParam, lParam );
2436 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2437 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2438 switch(msg)
2440 case LB_RESETCONTENT16:
2441 case LB_RESETCONTENT:
2442 LISTBOX_ResetContent( wnd, descr );
2443 return 0;
2445 case LB_ADDSTRING16:
2446 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2447 /* fall through */
2448 case LB_ADDSTRING:
2449 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2450 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2452 case LB_INSERTSTRING16:
2453 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2454 wParam = (INT)(INT16)wParam;
2455 /* fall through */
2456 case LB_INSERTSTRING:
2457 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2459 case LB_ADDFILE16:
2460 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2461 /* fall through */
2462 case LB_ADDFILE:
2463 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2464 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2466 case LB_DELETESTRING16:
2467 case LB_DELETESTRING:
2468 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2469 return descr->nb_items;
2470 else
2471 return LB_ERR;
2473 case LB_GETITEMDATA16:
2474 case LB_GETITEMDATA:
2475 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2476 return LB_ERR;
2477 return descr->items[wParam].data;
2479 case LB_SETITEMDATA16:
2480 case LB_SETITEMDATA:
2481 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2482 return LB_ERR;
2483 descr->items[wParam].data = (DWORD)lParam;
2484 return LB_OKAY;
2486 case LB_GETCOUNT16:
2487 case LB_GETCOUNT:
2488 return descr->nb_items;
2490 case LB_GETTEXT16:
2491 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2492 /* fall through */
2493 case LB_GETTEXT:
2494 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2496 case LB_GETTEXTLEN16:
2497 /* fall through */
2498 case LB_GETTEXTLEN:
2499 if (wParam >= descr->nb_items)
2500 return LB_ERR;
2501 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2502 : sizeof(DWORD));
2504 case LB_GETCURSEL16:
2505 case LB_GETCURSEL:
2506 if (descr->nb_items==0)
2507 return LB_ERR;
2508 if (!IS_MULTISELECT(descr))
2509 return descr->selected_item;
2510 /* else */
2511 if (descr->selected_item!=-1)
2512 return descr->selected_item;
2513 /* else */
2514 return descr->focus_item;
2515 /* otherwise, if the user tries to move the selection with the */
2516 /* arrow keys, we will give the application something to choke on */
2517 case LB_GETTOPINDEX16:
2518 case LB_GETTOPINDEX:
2519 return descr->top_item;
2521 case LB_GETITEMHEIGHT16:
2522 case LB_GETITEMHEIGHT:
2523 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2525 case LB_SETITEMHEIGHT16:
2526 lParam = LOWORD(lParam);
2527 /* fall through */
2528 case LB_SETITEMHEIGHT:
2529 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2531 case LB_ITEMFROMPOINT:
2533 POINT pt;
2534 RECT rect;
2536 pt.x = LOWORD(lParam);
2537 pt.y = HIWORD(lParam);
2538 rect.left = 0;
2539 rect.top = 0;
2540 rect.right = descr->width;
2541 rect.bottom = descr->height;
2543 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2544 !PtInRect( &rect, pt ) );
2547 case LB_SETCARETINDEX16:
2548 case LB_SETCARETINDEX:
2549 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2550 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2551 return LB_ERR;
2552 else if (ISWIN31)
2553 return wParam;
2554 else
2555 return LB_OKAY;
2557 case LB_GETCARETINDEX16:
2558 case LB_GETCARETINDEX:
2559 return descr->focus_item;
2561 case LB_SETTOPINDEX16:
2562 case LB_SETTOPINDEX:
2563 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2565 case LB_SETCOLUMNWIDTH16:
2566 case LB_SETCOLUMNWIDTH:
2567 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2569 case LB_GETITEMRECT16:
2571 RECT rect;
2572 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2573 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2575 return ret;
2577 case LB_GETITEMRECT:
2578 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2580 case LB_FINDSTRING16:
2581 wParam = (INT)(INT16)wParam;
2582 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2583 /* fall through */
2584 case LB_FINDSTRING:
2585 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2587 case LB_FINDSTRINGEXACT16:
2588 wParam = (INT)(INT16)wParam;
2589 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2590 /* fall through */
2591 case LB_FINDSTRINGEXACT:
2592 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2594 case LB_SELECTSTRING16:
2595 wParam = (INT)(INT16)wParam;
2596 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2597 /* fall through */
2598 case LB_SELECTSTRING:
2600 INT index = LISTBOX_FindString( wnd, descr, wParam,
2601 (LPCSTR)lParam, FALSE );
2602 if (index == LB_ERR)
2603 return LB_ERR;
2604 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2605 return index;
2608 case LB_GETSEL16:
2609 wParam = (INT)(INT16)wParam;
2610 /* fall through */
2611 case LB_GETSEL:
2612 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2613 return LB_ERR;
2614 return descr->items[wParam].selected;
2616 case LB_SETSEL16:
2617 lParam = (INT)(INT16)lParam;
2618 /* fall through */
2619 case LB_SETSEL:
2620 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2622 case LB_SETCURSEL16:
2623 wParam = (INT)(INT16)wParam;
2624 /* fall through */
2625 case LB_SETCURSEL:
2626 if (IS_MULTISELECT(descr)) return LB_ERR;
2627 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2628 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2630 case LB_GETSELCOUNT16:
2631 case LB_GETSELCOUNT:
2632 return LISTBOX_GetSelCount( wnd, descr );
2634 case LB_GETSELITEMS16:
2635 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2636 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2638 case LB_GETSELITEMS:
2639 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2641 case LB_SELITEMRANGE16:
2642 case LB_SELITEMRANGE:
2643 if (LOWORD(lParam) <= HIWORD(lParam))
2644 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2645 HIWORD(lParam), wParam );
2646 else
2647 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2648 LOWORD(lParam), wParam );
2650 case LB_SELITEMRANGEEX16:
2651 case LB_SELITEMRANGEEX:
2652 if ((INT)lParam >= (INT)wParam)
2653 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2654 else
2655 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2657 case LB_GETHORIZONTALEXTENT16:
2658 case LB_GETHORIZONTALEXTENT:
2659 return descr->horz_extent;
2661 case LB_SETHORIZONTALEXTENT16:
2662 case LB_SETHORIZONTALEXTENT:
2663 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2665 case LB_GETANCHORINDEX16:
2666 case LB_GETANCHORINDEX:
2667 return descr->anchor_item;
2669 case LB_SETANCHORINDEX16:
2670 wParam = (INT)(INT16)wParam;
2671 /* fall through */
2672 case LB_SETANCHORINDEX:
2673 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2674 return LB_ERR;
2675 descr->anchor_item = (INT)wParam;
2676 return LB_OKAY;
2678 case LB_DIR16:
2679 return LISTBOX_Directory( wnd, descr, wParam,
2680 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2682 case LB_DIR:
2683 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2685 case LB_GETLOCALE:
2686 return descr->locale;
2688 case LB_SETLOCALE:
2689 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2690 return LB_OKAY;
2692 case LB_INITSTORAGE:
2693 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2695 case LB_SETCOUNT:
2696 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2698 case LB_SETTABSTOPS16:
2699 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2700 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2702 case LB_SETTABSTOPS:
2703 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2705 case LB_CARETON16:
2706 case LB_CARETON:
2707 if (descr->caret_on)
2708 return LB_OKAY;
2709 descr->caret_on = TRUE;
2710 if ((descr->focus_item != -1) && (descr->in_focus))
2711 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2712 return LB_OKAY;
2714 case LB_CARETOFF16:
2715 case LB_CARETOFF:
2716 if (!descr->caret_on)
2717 return LB_OKAY;
2718 descr->caret_on = FALSE;
2719 if ((descr->focus_item != -1) && (descr->in_focus))
2720 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2721 return LB_OKAY;
2723 case WM_DESTROY:
2724 return LISTBOX_Destroy( wnd, descr );
2726 case WM_ENABLE:
2727 InvalidateRect( hwnd, NULL, TRUE );
2728 return 0;
2730 case WM_SETREDRAW:
2731 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2732 return 0;
2734 case WM_GETDLGCODE:
2735 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2737 case WM_PAINT:
2739 PAINTSTRUCT ps;
2740 HDC hdc = ( wParam ) ? ((HDC)wParam)
2741 : BeginPaint( hwnd, &ps );
2742 ret = LISTBOX_Paint( wnd, descr, hdc );
2743 if( !wParam ) EndPaint( hwnd, &ps );
2745 return ret;
2746 case WM_SIZE:
2747 LISTBOX_UpdateSize( wnd, descr );
2748 return 0;
2749 case WM_GETFONT:
2750 return descr->font;
2751 case WM_SETFONT:
2752 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2753 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2754 return 0;
2755 case WM_SETFOCUS:
2756 descr->in_focus = TRUE;
2757 descr->caret_on = TRUE;
2758 if (descr->focus_item != -1)
2759 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2760 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2761 return 0;
2762 case WM_KILLFOCUS:
2763 descr->in_focus = FALSE;
2764 if ((descr->focus_item != -1) && descr->caret_on)
2765 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2766 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2767 return 0;
2768 case WM_HSCROLL:
2769 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2770 case WM_VSCROLL:
2771 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2772 case WM_MOUSEACTIVATE:
2773 return MA_NOACTIVATE;
2774 case WM_MOUSEWHEEL:
2775 if (wParam & (MK_SHIFT | MK_CONTROL))
2776 return DefWindowProcA( hwnd, msg, wParam, lParam );
2777 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2778 case WM_LBUTTONDOWN:
2779 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2780 (INT16)LOWORD(lParam),
2781 (INT16)HIWORD(lParam) );
2782 case WM_LBUTTONDBLCLK:
2783 if (descr->style & LBS_NOTIFY)
2784 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2785 return 0;
2786 case WM_MOUSEMOVE:
2787 if (GetCapture() == hwnd)
2788 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2789 (INT16)HIWORD(lParam) );
2790 return 0;
2791 case WM_LBUTTONUP:
2792 return LISTBOX_HandleLButtonUp( wnd, descr );
2793 case WM_KEYDOWN:
2794 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2795 case WM_CHAR:
2796 return LISTBOX_HandleChar( wnd, descr, wParam );
2797 case WM_SYSTIMER:
2798 return LISTBOX_HandleSystemTimer( wnd, descr );
2799 case WM_ERASEBKGND:
2800 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2802 RECT rect;
2803 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2804 wParam, (LPARAM)wnd->hwndSelf );
2805 GetClientRect(hwnd, &rect);
2806 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2808 return 1;
2809 case WM_DROPFILES:
2810 if( !descr->lphc )
2811 return SendMessageA( descr->owner, msg, wParam, lParam );
2812 break;
2814 case WM_DROPOBJECT:
2815 case WM_QUERYDROPOBJECT:
2816 case WM_DRAGSELECT:
2817 case WM_DRAGMOVE:
2818 if( !descr->lphc )
2820 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2821 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2822 dragInfo->pt.y );
2823 return SendMessageA( descr->owner, msg, wParam, lParam );
2825 break;
2827 default:
2828 if ((msg >= WM_USER) && (msg < 0xc000))
2829 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2830 hwnd, msg, wParam, lParam );
2831 return DefWindowProcA( hwnd, msg, wParam, lParam );
2833 return 0;
2836 /***********************************************************************
2837 * ListBoxWndProc
2839 * This is just a wrapper for the real wndproc, it only does window locking
2840 * and unlocking.
2842 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2843 WPARAM wParam, LPARAM lParam )
2845 WND* wndPtr = WIN_FindWndPtr( hwnd );
2846 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2848 WIN_ReleaseWndPtr(wndPtr);
2849 return res;
2852 /***********************************************************************
2853 * COMBO_Directory
2855 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2857 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2859 if( wnd )
2861 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2862 if( descr )
2864 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2866 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2867 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2868 WIN_ReleaseWndPtr(wnd);
2869 return lRet;
2871 WIN_ReleaseWndPtr(wnd);
2873 return CB_ERR;
2876 /***********************************************************************
2877 * ComboLBWndProc_locked
2879 * The real combo listbox wndproc, but called with locked WND struct.
2881 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2882 WPARAM wParam, LPARAM lParam )
2884 LRESULT lRet = 0;
2885 HWND hwnd = wnd->hwndSelf;
2887 if (wnd)
2889 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2891 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2892 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2894 if( descr || msg == WM_CREATE )
2896 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2898 switch( msg )
2900 case WM_CREATE:
2901 #define lpcs ((LPCREATESTRUCTA)lParam)
2902 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2903 (UINT)lpcs->lpCreateParams);
2905 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2906 #undef lpcs
2907 return LISTBOX_Create( wnd, lphc );
2908 case WM_MOUSEMOVE:
2909 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2910 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2912 POINT mousePos;
2913 BOOL captured;
2914 RECT clientRect;
2916 mousePos.x = (INT16)LOWORD(lParam);
2917 mousePos.y = (INT16)HIWORD(lParam);
2920 * If we are in a dropdown combobox, we simulate that
2921 * the mouse is captured to show the tracking of the item.
2923 GetClientRect(hwnd, &clientRect);
2925 if (PtInRect( &clientRect, mousePos ))
2927 captured = descr->captured;
2928 descr->captured = TRUE;
2930 LISTBOX_HandleMouseMove( wnd, descr,
2931 mousePos.x, mousePos.y);
2933 descr->captured = captured;
2936 else
2938 LISTBOX_HandleMouseMove( wnd, descr,
2939 mousePos.x, mousePos.y);
2942 return 0;
2945 else
2948 * If we are in Win3.1 look, go with the default behavior.
2950 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2952 case WM_LBUTTONUP:
2953 if (TWEAK_WineLook > WIN31_LOOK)
2955 POINT mousePos;
2956 RECT clientRect;
2959 * If the mouse button "up" is not in the listbox,
2960 * we make sure there is no selection by re-selecting the
2961 * item that was selected when the listbox was made visible.
2963 mousePos.x = (INT16)LOWORD(lParam);
2964 mousePos.y = (INT16)HIWORD(lParam);
2966 GetClientRect(hwnd, &clientRect);
2969 * When the user clicks outside the combobox and the focus
2970 * is lost, the owning combobox will send a fake buttonup with
2971 * 0xFFFFFFF as the mouse location, we must also revert the
2972 * selection to the original selection.
2974 if ( (lParam == 0xFFFFFFFF) ||
2975 (!PtInRect( &clientRect, mousePos )) )
2977 LISTBOX_MoveCaret( wnd,
2978 descr,
2979 lphc->droppedIndex,
2980 FALSE );
2983 return LISTBOX_HandleLButtonUp( wnd, descr );
2984 case WM_LBUTTONDBLCLK:
2985 case WM_LBUTTONDOWN:
2986 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
2987 (INT16)LOWORD(lParam),
2988 (INT16)HIWORD(lParam) );
2989 case WM_MOUSEACTIVATE:
2990 return MA_NOACTIVATE;
2991 case WM_NCACTIVATE:
2992 return FALSE;
2993 case WM_KEYDOWN:
2994 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2996 /* for some reason(?) Windows makes it possible to
2997 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2999 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3000 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3001 && (wParam == VK_DOWN || wParam == VK_UP)) )
3003 COMBO_FlipListbox( lphc, FALSE, FALSE );
3004 return 0;
3007 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
3009 case LB_SETCURSEL16:
3010 case LB_SETCURSEL:
3011 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
3012 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3013 return lRet;
3014 case WM_NCDESTROY:
3015 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3016 lphc->hWndLBox = 0;
3017 /* fall through */
3019 default:
3020 return ListBoxWndProc( hwnd, msg, wParam, lParam );
3023 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
3025 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3027 return lRet;
3030 /***********************************************************************
3031 * ComboLBWndProc
3033 * NOTE: in Windows, winproc address of the ComboLBox is the same
3034 * as that of the Listbox.
3036 * This is just a wrapper for the real wndproc, it only does window locking
3037 * and unlocking.
3039 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
3040 WPARAM wParam, LPARAM lParam )
3042 WND *wnd = WIN_FindWndPtr( hwnd );
3043 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
3045 WIN_ReleaseWndPtr(wnd);
3046 return res;