Merged display.dll into USER.
[wine.git] / controls / listbox.c
bloba836783dd9a7268037744eaa5f4ef3ccfb72e5b0
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include <stdlib.h>
9 #include <stdio.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "winuser.h"
15 #include "winerror.h"
16 #include "drive.h"
17 #include "heap.h"
18 #include "spy.h"
19 #include "selectors.h"
20 #include "win.h"
21 #include "combo.h"
22 #include "debugtools.h"
23 #include "tweak.h"
25 DEFAULT_DEBUG_CHANNEL(listbox);
26 DECLARE_DEBUG_CHANNEL(combo);
28 /* Unimplemented yet:
29 * - LBS_NOSEL
30 * - LBS_USETABSTOPS
31 * - Unicode
32 * - Locale handling
35 /* Items array granularity */
36 #define LB_ARRAY_GRANULARITY 16
38 /* Scrolling timeout in ms */
39 #define LB_SCROLL_TIMEOUT 50
41 /* Listbox system timer id */
42 #define LB_TIMER_ID 2
44 /* Item structure */
45 typedef struct
47 LPSTR str; /* Item text */
48 BOOL selected; /* Is item selected? */
49 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
50 DWORD data; /* User data */
51 } LB_ITEMDATA;
53 /* Listbox structure */
54 typedef struct
56 HANDLE heap; /* Heap for this listbox */
57 HWND owner; /* Owner window to send notifications to */
58 UINT style; /* Window style */
59 INT width; /* Window width */
60 INT height; /* Window height */
61 LB_ITEMDATA *items; /* Array of items */
62 INT nb_items; /* Number of items */
63 INT top_item; /* Top visible item */
64 INT selected_item; /* Selected item */
65 INT focus_item; /* Item that has the focus */
66 INT anchor_item; /* Anchor item for extended selection */
67 INT item_height; /* Default item height */
68 INT page_size; /* Items per listbox page */
69 INT column_width; /* Column width for multi-column listboxes */
70 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
71 INT horz_pos; /* Horizontal position */
72 INT nb_tabs; /* Number of tabs in array */
73 INT *tabs; /* Array of tabs */
74 BOOL caret_on; /* Is caret on? */
75 BOOL captured; /* Is mouse captured? */
76 BOOL in_focus;
77 HFONT font; /* Current font */
78 LCID locale; /* Current locale for string comparisons */
79 LPHEADCOMBO lphc; /* ComboLBox */
80 } LB_DESCR;
83 #define IS_OWNERDRAW(descr) \
84 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
86 #define HAS_STRINGS(descr) \
87 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
90 #define IS_MULTISELECT(descr) \
91 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
93 #define SEND_NOTIFICATION(wnd,descr,code) \
94 (SendMessageA( (descr)->owner, WM_COMMAND, \
95 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
97 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
99 /* Current timer status */
100 typedef enum
102 LB_TIMER_NONE,
103 LB_TIMER_UP,
104 LB_TIMER_LEFT,
105 LB_TIMER_DOWN,
106 LB_TIMER_RIGHT
107 } TIMER_DIRECTION;
109 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
112 /***********************************************************************
113 * LISTBOX_Dump
115 void LISTBOX_Dump( WND *wnd )
117 INT i;
118 LB_ITEMDATA *item;
119 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
121 TRACE( "Listbox:\n" );
122 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
123 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
124 descr->top_item );
125 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
127 TRACE( "%4d: %-40s %d %08lx %3d\n",
128 i, item->str, item->selected, item->data, item->height );
133 /***********************************************************************
134 * LISTBOX_GetCurrentPageSize
136 * Return the current page size
138 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
140 INT i, height;
141 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
142 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
144 if ((height += descr->items[i].height) > descr->height) break;
146 if (i == descr->top_item) return 1;
147 else return i - descr->top_item;
151 /***********************************************************************
152 * LISTBOX_GetMaxTopIndex
154 * Return the maximum possible index for the top of the listbox.
156 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
158 INT max, page;
160 if (descr->style & LBS_OWNERDRAWVARIABLE)
162 page = descr->height;
163 for (max = descr->nb_items - 1; max >= 0; max--)
164 if ((page -= descr->items[max].height) < 0) break;
165 if (max < descr->nb_items - 1) max++;
167 else if (descr->style & LBS_MULTICOLUMN)
169 if ((page = descr->width / descr->column_width) < 1) page = 1;
170 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
171 max = (max - page) * descr->page_size;
173 else
175 max = descr->nb_items - descr->page_size;
177 if (max < 0) max = 0;
178 return max;
182 /***********************************************************************
183 * LISTBOX_UpdateScroll
185 * Update the scrollbars. Should be called whenever the content
186 * of the listbox changes.
188 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
190 SCROLLINFO info;
192 /* Check the listbox scroll bar flags individually before we call
193 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
194 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
195 scroll bar when we do not need one.
196 if (!(descr->style & WS_VSCROLL)) return;
199 /* It is important that we check descr->style, and not wnd->dwStyle,
200 for WS_VSCROLL, as the former is exactly the one passed in
201 argument to CreateWindow.
202 In Windows (and from now on in Wine :) a listbox created
203 with such a style (no WS_SCROLL) does not update
204 the scrollbar with listbox-related data, thus letting
205 the programmer use it for his/her own purposes. */
207 if (descr->style & LBS_NOREDRAW) return;
208 info.cbSize = sizeof(info);
210 if (descr->style & LBS_MULTICOLUMN)
212 info.nMin = 0;
213 info.nMax = (descr->nb_items - 1) / descr->page_size;
214 info.nPos = descr->top_item / descr->page_size;
215 info.nPage = descr->width / descr->column_width;
216 if (info.nPage < 1) info.nPage = 1;
217 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
218 if (descr->style & LBS_DISABLENOSCROLL)
219 info.fMask |= SIF_DISABLENOSCROLL;
220 if (descr->style & WS_HSCROLL)
221 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
222 info.nMax = 0;
223 info.fMask = SIF_RANGE;
224 if (descr->style & WS_VSCROLL)
225 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
227 else
229 info.nMin = 0;
230 info.nMax = descr->nb_items - 1;
231 info.nPos = descr->top_item;
232 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
233 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
234 if (descr->style & LBS_DISABLENOSCROLL)
235 info.fMask |= SIF_DISABLENOSCROLL;
236 if (descr->style & WS_VSCROLL)
237 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
239 if (descr->horz_extent)
241 info.nMin = 0;
242 info.nMax = descr->horz_extent - 1;
243 info.nPos = descr->horz_pos;
244 info.nPage = descr->width;
245 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
246 if (descr->style & LBS_DISABLENOSCROLL)
247 info.fMask |= SIF_DISABLENOSCROLL;
248 if (descr->style & WS_HSCROLL)
249 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
256 /***********************************************************************
257 * LISTBOX_SetTopItem
259 * Set the top item of the listbox, scrolling up or down if necessary.
261 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
262 BOOL scroll )
264 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
265 if (index > max) index = max;
266 if (index < 0) index = 0;
267 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
268 if (descr->top_item == index) return LB_OKAY;
269 if (descr->style & LBS_MULTICOLUMN)
271 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
272 if (scroll && (abs(diff) < descr->width))
273 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
274 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
276 else
277 scroll = FALSE;
279 else if (scroll)
281 INT diff;
282 if (descr->style & LBS_OWNERDRAWVARIABLE)
284 INT i;
285 diff = 0;
286 if (index > descr->top_item)
288 for (i = index - 1; i >= descr->top_item; i--)
289 diff -= descr->items[i].height;
291 else
293 for (i = index; i < descr->top_item; i++)
294 diff += descr->items[i].height;
297 else
298 diff = (descr->top_item - index) * descr->item_height;
300 if (abs(diff) < descr->height)
301 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
302 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
303 else
304 scroll = FALSE;
306 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
307 descr->top_item = index;
308 LISTBOX_UpdateScroll( wnd, descr );
309 return LB_OKAY;
313 /***********************************************************************
314 * LISTBOX_UpdatePage
316 * Update the page size. Should be called when the size of
317 * the client area or the item height changes.
319 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
321 INT page_size;
323 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
324 page_size = 1;
325 if (page_size == descr->page_size) return;
326 descr->page_size = page_size;
327 if (descr->style & LBS_MULTICOLUMN)
328 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
329 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
333 /***********************************************************************
334 * LISTBOX_UpdateSize
336 * Update the size of the listbox. Should be called when the size of
337 * the client area changes.
339 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
341 RECT rect;
343 GetClientRect( wnd->hwndSelf, &rect );
344 descr->width = rect.right - rect.left;
345 descr->height = rect.bottom - rect.top;
346 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
348 if ((descr->height > descr->item_height) &&
349 (descr->height % descr->item_height))
351 TRACE("[%04x]: changing height %d -> %d\n",
352 wnd->hwndSelf, descr->height,
353 descr->height - descr->height%descr->item_height );
354 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
355 wnd->rectWindow.right - wnd->rectWindow.left,
356 wnd->rectWindow.bottom - wnd->rectWindow.top -
357 (descr->height % descr->item_height),
358 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
359 return;
362 TRACE("[%04x]: new size = %d,%d\n",
363 wnd->hwndSelf, descr->width, descr->height );
364 LISTBOX_UpdatePage( wnd, descr );
365 LISTBOX_UpdateScroll( wnd, descr );
369 /***********************************************************************
370 * LISTBOX_GetItemRect
372 * Get the rectangle enclosing an item, in listbox client coordinates.
373 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
375 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
376 RECT *rect )
378 /* Index <= 0 is legal even on empty listboxes */
379 if (index && (index >= descr->nb_items)) return -1;
380 SetRect( rect, 0, 0, descr->width, descr->height );
381 if (descr->style & LBS_MULTICOLUMN)
383 INT col = (index / descr->page_size) -
384 (descr->top_item / descr->page_size);
385 rect->left += col * descr->column_width;
386 rect->right = rect->left + descr->column_width;
387 rect->top += (index % descr->page_size) * descr->item_height;
388 rect->bottom = rect->top + descr->item_height;
390 else if (descr->style & LBS_OWNERDRAWVARIABLE)
392 INT i;
393 rect->right += descr->horz_pos;
394 if ((index >= 0) && (index < descr->nb_items))
396 if (index < descr->top_item)
398 for (i = descr->top_item-1; i >= index; i--)
399 rect->top -= descr->items[i].height;
401 else
403 for (i = descr->top_item; i < index; i++)
404 rect->top += descr->items[i].height;
406 rect->bottom = rect->top + descr->items[index].height;
410 else
412 rect->top += (index - descr->top_item) * descr->item_height;
413 rect->bottom = rect->top + descr->item_height;
414 rect->right += descr->horz_pos;
417 return ((rect->left < descr->width) && (rect->right > 0) &&
418 (rect->top < descr->height) && (rect->bottom > 0));
422 /***********************************************************************
423 * LISTBOX_GetItemFromPoint
425 * Return the item nearest from point (x,y) (in client coordinates).
427 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
428 INT x, INT y )
430 INT index = descr->top_item;
432 if (!descr->nb_items) return -1; /* No items */
433 if (descr->style & LBS_OWNERDRAWVARIABLE)
435 INT pos = 0;
436 if (y >= 0)
438 while (index < descr->nb_items)
440 if ((pos += descr->items[index].height) > y) break;
441 index++;
444 else
446 while (index > 0)
448 index--;
449 if ((pos -= descr->items[index].height) <= y) break;
453 else if (descr->style & LBS_MULTICOLUMN)
455 if (y >= descr->item_height * descr->page_size) return -1;
456 if (y >= 0) index += y / descr->item_height;
457 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
458 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
460 else
462 index += (y / descr->item_height);
464 if (index < 0) return 0;
465 if (index >= descr->nb_items) return -1;
466 return index;
470 /***********************************************************************
471 * LISTBOX_PaintItem
473 * Paint an item.
475 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
476 const RECT *rect, INT index, UINT action )
478 LB_ITEMDATA *item = NULL;
479 if (index < descr->nb_items) item = &descr->items[index];
481 if (IS_OWNERDRAW(descr))
483 DRAWITEMSTRUCT dis;
484 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
486 if (!item)
488 if (action == ODA_FOCUS)
489 DrawFocusRect( hdc, rect );
490 else
491 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
492 return;
494 dis.CtlType = ODT_LISTBOX;
495 dis.CtlID = id;
496 dis.hwndItem = wnd->hwndSelf;
497 dis.itemAction = action;
498 dis.hDC = hdc;
499 dis.itemID = index;
500 dis.itemState = 0;
501 if (item && item->selected) dis.itemState |= ODS_SELECTED;
502 if ((descr->focus_item == index) &&
503 (descr->caret_on) &&
504 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
505 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
506 dis.itemData = item ? item->data : 0;
507 dis.rcItem = *rect;
508 TRACE("[%04x]: drawitem %d (%s) action=%02x "
509 "state=%02x rect=%d,%d-%d,%d\n",
510 wnd->hwndSelf, index, item ? item->str : "", action,
511 dis.itemState, rect->left, rect->top,
512 rect->right, rect->bottom );
513 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
515 else
517 COLORREF oldText = 0, oldBk = 0;
519 if (action == ODA_FOCUS)
521 DrawFocusRect( hdc, rect );
522 return;
524 if (item && item->selected)
526 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
527 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
530 TRACE("[%04x]: painting %d (%s) action=%02x "
531 "rect=%d,%d-%d,%d\n",
532 wnd->hwndSelf, index, item ? item->str : "", action,
533 rect->left, rect->top, rect->right, rect->bottom );
534 if (!item)
535 ExtTextOutA( hdc, rect->left + 1, rect->top,
536 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
537 else if (!(descr->style & LBS_USETABSTOPS))
538 ExtTextOutA( hdc, rect->left + 1, rect->top,
539 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
540 strlen(item->str), NULL );
541 else
543 /* Output empty string to paint background in the full width. */
544 ExtTextOutA( hdc, rect->left + 1, rect->top,
545 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
546 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
547 item->str, strlen(item->str),
548 descr->nb_tabs, descr->tabs, 0);
550 if (item && item->selected)
552 SetBkColor( hdc, oldBk );
553 SetTextColor( hdc, oldText );
555 if ((descr->focus_item == index) &&
556 (descr->caret_on) &&
557 (descr->in_focus)) DrawFocusRect( hdc, rect );
562 /***********************************************************************
563 * LISTBOX_SetRedraw
565 * Change the redraw flag.
567 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
569 if (on)
571 if (!(descr->style & LBS_NOREDRAW)) return;
572 descr->style &= ~LBS_NOREDRAW;
573 LISTBOX_UpdateScroll( wnd, descr );
575 else descr->style |= LBS_NOREDRAW;
579 /***********************************************************************
580 * LISTBOX_RepaintItem
582 * Repaint a single item synchronously.
584 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
585 UINT action )
587 HDC hdc;
588 RECT rect;
589 HFONT oldFont = 0;
590 HBRUSH hbrush, oldBrush = 0;
592 /* Do not repaint the item if the item is not visible */
593 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
595 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
596 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
597 if (descr->font) oldFont = SelectObject( hdc, descr->font );
598 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
599 hdc, (LPARAM)wnd->hwndSelf );
600 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
601 if (wnd->dwStyle & WS_DISABLED)
602 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
603 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
604 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
605 if (oldFont) SelectObject( hdc, oldFont );
606 if (oldBrush) SelectObject( hdc, oldBrush );
607 ReleaseDC( wnd->hwndSelf, hdc );
611 /***********************************************************************
612 * LISTBOX_InitStorage
614 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
615 DWORD bytes )
617 LB_ITEMDATA *item;
619 nb_items += LB_ARRAY_GRANULARITY - 1;
620 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
621 if (descr->items)
622 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
623 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
624 nb_items * sizeof(LB_ITEMDATA) )))
626 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
627 return LB_ERRSPACE;
629 descr->items = item;
630 return LB_OKAY;
634 /***********************************************************************
635 * LISTBOX_SetTabStops
637 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
638 LPINT tabs, BOOL short_ints )
640 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
641 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
642 if (!(descr->nb_tabs = count))
644 descr->tabs = NULL;
645 return TRUE;
647 /* FIXME: count = 1 */
648 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
649 descr->nb_tabs * sizeof(INT) )))
650 return FALSE;
651 if (short_ints)
653 INT i;
654 LPINT16 p = (LPINT16)tabs;
656 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
657 for (i = 0; i < descr->nb_tabs; i++) {
658 descr->tabs[i] = *p++<<1; /* FIXME */
659 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
661 if (TRACE_ON(listbox)) DPRINTF("\n");
663 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
664 /* FIXME: repaint the window? */
665 return TRUE;
669 /***********************************************************************
670 * LISTBOX_GetText
672 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
673 LPSTR buffer )
675 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
676 if (HAS_STRINGS(descr))
678 if (!buffer)
679 return strlen(descr->items[index].str);
680 lstrcpyA( buffer, descr->items[index].str );
681 return strlen(buffer);
682 } else {
683 if (buffer)
684 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
685 return sizeof(DWORD);
690 /***********************************************************************
691 * LISTBOX_FindStringPos
693 * Find the nearest string located before a given string in sort order.
694 * If 'exact' is TRUE, return an error if we don't get an exact match.
696 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
697 BOOL exact )
699 INT index, min, max, res = -1;
701 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
702 min = 0;
703 max = descr->nb_items;
704 while (min != max)
706 index = (min + max) / 2;
707 if (HAS_STRINGS(descr))
708 res = lstrcmpiA( descr->items[index].str, str );
709 else
711 COMPAREITEMSTRUCT cis;
712 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
714 cis.CtlType = ODT_LISTBOX;
715 cis.CtlID = id;
716 cis.hwndItem = wnd->hwndSelf;
717 cis.itemID1 = index;
718 cis.itemData1 = descr->items[index].data;
719 cis.itemID2 = -1;
720 cis.itemData2 = (DWORD)str;
721 cis.dwLocaleId = descr->locale;
722 res = SendMessageA( descr->owner, WM_COMPAREITEM,
723 id, (LPARAM)&cis );
725 if (!res) return index;
726 if (res > 0) max = index;
727 else min = index + 1;
729 return exact ? -1 : max;
733 /***********************************************************************
734 * LISTBOX_FindFileStrPos
736 * Find the nearest string located before a given string in directory
737 * sort order (i.e. first files, then directories, then drives).
739 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
741 INT min, max, res = -1;
743 if (!HAS_STRINGS(descr))
744 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
745 min = 0;
746 max = descr->nb_items;
747 while (min != max)
749 INT index = (min + max) / 2;
750 const char *p = descr->items[index].str;
751 if (*p == '[') /* drive or directory */
753 if (*str != '[') res = -1;
754 else if (p[1] == '-') /* drive */
756 if (str[1] == '-') res = str[2] - p[2];
757 else res = -1;
759 else /* directory */
761 if (str[1] == '-') res = 1;
762 else res = lstrcmpiA( str, p );
765 else /* filename */
767 if (*str == '[') res = 1;
768 else res = lstrcmpiA( str, p );
770 if (!res) return index;
771 if (res < 0) max = index;
772 else min = index + 1;
774 return max;
778 /***********************************************************************
779 * LISTBOX_FindString
781 * Find the item beginning with a given string.
783 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
784 LPCSTR str, BOOL exact )
786 INT i;
787 LB_ITEMDATA *item;
789 if (start >= descr->nb_items) start = -1;
790 item = descr->items + start + 1;
791 if (HAS_STRINGS(descr))
793 if (!str || ! str[0] ) return LB_ERR;
794 if (exact)
796 for (i = start + 1; i < descr->nb_items; i++, item++)
797 if (!lstrcmpiA( str, item->str )) return i;
798 for (i = 0, item = descr->items; i <= start; i++, item++)
799 if (!lstrcmpiA( str, item->str )) return i;
801 else
803 /* Special case for drives and directories: ignore prefix */
804 #define CHECK_DRIVE(item) \
805 if ((item)->str[0] == '[') \
807 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
808 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
809 return i; \
812 INT len = strlen(str);
813 for (i = start + 1; i < descr->nb_items; i++, item++)
815 if (!lstrncmpiA( str, item->str, len )) return i;
816 CHECK_DRIVE(item);
818 for (i = 0, item = descr->items; i <= start; i++, item++)
820 if (!lstrncmpiA( str, item->str, len )) return i;
821 CHECK_DRIVE(item);
823 #undef CHECK_DRIVE
826 else
828 if (exact && (descr->style & LBS_SORT))
829 /* If sorted, use a WM_COMPAREITEM binary search */
830 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
832 /* Otherwise use a linear search */
833 for (i = start + 1; i < descr->nb_items; i++, item++)
834 if (item->data == (DWORD)str) return i;
835 for (i = 0, item = descr->items; i <= start; i++, item++)
836 if (item->data == (DWORD)str) return i;
838 return LB_ERR;
842 /***********************************************************************
843 * LISTBOX_GetSelCount
845 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
847 INT i, count;
848 LB_ITEMDATA *item = descr->items;
850 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
851 for (i = count = 0; i < descr->nb_items; i++, item++)
852 if (item->selected) count++;
853 return count;
857 /***********************************************************************
858 * LISTBOX_GetSelItems16
860 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
861 LPINT16 array )
863 INT i, count;
864 LB_ITEMDATA *item = descr->items;
866 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
867 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
868 if (item->selected) array[count++] = (INT16)i;
869 return count;
873 /***********************************************************************
874 * LISTBOX_GetSelItems32
876 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
877 LPINT array )
879 INT i, count;
880 LB_ITEMDATA *item = descr->items;
882 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
883 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
884 if (item->selected) array[count++] = i;
885 return count;
889 /***********************************************************************
890 * LISTBOX_Paint
892 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
894 INT i, col_pos = descr->page_size - 1;
895 RECT rect;
896 HFONT oldFont = 0;
897 HBRUSH hbrush, oldBrush = 0;
899 SetRect( &rect, 0, 0, descr->width, descr->height );
900 if (descr->style & LBS_NOREDRAW) return 0;
901 if (descr->style & LBS_MULTICOLUMN)
902 rect.right = rect.left + descr->column_width;
903 else if (descr->horz_pos)
905 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
906 rect.right += descr->horz_pos;
909 if (descr->font) oldFont = SelectObject( hdc, descr->font );
910 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
911 hdc, (LPARAM)wnd->hwndSelf );
912 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
913 if (wnd->dwStyle & WS_DISABLED)
914 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
916 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
917 (descr->in_focus))
919 /* Special case for empty listbox: paint focus rect */
920 rect.bottom = rect.top + descr->item_height;
921 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
922 ODA_FOCUS );
923 rect.top = rect.bottom;
926 for (i = descr->top_item; i < descr->nb_items; i++)
928 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
929 rect.bottom = rect.top + descr->item_height;
930 else
931 rect.bottom = rect.top + descr->items[i].height;
933 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
934 rect.top = rect.bottom;
936 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
938 if (!IS_OWNERDRAW(descr))
940 /* Clear the bottom of the column */
941 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
942 if (rect.top < descr->height)
944 rect.bottom = descr->height;
945 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
946 &rect, NULL, 0, NULL );
950 /* Go to the next column */
951 rect.left += descr->column_width;
952 rect.right += descr->column_width;
953 rect.top = 0;
954 col_pos = descr->page_size - 1;
956 else
958 col_pos--;
959 if (rect.top >= descr->height) break;
963 if (!IS_OWNERDRAW(descr))
965 /* Clear the remainder of the client area */
966 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
967 if (rect.top < descr->height)
969 rect.bottom = descr->height;
970 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
971 &rect, NULL, 0, NULL );
973 if (rect.right < descr->width)
975 rect.left = rect.right;
976 rect.right = descr->width;
977 rect.top = 0;
978 rect.bottom = descr->height;
979 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
980 &rect, NULL, 0, NULL );
983 if (oldFont) SelectObject( hdc, oldFont );
984 if (oldBrush) SelectObject( hdc, oldBrush );
985 return 0;
989 /***********************************************************************
990 * LISTBOX_InvalidateItems
992 * Invalidate all items from a given item. If the specified item is not
993 * visible, nothing happens.
995 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
997 RECT rect;
999 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1001 rect.bottom = descr->height;
1002 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1003 if (descr->style & LBS_MULTICOLUMN)
1005 /* Repaint the other columns */
1006 rect.left = rect.right;
1007 rect.right = descr->width;
1008 rect.top = 0;
1009 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1015 /***********************************************************************
1016 * LISTBOX_GetItemHeight
1018 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1020 if (descr->style & LBS_OWNERDRAWVARIABLE)
1022 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1023 return descr->items[index].height;
1025 else return descr->item_height;
1029 /***********************************************************************
1030 * LISTBOX_SetItemHeight
1032 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1033 UINT height )
1035 if (!height) height = 1;
1037 if (descr->style & LBS_OWNERDRAWVARIABLE)
1039 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1040 TRACE("[%04x]: item %d height = %d\n",
1041 wnd->hwndSelf, index, height );
1042 descr->items[index].height = height;
1043 LISTBOX_UpdateScroll( wnd, descr );
1044 LISTBOX_InvalidateItems( wnd, descr, index );
1046 else if (height != descr->item_height)
1048 TRACE("[%04x]: new height = %d\n",
1049 wnd->hwndSelf, height );
1050 descr->item_height = height;
1051 LISTBOX_UpdatePage( wnd, descr );
1052 LISTBOX_UpdateScroll( wnd, descr );
1053 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1055 return LB_OKAY;
1059 /***********************************************************************
1060 * LISTBOX_SetHorizontalPos
1062 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1064 INT diff;
1066 if (pos > descr->horz_extent - descr->width)
1067 pos = descr->horz_extent - descr->width;
1068 if (pos < 0) pos = 0;
1069 if (!(diff = descr->horz_pos - pos)) return;
1070 TRACE("[%04x]: new horz pos = %d\n",
1071 wnd->hwndSelf, pos );
1072 descr->horz_pos = pos;
1073 LISTBOX_UpdateScroll( wnd, descr );
1074 if (abs(diff) < descr->width)
1075 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1076 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1077 else
1078 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1082 /***********************************************************************
1083 * LISTBOX_SetHorizontalExtent
1085 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1086 UINT extent )
1088 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1089 return LB_OKAY;
1090 if (extent <= 0) extent = 1;
1091 if (extent == descr->horz_extent) return LB_OKAY;
1092 TRACE("[%04x]: new horz extent = %d\n",
1093 wnd->hwndSelf, extent );
1094 descr->horz_extent = extent;
1095 if (descr->horz_pos > extent - descr->width)
1096 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1097 else
1098 LISTBOX_UpdateScroll( wnd, descr );
1099 return LB_OKAY;
1103 /***********************************************************************
1104 * LISTBOX_SetColumnWidth
1106 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1108 if (width == descr->column_width) return LB_OKAY;
1109 TRACE("[%04x]: new column width = %d\n",
1110 wnd->hwndSelf, width );
1111 descr->column_width = width;
1112 LISTBOX_UpdatePage( wnd, descr );
1113 return LB_OKAY;
1117 /***********************************************************************
1118 * LISTBOX_SetFont
1120 * Returns the item height.
1122 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1124 HDC hdc;
1125 HFONT oldFont = 0;
1126 TEXTMETRICA tm;
1128 descr->font = font;
1130 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1132 ERR("unable to get DC.\n" );
1133 return 16;
1135 if (font) oldFont = SelectObject( hdc, font );
1136 GetTextMetricsA( hdc, &tm );
1137 if (oldFont) SelectObject( hdc, oldFont );
1138 ReleaseDC( wnd->hwndSelf, hdc );
1139 if (!IS_OWNERDRAW(descr))
1140 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1141 return tm.tmHeight ;
1145 /***********************************************************************
1146 * LISTBOX_MakeItemVisible
1148 * Make sure that a given item is partially or fully visible.
1150 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1151 BOOL fully )
1153 INT top;
1155 if (index <= descr->top_item) top = index;
1156 else if (descr->style & LBS_MULTICOLUMN)
1158 INT cols = descr->width;
1159 if (!fully) cols += descr->column_width - 1;
1160 if (cols >= descr->column_width) cols /= descr->column_width;
1161 else cols = 1;
1162 if (index < descr->top_item + (descr->page_size * cols)) return;
1163 top = index - descr->page_size * (cols - 1);
1165 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1167 INT height = fully ? descr->items[index].height : 1;
1168 for (top = index; top > descr->top_item; top--)
1169 if ((height += descr->items[top-1].height) > descr->height) break;
1171 else
1173 if (index < descr->top_item + descr->page_size) return;
1174 if (!fully && (index == descr->top_item + descr->page_size) &&
1175 (descr->height > (descr->page_size * descr->item_height))) return;
1176 top = index - descr->page_size + 1;
1178 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1182 /***********************************************************************
1183 * LISTBOX_SelectItemRange
1185 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1187 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1188 INT last, BOOL on )
1190 INT i;
1192 /* A few sanity checks */
1194 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1195 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1196 if (last == -1) last = descr->nb_items - 1;
1197 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1198 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1199 /* selected_item reflects last selected/unselected item on multiple sel */
1200 descr->selected_item = last;
1202 if (on) /* Turn selection on */
1204 for (i = first; i <= last; i++)
1206 if (descr->items[i].selected) continue;
1207 descr->items[i].selected = TRUE;
1208 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1211 else /* Turn selection off */
1213 for (i = first; i <= last; i++)
1215 if (!descr->items[i].selected) continue;
1216 descr->items[i].selected = FALSE;
1217 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1220 return LB_OKAY;
1224 /***********************************************************************
1225 * LISTBOX_SetCaretIndex
1227 * NOTES
1228 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1231 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1232 BOOL fully_visible )
1234 INT oldfocus = descr->focus_item;
1236 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1237 if (index == oldfocus) return LB_OKAY;
1238 descr->focus_item = index;
1239 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1240 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1242 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1243 if (descr->caret_on && (descr->in_focus))
1244 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1246 return LB_OKAY;
1250 /***********************************************************************
1251 * LISTBOX_SetSelection
1253 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1254 BOOL on, BOOL send_notify )
1256 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1258 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1259 if (descr->style & LBS_MULTIPLESEL)
1261 if (index == -1) /* Select all items */
1262 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1263 else /* Only one item */
1264 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1266 else
1268 INT oldsel = descr->selected_item;
1269 if (index == oldsel) return LB_OKAY;
1270 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1271 if (index != -1) descr->items[index].selected = TRUE;
1272 descr->selected_item = index;
1273 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1274 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1275 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1276 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1277 else
1278 if( descr->lphc ) /* set selection change flag for parent combo */
1279 descr->lphc->wState |= CBF_SELCHANGE;
1281 return LB_OKAY;
1285 /***********************************************************************
1286 * LISTBOX_MoveCaret
1288 * Change the caret position and extend the selection to the new caret.
1290 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1291 BOOL fully_visible )
1293 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1294 if (descr->style & LBS_EXTENDEDSEL)
1296 if (descr->anchor_item != -1)
1298 INT first = min( descr->focus_item, descr->anchor_item );
1299 INT last = max( descr->focus_item, descr->anchor_item );
1300 if (first > 0)
1301 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1302 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1303 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1306 else if (!(descr->style & LBS_MULTIPLESEL))
1308 /* Set selection to new caret item */
1309 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1314 /***********************************************************************
1315 * LISTBOX_InsertItem
1317 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1318 LPSTR str, DWORD data )
1320 LB_ITEMDATA *item;
1321 INT max_items;
1322 INT oldfocus = descr->focus_item;
1324 if (index == -1) index = descr->nb_items;
1325 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1326 if (!descr->items) max_items = 0;
1327 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1328 if (descr->nb_items == max_items)
1330 /* We need to grow the array */
1331 max_items += LB_ARRAY_GRANULARITY;
1332 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1333 max_items * sizeof(LB_ITEMDATA) )))
1335 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1336 return LB_ERRSPACE;
1338 descr->items = item;
1341 /* Insert the item structure */
1343 item = &descr->items[index];
1344 if (index < descr->nb_items)
1345 RtlMoveMemory( item + 1, item,
1346 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1347 item->str = str;
1348 item->data = data;
1349 item->height = 0;
1350 item->selected = FALSE;
1351 descr->nb_items++;
1353 /* Get item height */
1355 if (descr->style & LBS_OWNERDRAWVARIABLE)
1357 MEASUREITEMSTRUCT mis;
1358 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1360 mis.CtlType = ODT_LISTBOX;
1361 mis.CtlID = id;
1362 mis.itemID = index;
1363 mis.itemData = descr->items[index].data;
1364 mis.itemHeight = descr->item_height;
1365 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1366 item->height = mis.itemHeight ? mis.itemHeight : 1;
1367 TRACE("[%04x]: measure item %d (%s) = %d\n",
1368 wnd->hwndSelf, index, str ? str : "", item->height );
1371 /* Repaint the items */
1373 LISTBOX_UpdateScroll( wnd, descr );
1374 LISTBOX_InvalidateItems( wnd, descr, index );
1376 /* Move selection and focused item */
1377 /* If listbox was empty, set focus to the first item */
1378 if (descr->nb_items == 1)
1379 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1380 /* single select don't change selection index in win31 */
1381 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1383 descr->selected_item++;
1384 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1386 else
1388 if (index <= descr->selected_item)
1390 descr->selected_item++;
1391 descr->focus_item = oldfocus; /* focus not changed */
1394 return LB_OKAY;
1398 /***********************************************************************
1399 * LISTBOX_InsertString
1401 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1402 LPCSTR str )
1404 LPSTR new_str = NULL;
1405 DWORD data = 0;
1406 LRESULT ret;
1408 if (HAS_STRINGS(descr))
1410 if (!str) str="";
1411 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1413 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1414 return LB_ERRSPACE;
1417 else data = (DWORD)str;
1419 if (index == -1) index = descr->nb_items;
1420 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1422 if (new_str) HeapFree( descr->heap, 0, new_str );
1423 return ret;
1426 TRACE("[%04x]: added item %d '%s'\n",
1427 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1428 return index;
1432 /***********************************************************************
1433 * LISTBOX_DeleteItem
1435 * Delete the content of an item. 'index' must be a valid index.
1437 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1439 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1440 * while Win95 sends it for all items with user data.
1441 * It's probably better to send it too often than not
1442 * often enough, so this is what we do here.
1444 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1446 DELETEITEMSTRUCT dis;
1447 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1449 dis.CtlType = ODT_LISTBOX;
1450 dis.CtlID = id;
1451 dis.itemID = index;
1452 dis.hwndItem = wnd->hwndSelf;
1453 dis.itemData = descr->items[index].data;
1454 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1456 if (HAS_STRINGS(descr) && descr->items[index].str)
1457 HeapFree( descr->heap, 0, descr->items[index].str );
1461 /***********************************************************************
1462 * LISTBOX_RemoveItem
1464 * Remove an item from the listbox and delete its content.
1466 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1468 LB_ITEMDATA *item;
1469 INT max_items;
1471 if (index == -1) index = descr->nb_items - 1;
1472 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1474 /* We need to invalidate the original rect instead of the updated one. */
1475 LISTBOX_InvalidateItems( wnd, descr, index );
1477 LISTBOX_DeleteItem( wnd, descr, index );
1479 /* Remove the item */
1481 item = &descr->items[index];
1482 if (index < descr->nb_items-1)
1483 RtlMoveMemory( item, item + 1,
1484 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1485 descr->nb_items--;
1486 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1488 /* Shrink the item array if possible */
1490 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1491 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1493 max_items -= LB_ARRAY_GRANULARITY;
1494 item = HeapReAlloc( descr->heap, 0, descr->items,
1495 max_items * sizeof(LB_ITEMDATA) );
1496 if (item) descr->items = item;
1498 /* Repaint the items */
1500 LISTBOX_UpdateScroll( wnd, descr );
1501 /* if we removed the scrollbar, reset the top of the list
1502 (correct for owner-drawn ???) */
1503 if (descr->nb_items == descr->page_size)
1504 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1506 /* Move selection and focused item */
1507 if (!IS_MULTISELECT(descr))
1509 if (index == descr->selected_item)
1510 descr->selected_item = -1;
1511 else if (index < descr->selected_item)
1513 descr->selected_item--;
1514 if (ISWIN31) /* win 31 do not change the selected item number */
1515 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1519 if (descr->focus_item >= descr->nb_items)
1521 descr->focus_item = descr->nb_items - 1;
1522 if (descr->focus_item < 0) descr->focus_item = 0;
1524 return LB_OKAY;
1528 /***********************************************************************
1529 * LISTBOX_ResetContent
1531 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1533 INT i;
1535 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1536 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1537 descr->nb_items = 0;
1538 descr->top_item = 0;
1539 descr->selected_item = -1;
1540 descr->focus_item = 0;
1541 descr->anchor_item = -1;
1542 descr->items = NULL;
1543 LISTBOX_UpdateScroll( wnd, descr );
1544 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1548 /***********************************************************************
1549 * LISTBOX_SetCount
1551 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1553 LRESULT ret;
1555 if (HAS_STRINGS(descr)) return LB_ERR;
1556 /* FIXME: this is far from optimal... */
1557 if (count > descr->nb_items)
1559 while (count > descr->nb_items)
1560 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1561 return ret;
1563 else if (count < descr->nb_items)
1565 while (count < descr->nb_items)
1566 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1567 return ret;
1569 return LB_OKAY;
1573 /***********************************************************************
1574 * LISTBOX_Directory
1576 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1577 LPCSTR filespec, BOOL long_names )
1579 HANDLE handle;
1580 LRESULT ret = LB_OKAY;
1581 WIN32_FIND_DATAA entry;
1582 int pos;
1584 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1586 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1588 else
1592 char buffer[270];
1593 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1595 if (!(attrib & DDL_DIRECTORY) ||
1596 !strcmp( entry.cAlternateFileName, "." )) continue;
1597 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1598 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1600 else /* not a directory */
1602 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1603 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1605 if ((attrib & DDL_EXCLUSIVE) &&
1606 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1607 continue;
1608 #undef ATTRIBS
1609 if (long_names) strcpy( buffer, entry.cFileName );
1610 else strcpy( buffer, entry.cAlternateFileName );
1612 if (!long_names) CharLowerA( buffer );
1613 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1614 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1615 break;
1616 } while (FindNextFileA( handle, &entry ));
1617 FindClose( handle );
1620 if ((ret >= 0) && (attrib & DDL_DRIVES))
1622 char buffer[] = "[-a-]";
1623 int drive;
1624 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1626 if (!DRIVE_IsValid(drive)) continue;
1627 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1628 break;
1631 return ret;
1635 /***********************************************************************
1636 * LISTBOX_HandleVScroll
1638 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1639 WPARAM wParam, LPARAM lParam )
1641 SCROLLINFO info;
1643 if (descr->style & LBS_MULTICOLUMN) return 0;
1644 switch(LOWORD(wParam))
1646 case SB_LINEUP:
1647 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1648 break;
1649 case SB_LINEDOWN:
1650 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1651 break;
1652 case SB_PAGEUP:
1653 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1654 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1655 break;
1656 case SB_PAGEDOWN:
1657 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1658 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1659 break;
1660 case SB_THUMBPOSITION:
1661 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1662 break;
1663 case SB_THUMBTRACK:
1664 info.cbSize = sizeof(info);
1665 info.fMask = SIF_TRACKPOS;
1666 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1667 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1668 break;
1669 case SB_TOP:
1670 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1671 break;
1672 case SB_BOTTOM:
1673 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1674 break;
1676 return 0;
1680 /***********************************************************************
1681 * LISTBOX_HandleHScroll
1683 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1684 WPARAM wParam, LPARAM lParam )
1686 SCROLLINFO info;
1687 INT page;
1689 if (descr->style & LBS_MULTICOLUMN)
1691 switch(LOWORD(wParam))
1693 case SB_LINELEFT:
1694 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1695 TRUE );
1696 break;
1697 case SB_LINERIGHT:
1698 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1699 TRUE );
1700 break;
1701 case SB_PAGELEFT:
1702 page = descr->width / descr->column_width;
1703 if (page < 1) page = 1;
1704 LISTBOX_SetTopItem( wnd, descr,
1705 descr->top_item - page * descr->page_size, TRUE );
1706 break;
1707 case SB_PAGERIGHT:
1708 page = descr->width / descr->column_width;
1709 if (page < 1) page = 1;
1710 LISTBOX_SetTopItem( wnd, descr,
1711 descr->top_item + page * descr->page_size, TRUE );
1712 break;
1713 case SB_THUMBPOSITION:
1714 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1715 TRUE );
1716 break;
1717 case SB_THUMBTRACK:
1718 info.cbSize = sizeof(info);
1719 info.fMask = SIF_TRACKPOS;
1720 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1721 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1722 TRUE );
1723 break;
1724 case SB_LEFT:
1725 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1726 break;
1727 case SB_RIGHT:
1728 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1729 break;
1732 else if (descr->horz_extent)
1734 switch(LOWORD(wParam))
1736 case SB_LINELEFT:
1737 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1738 break;
1739 case SB_LINERIGHT:
1740 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1741 break;
1742 case SB_PAGELEFT:
1743 LISTBOX_SetHorizontalPos( wnd, descr,
1744 descr->horz_pos - descr->width );
1745 break;
1746 case SB_PAGERIGHT:
1747 LISTBOX_SetHorizontalPos( wnd, descr,
1748 descr->horz_pos + descr->width );
1749 break;
1750 case SB_THUMBPOSITION:
1751 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1752 break;
1753 case SB_THUMBTRACK:
1754 info.cbSize = sizeof(info);
1755 info.fMask = SIF_TRACKPOS;
1756 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1757 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1758 break;
1759 case SB_LEFT:
1760 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1761 break;
1762 case SB_RIGHT:
1763 LISTBOX_SetHorizontalPos( wnd, descr,
1764 descr->horz_extent - descr->width );
1765 break;
1768 return 0;
1771 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1773 short gcWheelDelta = 0;
1774 UINT pulScrollLines = 3;
1776 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1778 gcWheelDelta -= (short) HIWORD(wParam);
1780 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1782 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1783 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1784 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1786 return 0;
1789 /***********************************************************************
1790 * LISTBOX_HandleLButtonDown
1792 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1793 WPARAM wParam, INT x, INT y )
1795 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1796 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1797 wnd->hwndSelf, x, y, index );
1798 if (!descr->caret_on && (descr->in_focus)) return 0;
1799 if (index != -1)
1801 if (descr->style & LBS_EXTENDEDSEL)
1803 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1804 if (wParam & MK_CONTROL)
1806 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1807 LISTBOX_SetSelection( wnd, descr, index,
1808 !descr->items[index].selected,
1809 (descr->style & LBS_NOTIFY) != 0);
1811 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1813 else
1815 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1816 LISTBOX_SetSelection( wnd, descr, index,
1817 (!(descr->style & LBS_MULTIPLESEL) ||
1818 !descr->items[index].selected),
1819 (descr->style & LBS_NOTIFY) != 0 );
1823 if(!descr->in_focus)
1825 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1826 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1827 : descr->lphc->self->hwndSelf );
1830 descr->captured = TRUE;
1831 SetCapture( wnd->hwndSelf );
1832 if (index != -1 && !descr->lphc)
1834 if (descr->style & LBS_NOTIFY )
1835 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1836 MAKELPARAM( x, y ) );
1837 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1839 POINT pt;
1841 pt.x = x;
1842 pt.y = y;
1844 if (DragDetect( wnd->hwndSelf, pt ))
1845 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1848 return 0;
1852 /*************************************************************************
1853 * LISTBOX_HandleLButtonDownCombo [Internal]
1855 * Process LButtonDown message for the ComboListBox
1857 * PARAMS
1858 * pWnd [I] The windows internal structure
1859 * pDescr [I] The ListBox internal structure
1860 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1861 * x [I] X Mouse Coordinate
1862 * y [I] Y Mouse Coordinate
1864 * RETURNS
1865 * 0 since we are processing the WM_LBUTTONDOWN Message
1867 * NOTES
1868 * This function is only to be used when a ListBox is a ComboListBox
1871 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1872 UINT msg, WPARAM wParam, INT x, INT y)
1874 RECT clientRect, screenRect;
1875 POINT mousePos;
1877 mousePos.x = x;
1878 mousePos.y = y;
1880 GetClientRect(pWnd->hwndSelf, &clientRect);
1882 if(PtInRect(&clientRect, mousePos))
1884 /* MousePos is in client, resume normal processing */
1885 if (msg == WM_LBUTTONDOWN)
1886 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1887 else if (pDescr->style & LBS_NOTIFY)
1888 SEND_NOTIFICATION( pWnd, pDescr, LBN_DBLCLK );
1889 return 0;
1891 else
1893 POINT screenMousePos;
1894 HWND hWndOldCapture;
1896 /* Check the Non-Client Area */
1897 screenMousePos = mousePos;
1898 hWndOldCapture = GetCapture();
1899 ReleaseCapture();
1900 GetWindowRect(pWnd->hwndSelf, &screenRect);
1901 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1903 if(!PtInRect(&screenRect, screenMousePos))
1905 /* Close The Drop Down */
1906 SEND_NOTIFICATION( pWnd, pDescr, LBN_SELCANCEL );
1907 return 0;
1909 else
1911 /* Check to see the NC is a scrollbar */
1912 INT nHitTestType=0;
1913 /* Check Vertical scroll bar */
1914 if (pWnd->dwStyle & WS_VSCROLL)
1916 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1917 if (PtInRect( &clientRect, mousePos ))
1919 nHitTestType = HTVSCROLL;
1922 /* Check horizontal scroll bar */
1923 if (pWnd->dwStyle & WS_HSCROLL)
1925 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1926 if (PtInRect( &clientRect, mousePos ))
1928 nHitTestType = HTHSCROLL;
1931 /* Windows sends this message when a scrollbar is clicked
1934 if(nHitTestType != 0)
1936 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1937 MAKELONG(screenMousePos.x, screenMousePos.y));
1939 /* Resume the Capture after scrolling is complete
1941 if(hWndOldCapture != 0)
1943 SetCapture(hWndOldCapture);
1947 return 0;
1950 /***********************************************************************
1951 * LISTBOX_HandleLButtonUp
1953 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1955 if (LISTBOX_Timer != LB_TIMER_NONE)
1956 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1957 LISTBOX_Timer = LB_TIMER_NONE;
1958 if (descr->captured)
1960 descr->captured = FALSE;
1961 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1962 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
1963 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1965 return 0;
1969 /***********************************************************************
1970 * LISTBOX_HandleTimer
1972 * Handle scrolling upon a timer event.
1973 * Return TRUE if scrolling should continue.
1975 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1976 INT index, TIMER_DIRECTION dir )
1978 switch(dir)
1980 case LB_TIMER_UP:
1981 if (descr->top_item) index = descr->top_item - 1;
1982 else index = 0;
1983 break;
1984 case LB_TIMER_LEFT:
1985 if (descr->top_item) index -= descr->page_size;
1986 break;
1987 case LB_TIMER_DOWN:
1988 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1989 if (index == descr->focus_item) index++;
1990 if (index >= descr->nb_items) index = descr->nb_items - 1;
1991 break;
1992 case LB_TIMER_RIGHT:
1993 if (index + descr->page_size < descr->nb_items)
1994 index += descr->page_size;
1995 break;
1996 case LB_TIMER_NONE:
1997 break;
1999 if (index == descr->focus_item) return FALSE;
2000 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
2001 return TRUE;
2005 /***********************************************************************
2006 * LISTBOX_HandleSystemTimer
2008 * WM_SYSTIMER handler.
2010 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2012 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2014 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2015 LISTBOX_Timer = LB_TIMER_NONE;
2017 return 0;
2021 /***********************************************************************
2022 * LISTBOX_HandleMouseMove
2024 * WM_MOUSEMOVE handler.
2026 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2027 INT x, INT y )
2029 INT index;
2030 TIMER_DIRECTION dir;
2032 if (!descr->captured) return;
2034 if (descr->style & LBS_MULTICOLUMN)
2036 if (y < 0) y = 0;
2037 else if (y >= descr->item_height * descr->page_size)
2038 y = descr->item_height * descr->page_size - 1;
2040 if (x < 0)
2042 dir = LB_TIMER_LEFT;
2043 x = 0;
2045 else if (x >= descr->width)
2047 dir = LB_TIMER_RIGHT;
2048 x = descr->width - 1;
2050 else dir = LB_TIMER_NONE; /* inside */
2052 else
2054 if (y < 0) dir = LB_TIMER_UP; /* above */
2055 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2056 else dir = LB_TIMER_NONE; /* inside */
2059 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2060 if (index == -1) index = descr->focus_item;
2061 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2063 /* Start/stop the system timer */
2065 if (dir != LB_TIMER_NONE)
2066 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2067 else if (LISTBOX_Timer != LB_TIMER_NONE)
2068 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2069 LISTBOX_Timer = dir;
2073 /***********************************************************************
2074 * LISTBOX_HandleKeyDown
2076 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2078 INT caret = -1;
2079 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2080 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2081 bForceSelection = FALSE; /* only for single select list */
2083 if (descr->style & LBS_WANTKEYBOARDINPUT)
2085 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2086 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2087 wnd->hwndSelf );
2088 if (caret == -2) return 0;
2090 if (caret == -1) switch(wParam)
2092 case VK_LEFT:
2093 if (descr->style & LBS_MULTICOLUMN)
2095 bForceSelection = FALSE;
2096 if (descr->focus_item >= descr->page_size)
2097 caret = descr->focus_item - descr->page_size;
2098 break;
2100 /* fall through */
2101 case VK_UP:
2102 caret = descr->focus_item - 1;
2103 if (caret < 0) caret = 0;
2104 break;
2105 case VK_RIGHT:
2106 if (descr->style & LBS_MULTICOLUMN)
2108 bForceSelection = FALSE;
2109 if (descr->focus_item + descr->page_size < descr->nb_items)
2110 caret = descr->focus_item + descr->page_size;
2111 break;
2113 /* fall through */
2114 case VK_DOWN:
2115 caret = descr->focus_item + 1;
2116 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2117 break;
2119 case VK_PRIOR:
2120 if (descr->style & LBS_MULTICOLUMN)
2122 INT page = descr->width / descr->column_width;
2123 if (page < 1) page = 1;
2124 caret = descr->focus_item - (page * descr->page_size) + 1;
2126 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2127 if (caret < 0) caret = 0;
2128 break;
2129 case VK_NEXT:
2130 if (descr->style & LBS_MULTICOLUMN)
2132 INT page = descr->width / descr->column_width;
2133 if (page < 1) page = 1;
2134 caret = descr->focus_item + (page * descr->page_size) - 1;
2136 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2137 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2138 break;
2139 case VK_HOME:
2140 caret = 0;
2141 break;
2142 case VK_END:
2143 caret = descr->nb_items - 1;
2144 break;
2145 case VK_SPACE:
2146 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2147 else if (descr->style & LBS_MULTIPLESEL)
2149 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2150 !descr->items[descr->focus_item].selected,
2151 (descr->style & LBS_NOTIFY) != 0 );
2153 break;
2154 default:
2155 bForceSelection = FALSE;
2157 if (bForceSelection) /* focused item is used instead of key */
2158 caret = descr->focus_item;
2159 if (caret >= 0)
2161 if ((descr->style & LBS_EXTENDEDSEL) &&
2162 !(GetKeyState( VK_SHIFT ) & 0x8000))
2163 descr->anchor_item = caret;
2164 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2165 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2166 if (descr->style & LBS_NOTIFY)
2168 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2170 /* make sure that combo parent doesn't hide us */
2171 descr->lphc->wState |= CBF_NOROLLUP;
2173 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2176 return 0;
2180 /***********************************************************************
2181 * LISTBOX_HandleChar
2183 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2184 WPARAM wParam )
2186 INT caret = -1;
2187 char str[2];
2189 str[0] = wParam & 0xff;
2190 str[1] = '\0';
2192 if (descr->style & LBS_WANTKEYBOARDINPUT)
2194 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2195 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2196 wnd->hwndSelf );
2197 if (caret == -2) return 0;
2199 if (caret == -1)
2200 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2201 if (caret != -1)
2203 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2204 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2205 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2206 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2207 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2209 return 0;
2213 /***********************************************************************
2214 * LISTBOX_Create
2216 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2218 LB_DESCR *descr;
2219 MEASUREITEMSTRUCT mis;
2220 RECT rect;
2222 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2223 return FALSE;
2224 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2226 HeapFree( GetProcessHeap(), 0, descr );
2227 return FALSE;
2229 GetClientRect( wnd->hwndSelf, &rect );
2230 descr->owner = GetParent( wnd->hwndSelf );
2231 descr->style = wnd->dwStyle;
2232 descr->width = rect.right - rect.left;
2233 descr->height = rect.bottom - rect.top;
2234 descr->items = NULL;
2235 descr->nb_items = 0;
2236 descr->top_item = 0;
2237 descr->selected_item = -1;
2238 descr->focus_item = 0;
2239 descr->anchor_item = -1;
2240 descr->item_height = 1;
2241 descr->page_size = 1;
2242 descr->column_width = 150;
2243 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2244 descr->horz_pos = 0;
2245 descr->nb_tabs = 0;
2246 descr->tabs = NULL;
2247 descr->caret_on = TRUE;
2248 descr->in_focus = FALSE;
2249 descr->captured = FALSE;
2250 descr->font = 0;
2251 descr->locale = 0; /* FIXME */
2252 descr->lphc = lphc;
2254 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2255 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2257 /* Win95 document "List Box Differences" from MSDN:
2258 If a list box in a version 3.x application has either the
2259 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2260 horizontal and vertical scroll bars.
2262 descr->style |= WS_VSCROLL | WS_HSCROLL;
2265 if( lphc )
2267 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2268 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2269 descr->owner = lphc->self->hwndSelf;
2272 *(LB_DESCR **)wnd->wExtra = descr;
2274 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2276 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2277 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2278 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2279 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2281 if (descr->style & LBS_OWNERDRAWFIXED)
2283 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2285 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2286 descr->item_height = lphc->fixedOwnerDrawHeight;
2288 else
2290 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2292 mis.CtlType = ODT_LISTBOX;
2293 mis.CtlID = id;
2294 mis.itemID = -1;
2295 mis.itemWidth = 0;
2296 mis.itemData = 0;
2297 mis.itemHeight = descr->item_height;
2298 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2299 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2303 return TRUE;
2307 /***********************************************************************
2308 * LISTBOX_Destroy
2310 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2312 LISTBOX_ResetContent( wnd, descr );
2313 HeapDestroy( descr->heap );
2314 HeapFree( GetProcessHeap(), 0, descr );
2315 wnd->wExtra[0] = 0;
2316 return TRUE;
2320 /***********************************************************************
2321 * ListBoxWndProc
2323 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2324 WPARAM wParam, LPARAM lParam )
2326 LRESULT ret;
2327 LB_DESCR *descr;
2328 HWND hwnd = wnd->hwndSelf;
2330 if (!wnd) return 0;
2331 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2333 switch (msg)
2335 case WM_CREATE:
2337 if (!LISTBOX_Create( wnd, NULL ))
2338 return -1;
2339 TRACE("creating wnd=%04x descr=%p\n",
2340 hwnd, *(LB_DESCR **)wnd->wExtra );
2341 return 0;
2343 case WM_NCCREATE:
2346 * When a listbox is not in a combobox and the look
2347 * is win95, the WS_BORDER style is replaced with
2348 * the WS_EX_CLIENTEDGE style.
2350 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2351 (wnd->dwStyle & WS_BORDER) )
2353 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2354 wnd->dwStyle &= ~ WS_BORDER;
2359 /* Ignore all other messages before we get a WM_CREATE */
2360 return DefWindowProcA( hwnd, msg, wParam, lParam );
2363 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2364 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2365 switch(msg)
2367 case LB_RESETCONTENT16:
2368 case LB_RESETCONTENT:
2369 LISTBOX_ResetContent( wnd, descr );
2370 return 0;
2372 case LB_ADDSTRING16:
2373 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2374 /* fall through */
2375 case LB_ADDSTRING:
2376 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2377 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2379 case LB_INSERTSTRING16:
2380 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2381 wParam = (INT)(INT16)wParam;
2382 /* fall through */
2383 case LB_INSERTSTRING:
2384 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2386 case LB_ADDFILE16:
2387 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2388 /* fall through */
2389 case LB_ADDFILE:
2390 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2391 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2393 case LB_DELETESTRING16:
2394 case LB_DELETESTRING:
2395 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2396 return descr->nb_items;
2397 else
2398 return LB_ERR;
2400 case LB_GETITEMDATA16:
2401 case LB_GETITEMDATA:
2402 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2403 return LB_ERR;
2404 return descr->items[wParam].data;
2406 case LB_SETITEMDATA16:
2407 case LB_SETITEMDATA:
2408 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2409 return LB_ERR;
2410 descr->items[wParam].data = (DWORD)lParam;
2411 return LB_OKAY;
2413 case LB_GETCOUNT16:
2414 case LB_GETCOUNT:
2415 return descr->nb_items;
2417 case LB_GETTEXT16:
2418 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2419 /* fall through */
2420 case LB_GETTEXT:
2421 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2423 case LB_GETTEXTLEN16:
2424 /* fall through */
2425 case LB_GETTEXTLEN:
2426 if (wParam >= descr->nb_items)
2427 return LB_ERR;
2428 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2429 : sizeof(DWORD));
2431 case LB_GETCURSEL16:
2432 case LB_GETCURSEL:
2433 if (descr->nb_items==0)
2434 return LB_ERR;
2435 if (!IS_MULTISELECT(descr))
2436 return descr->selected_item;
2437 /* else */
2438 if (descr->selected_item!=-1)
2439 return descr->selected_item;
2440 /* else */
2441 return descr->focus_item;
2442 /* otherwise, if the user tries to move the selection with the */
2443 /* arrow keys, we will give the application something to choke on */
2444 case LB_GETTOPINDEX16:
2445 case LB_GETTOPINDEX:
2446 return descr->top_item;
2448 case LB_GETITEMHEIGHT16:
2449 case LB_GETITEMHEIGHT:
2450 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2452 case LB_SETITEMHEIGHT16:
2453 lParam = LOWORD(lParam);
2454 /* fall through */
2455 case LB_SETITEMHEIGHT:
2456 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2458 case LB_ITEMFROMPOINT:
2460 POINT pt;
2461 RECT rect;
2463 pt.x = LOWORD(lParam);
2464 pt.y = HIWORD(lParam);
2465 rect.left = 0;
2466 rect.top = 0;
2467 rect.right = descr->width;
2468 rect.bottom = descr->height;
2470 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2471 !PtInRect( &rect, pt ) );
2474 case LB_SETCARETINDEX16:
2475 case LB_SETCARETINDEX:
2476 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2477 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2478 return LB_ERR;
2479 else if (ISWIN31)
2480 return wParam;
2481 else
2482 return LB_OKAY;
2484 case LB_GETCARETINDEX16:
2485 case LB_GETCARETINDEX:
2486 return descr->focus_item;
2488 case LB_SETTOPINDEX16:
2489 case LB_SETTOPINDEX:
2490 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2492 case LB_SETCOLUMNWIDTH16:
2493 case LB_SETCOLUMNWIDTH:
2494 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2496 case LB_GETITEMRECT16:
2498 RECT rect;
2499 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2500 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2502 return ret;
2504 case LB_GETITEMRECT:
2505 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2507 case LB_FINDSTRING16:
2508 wParam = (INT)(INT16)wParam;
2509 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2510 /* fall through */
2511 case LB_FINDSTRING:
2512 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2514 case LB_FINDSTRINGEXACT16:
2515 wParam = (INT)(INT16)wParam;
2516 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2517 /* fall through */
2518 case LB_FINDSTRINGEXACT:
2519 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2521 case LB_SELECTSTRING16:
2522 wParam = (INT)(INT16)wParam;
2523 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2524 /* fall through */
2525 case LB_SELECTSTRING:
2527 INT index = LISTBOX_FindString( wnd, descr, wParam,
2528 (LPCSTR)lParam, FALSE );
2529 if (index == LB_ERR)
2530 return LB_ERR;
2531 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2532 return index;
2535 case LB_GETSEL16:
2536 wParam = (INT)(INT16)wParam;
2537 /* fall through */
2538 case LB_GETSEL:
2539 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2540 return LB_ERR;
2541 return descr->items[wParam].selected;
2543 case LB_SETSEL16:
2544 lParam = (INT)(INT16)lParam;
2545 /* fall through */
2546 case LB_SETSEL:
2547 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2549 case LB_SETCURSEL16:
2550 wParam = (INT)(INT16)wParam;
2551 /* fall through */
2552 case LB_SETCURSEL:
2553 if (IS_MULTISELECT(descr)) return LB_ERR;
2554 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2555 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2557 case LB_GETSELCOUNT16:
2558 case LB_GETSELCOUNT:
2559 return LISTBOX_GetSelCount( wnd, descr );
2561 case LB_GETSELITEMS16:
2562 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2563 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2565 case LB_GETSELITEMS:
2566 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2568 case LB_SELITEMRANGE16:
2569 case LB_SELITEMRANGE:
2570 if (LOWORD(lParam) <= HIWORD(lParam))
2571 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2572 HIWORD(lParam), wParam );
2573 else
2574 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2575 LOWORD(lParam), wParam );
2577 case LB_SELITEMRANGEEX16:
2578 case LB_SELITEMRANGEEX:
2579 if ((INT)lParam >= (INT)wParam)
2580 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2581 else
2582 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2584 case LB_GETHORIZONTALEXTENT16:
2585 case LB_GETHORIZONTALEXTENT:
2586 return descr->horz_extent;
2588 case LB_SETHORIZONTALEXTENT16:
2589 case LB_SETHORIZONTALEXTENT:
2590 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2592 case LB_GETANCHORINDEX16:
2593 case LB_GETANCHORINDEX:
2594 return descr->anchor_item;
2596 case LB_SETANCHORINDEX16:
2597 wParam = (INT)(INT16)wParam;
2598 /* fall through */
2599 case LB_SETANCHORINDEX:
2600 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2601 return LB_ERR;
2602 descr->anchor_item = (INT)wParam;
2603 return LB_OKAY;
2605 case LB_DIR16:
2606 return LISTBOX_Directory( wnd, descr, wParam,
2607 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2609 case LB_DIR:
2610 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2612 case LB_GETLOCALE:
2613 return descr->locale;
2615 case LB_SETLOCALE:
2616 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2617 return LB_OKAY;
2619 case LB_INITSTORAGE:
2620 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2622 case LB_SETCOUNT:
2623 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2625 case LB_SETTABSTOPS16:
2626 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2627 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2629 case LB_SETTABSTOPS:
2630 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2632 case LB_CARETON16:
2633 case LB_CARETON:
2634 if (descr->caret_on)
2635 return LB_OKAY;
2636 descr->caret_on = TRUE;
2637 if ((descr->focus_item != -1) && (descr->in_focus))
2638 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2639 return LB_OKAY;
2641 case LB_CARETOFF16:
2642 case LB_CARETOFF:
2643 if (!descr->caret_on)
2644 return LB_OKAY;
2645 descr->caret_on = FALSE;
2646 if ((descr->focus_item != -1) && (descr->in_focus))
2647 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2648 return LB_OKAY;
2650 case WM_DESTROY:
2651 return LISTBOX_Destroy( wnd, descr );
2653 case WM_ENABLE:
2654 InvalidateRect( hwnd, NULL, TRUE );
2655 return 0;
2657 case WM_SETREDRAW:
2658 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2659 return 0;
2661 case WM_GETDLGCODE:
2662 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2664 case WM_PAINT:
2666 PAINTSTRUCT ps;
2667 HDC hdc = ( wParam ) ? ((HDC)wParam)
2668 : BeginPaint( hwnd, &ps );
2669 ret = LISTBOX_Paint( wnd, descr, hdc );
2670 if( !wParam ) EndPaint( hwnd, &ps );
2672 return ret;
2673 case WM_SIZE:
2674 LISTBOX_UpdateSize( wnd, descr );
2675 return 0;
2676 case WM_GETFONT:
2677 return descr->font;
2678 case WM_SETFONT:
2679 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2680 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2681 return 0;
2682 case WM_SETFOCUS:
2683 descr->in_focus = TRUE;
2684 descr->caret_on = TRUE;
2685 if (descr->focus_item != -1)
2686 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2687 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2688 return 0;
2689 case WM_KILLFOCUS:
2690 descr->in_focus = FALSE;
2691 if ((descr->focus_item != -1) && descr->caret_on)
2692 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2693 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2694 return 0;
2695 case WM_HSCROLL:
2696 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2697 case WM_VSCROLL:
2698 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2699 case WM_MOUSEACTIVATE:
2700 return MA_NOACTIVATE;
2701 case WM_MOUSEWHEEL:
2702 if (wParam & (MK_SHIFT | MK_CONTROL))
2703 return DefWindowProcA( hwnd, msg, wParam, lParam );
2704 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2705 case WM_LBUTTONDOWN:
2706 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2707 (INT16)LOWORD(lParam),
2708 (INT16)HIWORD(lParam) );
2709 case WM_LBUTTONDBLCLK:
2710 if (descr->style & LBS_NOTIFY)
2711 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2712 return 0;
2713 case WM_MOUSEMOVE:
2714 if (GetCapture() == hwnd)
2715 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2716 (INT16)HIWORD(lParam) );
2717 return 0;
2718 case WM_LBUTTONUP:
2719 return LISTBOX_HandleLButtonUp( wnd, descr );
2720 case WM_KEYDOWN:
2721 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2722 case WM_CHAR:
2723 return LISTBOX_HandleChar( wnd, descr, wParam );
2724 case WM_SYSTIMER:
2725 return LISTBOX_HandleSystemTimer( wnd, descr );
2726 case WM_ERASEBKGND:
2727 if (IS_OWNERDRAW(descr))
2729 RECT rect;
2730 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2731 wParam, (LPARAM)wnd->hwndSelf );
2732 GetClientRect(hwnd, &rect);
2733 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2735 return 1;
2736 case WM_DROPFILES:
2737 if( !descr->lphc )
2738 return SendMessageA( descr->owner, msg, wParam, lParam );
2739 break;
2741 case WM_DROPOBJECT:
2742 case WM_QUERYDROPOBJECT:
2743 case WM_DRAGSELECT:
2744 case WM_DRAGMOVE:
2745 if( !descr->lphc )
2747 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2748 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2749 dragInfo->pt.y );
2750 return SendMessageA( descr->owner, msg, wParam, lParam );
2752 break;
2754 default:
2755 if ((msg >= WM_USER) && (msg < 0xc000))
2756 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2757 hwnd, msg, wParam, lParam );
2758 return DefWindowProcA( hwnd, msg, wParam, lParam );
2760 return 0;
2763 /***********************************************************************
2764 * ListBoxWndProc
2766 * This is just a wrapper for the real wndproc, it only does window locking
2767 * and unlocking.
2769 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2770 WPARAM wParam, LPARAM lParam )
2772 WND* wndPtr = WIN_FindWndPtr( hwnd );
2773 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2775 WIN_ReleaseWndPtr(wndPtr);
2776 return res;
2779 /***********************************************************************
2780 * COMBO_Directory
2782 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2784 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2786 if( wnd )
2788 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2789 if( descr )
2791 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2793 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2794 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2795 WIN_ReleaseWndPtr(wnd);
2796 return lRet;
2798 WIN_ReleaseWndPtr(wnd);
2800 return CB_ERR;
2803 /***********************************************************************
2804 * ComboLBWndProc_locked
2806 * The real combo listbox wndproc, but called with locked WND struct.
2808 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2809 WPARAM wParam, LPARAM lParam )
2811 LRESULT lRet = 0;
2812 HWND hwnd = wnd->hwndSelf;
2814 if (wnd)
2816 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2818 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2819 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2821 if( descr || msg == WM_CREATE )
2823 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2825 switch( msg )
2827 case WM_CREATE:
2828 #define lpcs ((LPCREATESTRUCTA)lParam)
2829 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2830 (UINT)lpcs->lpCreateParams);
2832 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2833 #undef lpcs
2834 return LISTBOX_Create( wnd, lphc );
2835 case WM_MOUSEMOVE:
2836 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2837 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2839 POINT mousePos;
2840 BOOL captured;
2841 RECT clientRect;
2843 mousePos.x = (INT16)LOWORD(lParam);
2844 mousePos.y = (INT16)HIWORD(lParam);
2847 * If we are in a dropdown combobox, we simulate that
2848 * the mouse is captured to show the tracking of the item.
2850 GetClientRect(hwnd, &clientRect);
2852 if (PtInRect( &clientRect, mousePos ))
2854 captured = descr->captured;
2855 descr->captured = TRUE;
2857 LISTBOX_HandleMouseMove( wnd, descr,
2858 mousePos.x, mousePos.y);
2860 descr->captured = captured;
2863 else
2865 LISTBOX_HandleMouseMove( wnd, descr,
2866 mousePos.x, mousePos.y);
2869 return 0;
2872 else
2875 * If we are in Win3.1 look, go with the default behavior.
2877 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2879 case WM_LBUTTONUP:
2880 if (TWEAK_WineLook > WIN31_LOOK)
2882 POINT mousePos;
2883 RECT clientRect;
2886 * If the mouse button "up" is not in the listbox,
2887 * we make sure there is no selection by re-selecting the
2888 * item that was selected when the listbox was made visible.
2890 mousePos.x = (INT16)LOWORD(lParam);
2891 mousePos.y = (INT16)HIWORD(lParam);
2893 GetClientRect(hwnd, &clientRect);
2896 * When the user clicks outside the combobox and the focus
2897 * is lost, the owning combobox will send a fake buttonup with
2898 * 0xFFFFFFF as the mouse location, we must also revert the
2899 * selection to the original selection.
2901 if ( (lParam == 0xFFFFFFFF) ||
2902 (!PtInRect( &clientRect, mousePos )) )
2904 LISTBOX_MoveCaret( wnd,
2905 descr,
2906 lphc->droppedIndex,
2907 FALSE );
2910 return LISTBOX_HandleLButtonUp( wnd, descr );
2911 case WM_LBUTTONDBLCLK:
2912 case WM_LBUTTONDOWN:
2913 return LISTBOX_HandleLButtonDownCombo(wnd, descr, msg, wParam,
2914 (INT16)LOWORD(lParam),
2915 (INT16)HIWORD(lParam) );
2916 case WM_MOUSEACTIVATE:
2917 return MA_NOACTIVATE;
2918 case WM_NCACTIVATE:
2919 return FALSE;
2920 case WM_KEYDOWN:
2921 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2923 /* for some reason(?) Windows makes it possible to
2924 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2926 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2927 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2928 && (wParam == VK_DOWN || wParam == VK_UP)) )
2930 COMBO_FlipListbox( lphc, FALSE );
2931 return 0;
2934 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2936 case LB_SETCURSEL16:
2937 case LB_SETCURSEL:
2938 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2939 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2940 return lRet;
2941 case WM_NCDESTROY:
2942 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2943 lphc->hWndLBox = 0;
2944 /* fall through */
2946 default:
2947 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2950 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2952 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2954 return lRet;
2957 /***********************************************************************
2958 * ComboLBWndProc
2960 * NOTE: in Windows, winproc address of the ComboLBox is the same
2961 * as that of the Listbox.
2963 * This is just a wrapper for the real wndproc, it only does window locking
2964 * and unlocking.
2966 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2967 WPARAM wParam, LPARAM lParam )
2969 WND *wnd = WIN_FindWndPtr( hwnd );
2970 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2972 WIN_ReleaseWndPtr(wnd);
2973 return res;