Removed some HEAP_xalloc calls.
[wine/hacks.git] / controls / listbox.c
blobfa13ba7837833eeb6ffcf130f46f8125550036ed
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 HFONT font; /* Current font */
77 LCID locale; /* Current locale for string comparisons */
78 LPHEADCOMBO lphc; /* ComboLBox */
79 } LB_DESCR;
82 #define IS_OWNERDRAW(descr) \
83 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
85 #define HAS_STRINGS(descr) \
86 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
89 #define IS_MULTISELECT(descr) \
90 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
92 #define SEND_NOTIFICATION(wnd,descr,code) \
93 (SendMessageA( (descr)->owner, WM_COMMAND, \
94 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
96 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
98 /* Current timer status */
99 typedef enum
101 LB_TIMER_NONE,
102 LB_TIMER_UP,
103 LB_TIMER_LEFT,
104 LB_TIMER_DOWN,
105 LB_TIMER_RIGHT
106 } TIMER_DIRECTION;
108 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
111 /***********************************************************************
112 * LISTBOX_Dump
114 void LISTBOX_Dump( WND *wnd )
116 INT i;
117 LB_ITEMDATA *item;
118 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
120 TRACE( "Listbox:\n" );
121 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
122 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
123 descr->top_item );
124 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
126 TRACE( "%4d: %-40s %d %08lx %3d\n",
127 i, item->str, item->selected, item->data, item->height );
132 /***********************************************************************
133 * LISTBOX_GetCurrentPageSize
135 * Return the current page size
137 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
139 INT i, height;
140 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
141 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
143 if ((height += descr->items[i].height) > descr->height) break;
145 if (i == descr->top_item) return 1;
146 else return i - descr->top_item;
150 /***********************************************************************
151 * LISTBOX_GetMaxTopIndex
153 * Return the maximum possible index for the top of the listbox.
155 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
157 INT max, page;
159 if (descr->style & LBS_OWNERDRAWVARIABLE)
161 page = descr->height;
162 for (max = descr->nb_items - 1; max >= 0; max--)
163 if ((page -= descr->items[max].height) < 0) break;
164 if (max < descr->nb_items - 1) max++;
166 else if (descr->style & LBS_MULTICOLUMN)
168 if ((page = descr->width / descr->column_width) < 1) page = 1;
169 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
170 max = (max - page) * descr->page_size;
172 else
174 max = descr->nb_items - descr->page_size;
176 if (max < 0) max = 0;
177 return max;
181 /***********************************************************************
182 * LISTBOX_UpdateScroll
184 * Update the scrollbars. Should be called whenever the content
185 * of the listbox changes.
187 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
189 SCROLLINFO info;
191 /* Check the listbox scroll bar flags individually before we call
192 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
193 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
194 scroll bar when we do not need one.
195 if (!(descr->style & WS_VSCROLL)) return;
198 /* It is important that we check descr->style, and not wnd->dwStyle,
199 for WS_VSCROLL, as the former is exactly the one passed in
200 argument to CreateWindow.
201 In Windows (and from now on in Wine :) a listbox created
202 with such a style (no WS_SCROLL) does not update
203 the scrollbar with listbox-related data, thus letting
204 the programmer use it for his/her own purposes. */
206 if (descr->style & LBS_NOREDRAW) return;
207 info.cbSize = sizeof(info);
209 if (descr->style & LBS_MULTICOLUMN)
211 info.nMin = 0;
212 info.nMax = (descr->nb_items - 1) / descr->page_size;
213 info.nPos = descr->top_item / descr->page_size;
214 info.nPage = descr->width / descr->column_width;
215 if (info.nPage < 1) info.nPage = 1;
216 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
217 if (descr->style & LBS_DISABLENOSCROLL)
218 info.fMask |= SIF_DISABLENOSCROLL;
219 if (descr->style & WS_HSCROLL)
220 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
221 info.nMax = 0;
222 info.fMask = SIF_RANGE;
223 if (descr->style & WS_VSCROLL)
224 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
226 else
228 info.nMin = 0;
229 info.nMax = descr->nb_items - 1;
230 info.nPos = descr->top_item;
231 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
232 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
233 if (descr->style & LBS_DISABLENOSCROLL)
234 info.fMask |= SIF_DISABLENOSCROLL;
235 if (descr->style & WS_VSCROLL)
236 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
238 if (descr->horz_extent)
240 info.nMin = 0;
241 info.nMax = descr->horz_extent - 1;
242 info.nPos = descr->horz_pos;
243 info.nPage = descr->width;
244 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
245 if (descr->style & LBS_DISABLENOSCROLL)
246 info.fMask |= SIF_DISABLENOSCROLL;
247 if (descr->style & WS_HSCROLL)
248 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
255 /***********************************************************************
256 * LISTBOX_SetTopItem
258 * Set the top item of the listbox, scrolling up or down if necessary.
260 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
261 BOOL scroll )
263 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
264 if (index > max) index = max;
265 if (index < 0) index = 0;
266 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
267 if (descr->top_item == index) return LB_OKAY;
268 if (descr->style & LBS_MULTICOLUMN)
270 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
271 if (scroll && (abs(diff) < descr->width))
272 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
273 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
275 else
276 scroll = FALSE;
278 else if (scroll)
280 INT diff;
281 if (descr->style & LBS_OWNERDRAWVARIABLE)
283 INT i;
284 diff = 0;
285 if (index > descr->top_item)
287 for (i = index - 1; i >= descr->top_item; i--)
288 diff -= descr->items[i].height;
290 else
292 for (i = index; i < descr->top_item; i++)
293 diff += descr->items[i].height;
296 else
297 diff = (descr->top_item - index) * descr->item_height;
299 if (abs(diff) < descr->height)
300 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
301 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
302 else
303 scroll = FALSE;
305 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
306 descr->top_item = index;
307 LISTBOX_UpdateScroll( wnd, descr );
308 return LB_OKAY;
312 /***********************************************************************
313 * LISTBOX_UpdatePage
315 * Update the page size. Should be called when the size of
316 * the client area or the item height changes.
318 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
320 INT page_size;
322 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
323 page_size = 1;
324 if (page_size == descr->page_size) return;
325 descr->page_size = page_size;
326 if (descr->style & LBS_MULTICOLUMN)
327 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
328 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
332 /***********************************************************************
333 * LISTBOX_UpdateSize
335 * Update the size of the listbox. Should be called when the size of
336 * the client area changes.
338 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
340 RECT rect;
342 GetClientRect( wnd->hwndSelf, &rect );
343 descr->width = rect.right - rect.left;
344 descr->height = rect.bottom - rect.top;
345 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
347 if ((descr->height > descr->item_height) &&
348 (descr->height % descr->item_height))
350 TRACE("[%04x]: changing height %d -> %d\n",
351 wnd->hwndSelf, descr->height,
352 descr->height - descr->height%descr->item_height );
353 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
354 wnd->rectWindow.right - wnd->rectWindow.left,
355 wnd->rectWindow.bottom - wnd->rectWindow.top -
356 (descr->height % descr->item_height),
357 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
358 return;
361 TRACE("[%04x]: new size = %d,%d\n",
362 wnd->hwndSelf, descr->width, descr->height );
363 LISTBOX_UpdatePage( wnd, descr );
364 LISTBOX_UpdateScroll( wnd, descr );
368 /***********************************************************************
369 * LISTBOX_GetItemRect
371 * Get the rectangle enclosing an item, in listbox client coordinates.
372 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
374 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
375 RECT *rect )
377 /* Index <= 0 is legal even on empty listboxes */
378 if (index && (index >= descr->nb_items)) return -1;
379 SetRect( rect, 0, 0, descr->width, descr->height );
380 if (descr->style & LBS_MULTICOLUMN)
382 INT col = (index / descr->page_size) -
383 (descr->top_item / descr->page_size);
384 rect->left += col * descr->column_width;
385 rect->right = rect->left + descr->column_width;
386 rect->top += (index % descr->page_size) * descr->item_height;
387 rect->bottom = rect->top + descr->item_height;
389 else if (descr->style & LBS_OWNERDRAWVARIABLE)
391 INT i;
392 rect->right += descr->horz_pos;
393 if ((index >= 0) && (index < descr->nb_items))
395 if (index < descr->top_item)
397 for (i = descr->top_item-1; i >= index; i--)
398 rect->top -= descr->items[i].height;
400 else
402 for (i = descr->top_item; i < index; i++)
403 rect->top += descr->items[i].height;
405 rect->bottom = rect->top + descr->items[index].height;
409 else
411 rect->top += (index - descr->top_item) * descr->item_height;
412 rect->bottom = rect->top + descr->item_height;
413 rect->right += descr->horz_pos;
416 return ((rect->left < descr->width) && (rect->right > 0) &&
417 (rect->top < descr->height) && (rect->bottom > 0));
421 /***********************************************************************
422 * LISTBOX_GetItemFromPoint
424 * Return the item nearest from point (x,y) (in client coordinates).
426 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
427 INT x, INT y )
429 INT index = descr->top_item;
431 if (!descr->nb_items) return -1; /* No items */
432 if (descr->style & LBS_OWNERDRAWVARIABLE)
434 INT pos = 0;
435 if (y >= 0)
437 while (index < descr->nb_items)
439 if ((pos += descr->items[index].height) > y) break;
440 index++;
443 else
445 while (index > 0)
447 index--;
448 if ((pos -= descr->items[index].height) <= y) break;
452 else if (descr->style & LBS_MULTICOLUMN)
454 if (y >= descr->item_height * descr->page_size) return -1;
455 if (y >= 0) index += y / descr->item_height;
456 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
457 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
459 else
461 index += (y / descr->item_height);
463 if (index < 0) return 0;
464 if (index >= descr->nb_items) return -1;
465 return index;
469 /***********************************************************************
470 * LISTBOX_PaintItem
472 * Paint an item.
474 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
475 const RECT *rect, INT index, UINT action )
477 LB_ITEMDATA *item = NULL;
478 if (index < descr->nb_items) item = &descr->items[index];
480 if (IS_OWNERDRAW(descr))
482 DRAWITEMSTRUCT dis;
483 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
485 if (!item)
487 if (action == ODA_FOCUS)
488 DrawFocusRect( hdc, rect );
489 else
490 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
491 return;
493 dis.CtlType = ODT_LISTBOX;
494 dis.CtlID = id;
495 dis.hwndItem = wnd->hwndSelf;
496 dis.itemAction = action;
497 dis.hDC = hdc;
498 dis.itemID = index;
499 dis.itemState = 0;
500 if (item && item->selected) dis.itemState |= ODS_SELECTED;
501 if ((descr->focus_item == index) &&
502 (descr->caret_on) &&
503 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
504 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
505 dis.itemData = item ? item->data : 0;
506 dis.rcItem = *rect;
507 TRACE("[%04x]: drawitem %d (%s) action=%02x "
508 "state=%02x rect=%d,%d-%d,%d\n",
509 wnd->hwndSelf, index, item ? item->str : "", action,
510 dis.itemState, rect->left, rect->top,
511 rect->right, rect->bottom );
512 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
514 else
516 COLORREF oldText = 0, oldBk = 0;
518 if (action == ODA_FOCUS)
520 DrawFocusRect( hdc, rect );
521 return;
523 if (item && item->selected)
525 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
526 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
529 TRACE("[%04x]: painting %d (%s) action=%02x "
530 "rect=%d,%d-%d,%d\n",
531 wnd->hwndSelf, index, item ? item->str : "", action,
532 rect->left, rect->top, rect->right, rect->bottom );
533 if (!item)
534 ExtTextOutA( hdc, rect->left + 1, rect->top,
535 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
536 else if (!(descr->style & LBS_USETABSTOPS))
537 ExtTextOutA( hdc, rect->left + 1, rect->top,
538 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
539 strlen(item->str), NULL );
540 else
542 /* Output empty string to paint background in the full width. */
543 ExtTextOutA( hdc, rect->left + 1, rect->top,
544 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
545 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
546 item->str, strlen(item->str),
547 descr->nb_tabs, descr->tabs, 0);
549 if (item && item->selected)
551 SetBkColor( hdc, oldBk );
552 SetTextColor( hdc, oldText );
554 if ((descr->focus_item == index) &&
555 (descr->caret_on) &&
556 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
561 /***********************************************************************
562 * LISTBOX_SetRedraw
564 * Change the redraw flag.
566 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
568 if (on)
570 if (!(descr->style & LBS_NOREDRAW)) return;
571 descr->style &= ~LBS_NOREDRAW;
572 LISTBOX_UpdateScroll( wnd, descr );
574 else descr->style |= LBS_NOREDRAW;
578 /***********************************************************************
579 * LISTBOX_RepaintItem
581 * Repaint a single item synchronously.
583 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
584 UINT action )
586 HDC hdc;
587 RECT rect;
588 HFONT oldFont = 0;
589 HBRUSH hbrush, oldBrush = 0;
591 /* Do not repaint the item if the item is not visible */
592 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
594 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
595 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
596 if (descr->font) oldFont = SelectObject( hdc, descr->font );
597 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
598 hdc, (LPARAM)wnd->hwndSelf );
599 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
600 if (wnd->dwStyle & WS_DISABLED)
601 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
602 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
603 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
604 if (oldFont) SelectObject( hdc, oldFont );
605 if (oldBrush) SelectObject( hdc, oldBrush );
606 ReleaseDC( wnd->hwndSelf, hdc );
610 /***********************************************************************
611 * LISTBOX_InitStorage
613 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
614 DWORD bytes )
616 LB_ITEMDATA *item;
618 nb_items += LB_ARRAY_GRANULARITY - 1;
619 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
620 if (descr->items)
621 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
622 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
623 nb_items * sizeof(LB_ITEMDATA) )))
625 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
626 return LB_ERRSPACE;
628 descr->items = item;
629 return LB_OKAY;
633 /***********************************************************************
634 * LISTBOX_SetTabStops
636 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
637 LPINT tabs, BOOL short_ints )
639 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
640 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
641 if (!(descr->nb_tabs = count))
643 descr->tabs = NULL;
644 return TRUE;
646 /* FIXME: count = 1 */
647 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
648 descr->nb_tabs * sizeof(INT) )))
649 return FALSE;
650 if (short_ints)
652 INT i;
653 LPINT16 p = (LPINT16)tabs;
655 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
656 for (i = 0; i < descr->nb_tabs; i++) {
657 descr->tabs[i] = *p++<<1; /* FIXME */
658 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
660 if (TRACE_ON(listbox)) DPRINTF("\n");
662 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
663 /* FIXME: repaint the window? */
664 return TRUE;
668 /***********************************************************************
669 * LISTBOX_GetText
671 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
672 LPSTR buffer )
674 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
675 if (HAS_STRINGS(descr))
677 if (!buffer)
678 return strlen(descr->items[index].str);
679 lstrcpyA( buffer, descr->items[index].str );
680 return strlen(buffer);
681 } else {
682 if (buffer)
683 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
684 return sizeof(DWORD);
689 /***********************************************************************
690 * LISTBOX_FindStringPos
692 * Find the nearest string located before a given string in sort order.
693 * If 'exact' is TRUE, return an error if we don't get an exact match.
695 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
696 BOOL exact )
698 INT index, min, max, res = -1;
700 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
701 min = 0;
702 max = descr->nb_items;
703 while (min != max)
705 index = (min + max) / 2;
706 if (HAS_STRINGS(descr))
707 res = lstrcmpiA( descr->items[index].str, str );
708 else
710 COMPAREITEMSTRUCT cis;
711 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
713 cis.CtlType = ODT_LISTBOX;
714 cis.CtlID = id;
715 cis.hwndItem = wnd->hwndSelf;
716 cis.itemID1 = index;
717 cis.itemData1 = descr->items[index].data;
718 cis.itemID2 = -1;
719 cis.itemData2 = (DWORD)str;
720 cis.dwLocaleId = descr->locale;
721 res = SendMessageA( descr->owner, WM_COMPAREITEM,
722 id, (LPARAM)&cis );
724 if (!res) return index;
725 if (res > 0) max = index;
726 else min = index + 1;
728 return exact ? -1 : max;
732 /***********************************************************************
733 * LISTBOX_FindFileStrPos
735 * Find the nearest string located before a given string in directory
736 * sort order (i.e. first files, then directories, then drives).
738 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
740 INT min, max, res = -1;
742 if (!HAS_STRINGS(descr))
743 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
744 min = 0;
745 max = descr->nb_items;
746 while (min != max)
748 INT index = (min + max) / 2;
749 const char *p = descr->items[index].str;
750 if (*p == '[') /* drive or directory */
752 if (*str != '[') res = -1;
753 else if (p[1] == '-') /* drive */
755 if (str[1] == '-') res = str[2] - p[2];
756 else res = -1;
758 else /* directory */
760 if (str[1] == '-') res = 1;
761 else res = lstrcmpiA( str, p );
764 else /* filename */
766 if (*str == '[') res = 1;
767 else res = lstrcmpiA( str, p );
769 if (!res) return index;
770 if (res < 0) max = index;
771 else min = index + 1;
773 return max;
777 /***********************************************************************
778 * LISTBOX_FindString
780 * Find the item beginning with a given string.
782 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
783 LPCSTR str, BOOL exact )
785 INT i;
786 LB_ITEMDATA *item;
788 if (start >= descr->nb_items) start = -1;
789 item = descr->items + start + 1;
790 if (HAS_STRINGS(descr))
792 if (!str || ! str[0] ) return LB_ERR;
793 if (exact)
795 for (i = start + 1; i < descr->nb_items; i++, item++)
796 if (!lstrcmpiA( str, item->str )) return i;
797 for (i = 0, item = descr->items; i <= start; i++, item++)
798 if (!lstrcmpiA( str, item->str )) return i;
800 else
802 /* Special case for drives and directories: ignore prefix */
803 #define CHECK_DRIVE(item) \
804 if ((item)->str[0] == '[') \
806 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
807 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
808 return i; \
811 INT len = strlen(str);
812 for (i = start + 1; i < descr->nb_items; i++, item++)
814 if (!lstrncmpiA( str, item->str, len )) return i;
815 CHECK_DRIVE(item);
817 for (i = 0, item = descr->items; i <= start; i++, item++)
819 if (!lstrncmpiA( str, item->str, len )) return i;
820 CHECK_DRIVE(item);
822 #undef CHECK_DRIVE
825 else
827 if (exact && (descr->style & LBS_SORT))
828 /* If sorted, use a WM_COMPAREITEM binary search */
829 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
831 /* Otherwise use a linear search */
832 for (i = start + 1; i < descr->nb_items; i++, item++)
833 if (item->data == (DWORD)str) return i;
834 for (i = 0, item = descr->items; i <= start; i++, item++)
835 if (item->data == (DWORD)str) return i;
837 return LB_ERR;
841 /***********************************************************************
842 * LISTBOX_GetSelCount
844 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
846 INT i, count;
847 LB_ITEMDATA *item = descr->items;
849 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
850 for (i = count = 0; i < descr->nb_items; i++, item++)
851 if (item->selected) count++;
852 return count;
856 /***********************************************************************
857 * LISTBOX_GetSelItems16
859 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
860 LPINT16 array )
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) && (count < max); i++, item++)
867 if (item->selected) array[count++] = (INT16)i;
868 return count;
872 /***********************************************************************
873 * LISTBOX_GetSelItems32
875 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
876 LPINT 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++] = i;
884 return count;
888 /***********************************************************************
889 * LISTBOX_Paint
891 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
893 INT i, col_pos = descr->page_size - 1;
894 RECT rect;
895 HFONT oldFont = 0;
896 HBRUSH hbrush, oldBrush = 0;
898 SetRect( &rect, 0, 0, descr->width, descr->height );
899 if (descr->style & LBS_NOREDRAW) return 0;
900 if (descr->style & LBS_MULTICOLUMN)
901 rect.right = rect.left + descr->column_width;
902 else if (descr->horz_pos)
904 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
905 rect.right += descr->horz_pos;
908 if (descr->font) oldFont = SelectObject( hdc, descr->font );
909 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
910 hdc, (LPARAM)wnd->hwndSelf );
911 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
912 if (wnd->dwStyle & WS_DISABLED)
913 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
915 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
916 (GetFocus() == wnd->hwndSelf))
918 /* Special case for empty listbox: paint focus rect */
919 rect.bottom = rect.top + descr->item_height;
920 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
921 ODA_FOCUS );
922 rect.top = rect.bottom;
925 for (i = descr->top_item; i < descr->nb_items; i++)
927 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
928 rect.bottom = rect.top + descr->item_height;
929 else
930 rect.bottom = rect.top + descr->items[i].height;
932 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
933 rect.top = rect.bottom;
935 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
937 if (!IS_OWNERDRAW(descr))
939 /* Clear the bottom of the column */
940 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
941 if (rect.top < descr->height)
943 rect.bottom = descr->height;
944 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
945 &rect, NULL, 0, NULL );
949 /* Go to the next column */
950 rect.left += descr->column_width;
951 rect.right += descr->column_width;
952 rect.top = 0;
953 col_pos = descr->page_size - 1;
955 else
957 col_pos--;
958 if (rect.top >= descr->height) break;
962 if (!IS_OWNERDRAW(descr))
964 /* Clear the remainder of the client area */
965 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
966 if (rect.top < descr->height)
968 rect.bottom = descr->height;
969 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
970 &rect, NULL, 0, NULL );
972 if (rect.right < descr->width)
974 rect.left = rect.right;
975 rect.right = descr->width;
976 rect.top = 0;
977 rect.bottom = descr->height;
978 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
979 &rect, NULL, 0, NULL );
982 if (oldFont) SelectObject( hdc, oldFont );
983 if (oldBrush) SelectObject( hdc, oldBrush );
984 return 0;
988 /***********************************************************************
989 * LISTBOX_InvalidateItems
991 * Invalidate all items from a given item. If the specified item is not
992 * visible, nothing happens.
994 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
996 RECT rect;
998 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
1000 rect.bottom = descr->height;
1001 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1002 if (descr->style & LBS_MULTICOLUMN)
1004 /* Repaint the other columns */
1005 rect.left = rect.right;
1006 rect.right = descr->width;
1007 rect.top = 0;
1008 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1014 /***********************************************************************
1015 * LISTBOX_GetItemHeight
1017 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1019 if (descr->style & LBS_OWNERDRAWVARIABLE)
1021 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1022 return descr->items[index].height;
1024 else return descr->item_height;
1028 /***********************************************************************
1029 * LISTBOX_SetItemHeight
1031 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1032 UINT height )
1034 if (!height) height = 1;
1036 if (descr->style & LBS_OWNERDRAWVARIABLE)
1038 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1039 TRACE("[%04x]: item %d height = %d\n",
1040 wnd->hwndSelf, index, height );
1041 descr->items[index].height = height;
1042 LISTBOX_UpdateScroll( wnd, descr );
1043 LISTBOX_InvalidateItems( wnd, descr, index );
1045 else if (height != descr->item_height)
1047 TRACE("[%04x]: new height = %d\n",
1048 wnd->hwndSelf, height );
1049 descr->item_height = height;
1050 LISTBOX_UpdatePage( wnd, descr );
1051 LISTBOX_UpdateScroll( wnd, descr );
1052 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1054 return LB_OKAY;
1058 /***********************************************************************
1059 * LISTBOX_SetHorizontalPos
1061 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1063 INT diff;
1065 if (pos > descr->horz_extent - descr->width)
1066 pos = descr->horz_extent - descr->width;
1067 if (pos < 0) pos = 0;
1068 if (!(diff = descr->horz_pos - pos)) return;
1069 TRACE("[%04x]: new horz pos = %d\n",
1070 wnd->hwndSelf, pos );
1071 descr->horz_pos = pos;
1072 LISTBOX_UpdateScroll( wnd, descr );
1073 if (abs(diff) < descr->width)
1074 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1075 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1076 else
1077 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1081 /***********************************************************************
1082 * LISTBOX_SetHorizontalExtent
1084 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1085 UINT extent )
1087 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1088 return LB_OKAY;
1089 if (extent <= 0) extent = 1;
1090 if (extent == descr->horz_extent) return LB_OKAY;
1091 TRACE("[%04x]: new horz extent = %d\n",
1092 wnd->hwndSelf, extent );
1093 descr->horz_extent = extent;
1094 if (descr->horz_pos > extent - descr->width)
1095 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1096 else
1097 LISTBOX_UpdateScroll( wnd, descr );
1098 return LB_OKAY;
1102 /***********************************************************************
1103 * LISTBOX_SetColumnWidth
1105 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1107 if (width == descr->column_width) return LB_OKAY;
1108 TRACE("[%04x]: new column width = %d\n",
1109 wnd->hwndSelf, width );
1110 descr->column_width = width;
1111 LISTBOX_UpdatePage( wnd, descr );
1112 return LB_OKAY;
1116 /***********************************************************************
1117 * LISTBOX_SetFont
1119 * Returns the item height.
1121 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1123 HDC hdc;
1124 HFONT oldFont = 0;
1125 TEXTMETRICA tm;
1127 descr->font = font;
1129 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1131 ERR("unable to get DC.\n" );
1132 return 16;
1134 if (font) oldFont = SelectObject( hdc, font );
1135 GetTextMetricsA( hdc, &tm );
1136 if (oldFont) SelectObject( hdc, oldFont );
1137 ReleaseDC( wnd->hwndSelf, hdc );
1138 if (!IS_OWNERDRAW(descr))
1139 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1140 return tm.tmHeight ;
1144 /***********************************************************************
1145 * LISTBOX_MakeItemVisible
1147 * Make sure that a given item is partially or fully visible.
1149 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1150 BOOL fully )
1152 INT top;
1154 if (index <= descr->top_item) top = index;
1155 else if (descr->style & LBS_MULTICOLUMN)
1157 INT cols = descr->width;
1158 if (!fully) cols += descr->column_width - 1;
1159 if (cols >= descr->column_width) cols /= descr->column_width;
1160 else cols = 1;
1161 if (index < descr->top_item + (descr->page_size * cols)) return;
1162 top = index - descr->page_size * (cols - 1);
1164 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1166 INT height = fully ? descr->items[index].height : 1;
1167 for (top = index; top > descr->top_item; top--)
1168 if ((height += descr->items[top-1].height) > descr->height) break;
1170 else
1172 if (index < descr->top_item + descr->page_size) return;
1173 if (!fully && (index == descr->top_item + descr->page_size) &&
1174 (descr->height > (descr->page_size * descr->item_height))) return;
1175 top = index - descr->page_size + 1;
1177 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1181 /***********************************************************************
1182 * LISTBOX_SelectItemRange
1184 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1186 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1187 INT last, BOOL on )
1189 INT i;
1191 /* A few sanity checks */
1193 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1194 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1195 if (last == -1) last = descr->nb_items - 1;
1196 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1197 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1198 /* selected_item reflects last selected/unselected item on multiple sel */
1199 descr->selected_item = last;
1201 if (on) /* Turn selection on */
1203 for (i = first; i <= last; i++)
1205 if (descr->items[i].selected) continue;
1206 descr->items[i].selected = TRUE;
1207 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1210 else /* Turn selection off */
1212 for (i = first; i <= last; i++)
1214 if (!descr->items[i].selected) continue;
1215 descr->items[i].selected = FALSE;
1216 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1219 return LB_OKAY;
1223 /***********************************************************************
1224 * LISTBOX_SetCaretIndex
1226 * NOTES
1227 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1230 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1231 BOOL fully_visible )
1233 INT oldfocus = descr->focus_item;
1235 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1236 if (index == oldfocus) return LB_OKAY;
1237 descr->focus_item = index;
1238 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1239 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1241 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1242 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1243 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1245 return LB_OKAY;
1249 /***********************************************************************
1250 * LISTBOX_SetSelection
1252 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1253 BOOL on, BOOL send_notify )
1255 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1257 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1258 if (descr->style & LBS_MULTIPLESEL)
1260 if (index == -1) /* Select all items */
1261 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1262 else /* Only one item */
1263 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1265 else
1267 INT oldsel = descr->selected_item;
1268 if (index == oldsel) return LB_OKAY;
1269 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1270 if (index != -1) descr->items[index].selected = TRUE;
1271 descr->selected_item = index;
1272 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT );
1273 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1274 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1275 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1276 else
1277 if( descr->lphc ) /* set selection change flag for parent combo */
1278 descr->lphc->wState |= CBF_SELCHANGE;
1280 return LB_OKAY;
1284 /***********************************************************************
1285 * LISTBOX_MoveCaret
1287 * Change the caret position and extend the selection to the new caret.
1289 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1290 BOOL fully_visible )
1292 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1293 if (descr->style & LBS_EXTENDEDSEL)
1295 if (descr->anchor_item != -1)
1297 INT first = min( descr->focus_item, descr->anchor_item );
1298 INT last = max( descr->focus_item, descr->anchor_item );
1299 if (first > 0)
1300 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1301 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1302 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1305 else if (!(descr->style & LBS_MULTIPLESEL))
1307 /* Set selection to new caret item */
1308 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1313 /***********************************************************************
1314 * LISTBOX_InsertItem
1316 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1317 LPSTR str, DWORD data )
1319 LB_ITEMDATA *item;
1320 INT max_items;
1321 INT oldfocus = descr->focus_item;
1323 if (index == -1) index = descr->nb_items;
1324 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1325 if (!descr->items) max_items = 0;
1326 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1327 if (descr->nb_items == max_items)
1329 /* We need to grow the array */
1330 max_items += LB_ARRAY_GRANULARITY;
1331 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1332 max_items * sizeof(LB_ITEMDATA) )))
1334 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1335 return LB_ERRSPACE;
1337 descr->items = item;
1340 /* Insert the item structure */
1342 item = &descr->items[index];
1343 if (index < descr->nb_items)
1344 RtlMoveMemory( item + 1, item,
1345 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1346 item->str = str;
1347 item->data = data;
1348 item->height = 0;
1349 item->selected = FALSE;
1350 descr->nb_items++;
1352 /* Get item height */
1354 if (descr->style & LBS_OWNERDRAWVARIABLE)
1356 MEASUREITEMSTRUCT mis;
1357 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1359 mis.CtlType = ODT_LISTBOX;
1360 mis.CtlID = id;
1361 mis.itemID = index;
1362 mis.itemData = descr->items[index].data;
1363 mis.itemHeight = descr->item_height;
1364 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1365 item->height = mis.itemHeight ? mis.itemHeight : 1;
1366 TRACE("[%04x]: measure item %d (%s) = %d\n",
1367 wnd->hwndSelf, index, str ? str : "", item->height );
1370 /* Repaint the items */
1372 LISTBOX_UpdateScroll( wnd, descr );
1373 LISTBOX_InvalidateItems( wnd, descr, index );
1375 /* Move selection and focused item */
1376 /* If listbox was empty, set focus to the first item */
1377 if (descr->nb_items == 1)
1378 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1379 /* single select don't change selection index in win31 */
1380 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1382 descr->selected_item++;
1383 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1385 else
1387 if (index <= descr->selected_item)
1389 descr->selected_item++;
1390 descr->focus_item = oldfocus; /* focus not changed */
1393 return LB_OKAY;
1397 /***********************************************************************
1398 * LISTBOX_InsertString
1400 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1401 LPCSTR str )
1403 LPSTR new_str = NULL;
1404 DWORD data = 0;
1405 LRESULT ret;
1407 if (HAS_STRINGS(descr))
1409 if (!str) str="";
1410 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1412 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1413 return LB_ERRSPACE;
1416 else data = (DWORD)str;
1418 if (index == -1) index = descr->nb_items;
1419 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1421 if (new_str) HeapFree( descr->heap, 0, new_str );
1422 return ret;
1425 TRACE("[%04x]: added item %d '%s'\n",
1426 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1427 return index;
1431 /***********************************************************************
1432 * LISTBOX_DeleteItem
1434 * Delete the content of an item. 'index' must be a valid index.
1436 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1438 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1439 * while Win95 sends it for all items with user data.
1440 * It's probably better to send it too often than not
1441 * often enough, so this is what we do here.
1443 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1445 DELETEITEMSTRUCT dis;
1446 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1448 dis.CtlType = ODT_LISTBOX;
1449 dis.CtlID = id;
1450 dis.itemID = index;
1451 dis.hwndItem = wnd->hwndSelf;
1452 dis.itemData = descr->items[index].data;
1453 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1455 if (HAS_STRINGS(descr) && descr->items[index].str)
1456 HeapFree( descr->heap, 0, descr->items[index].str );
1460 /***********************************************************************
1461 * LISTBOX_RemoveItem
1463 * Remove an item from the listbox and delete its content.
1465 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1467 LB_ITEMDATA *item;
1468 INT max_items;
1470 if (index == -1) index = descr->nb_items - 1;
1471 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1473 /* We need to invalidate the original rect instead of the updated one. */
1474 LISTBOX_InvalidateItems( wnd, descr, index );
1476 LISTBOX_DeleteItem( wnd, descr, index );
1478 /* Remove the item */
1480 item = &descr->items[index];
1481 if (index < descr->nb_items-1)
1482 RtlMoveMemory( item, item + 1,
1483 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1484 descr->nb_items--;
1485 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1487 /* Shrink the item array if possible */
1489 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1490 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1492 max_items -= LB_ARRAY_GRANULARITY;
1493 item = HeapReAlloc( descr->heap, 0, descr->items,
1494 max_items * sizeof(LB_ITEMDATA) );
1495 if (item) descr->items = item;
1497 /* Repaint the items */
1499 LISTBOX_UpdateScroll( wnd, descr );
1500 /* if we removed the scrollbar, reset the top of the list
1501 (correct for owner-drawn ???) */
1502 if (descr->nb_items == descr->page_size)
1503 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1505 /* Move selection and focused item */
1506 if (!IS_MULTISELECT(descr))
1508 if (index == descr->selected_item)
1509 descr->selected_item = -1;
1510 else if (index < descr->selected_item)
1512 descr->selected_item--;
1513 if (ISWIN31) /* win 31 do not change the selected item number */
1514 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1518 if (descr->focus_item >= descr->nb_items)
1520 descr->focus_item = descr->nb_items - 1;
1521 if (descr->focus_item < 0) descr->focus_item = 0;
1523 return LB_OKAY;
1527 /***********************************************************************
1528 * LISTBOX_ResetContent
1530 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1532 INT i;
1534 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1535 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1536 descr->nb_items = 0;
1537 descr->top_item = 0;
1538 descr->selected_item = -1;
1539 descr->focus_item = 0;
1540 descr->anchor_item = -1;
1541 descr->items = NULL;
1542 LISTBOX_UpdateScroll( wnd, descr );
1543 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1547 /***********************************************************************
1548 * LISTBOX_SetCount
1550 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1552 LRESULT ret;
1554 if (HAS_STRINGS(descr)) return LB_ERR;
1555 /* FIXME: this is far from optimal... */
1556 if (count > descr->nb_items)
1558 while (count > descr->nb_items)
1559 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1560 return ret;
1562 else if (count < descr->nb_items)
1564 while (count < descr->nb_items)
1565 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1566 return ret;
1568 return LB_OKAY;
1572 /***********************************************************************
1573 * LISTBOX_Directory
1575 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1576 LPCSTR filespec, BOOL long_names )
1578 HANDLE handle;
1579 LRESULT ret = LB_OKAY;
1580 WIN32_FIND_DATAA entry;
1581 int pos;
1583 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1585 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1587 else
1591 char buffer[270];
1592 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1594 if (!(attrib & DDL_DIRECTORY) ||
1595 !strcmp( entry.cAlternateFileName, "." )) continue;
1596 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1597 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1599 else /* not a directory */
1601 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1602 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1604 if ((attrib & DDL_EXCLUSIVE) &&
1605 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1606 continue;
1607 #undef ATTRIBS
1608 if (long_names) strcpy( buffer, entry.cFileName );
1609 else strcpy( buffer, entry.cAlternateFileName );
1611 if (!long_names) CharLowerA( buffer );
1612 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1613 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1614 break;
1615 } while (FindNextFileA( handle, &entry ));
1616 FindClose( handle );
1619 if ((ret >= 0) && (attrib & DDL_DRIVES))
1621 char buffer[] = "[-a-]";
1622 int drive;
1623 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1625 if (!DRIVE_IsValid(drive)) continue;
1626 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1627 break;
1630 return ret;
1634 /***********************************************************************
1635 * LISTBOX_HandleVScroll
1637 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1638 WPARAM wParam, LPARAM lParam )
1640 SCROLLINFO info;
1642 if (descr->style & LBS_MULTICOLUMN) return 0;
1643 switch(LOWORD(wParam))
1645 case SB_LINEUP:
1646 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1647 break;
1648 case SB_LINEDOWN:
1649 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1650 break;
1651 case SB_PAGEUP:
1652 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1653 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1654 break;
1655 case SB_PAGEDOWN:
1656 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1657 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1658 break;
1659 case SB_THUMBPOSITION:
1660 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1661 break;
1662 case SB_THUMBTRACK:
1663 info.cbSize = sizeof(info);
1664 info.fMask = SIF_TRACKPOS;
1665 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1666 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1667 break;
1668 case SB_TOP:
1669 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1670 break;
1671 case SB_BOTTOM:
1672 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1673 break;
1675 return 0;
1679 /***********************************************************************
1680 * LISTBOX_HandleHScroll
1682 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1683 WPARAM wParam, LPARAM lParam )
1685 SCROLLINFO info;
1686 INT page;
1688 if (descr->style & LBS_MULTICOLUMN)
1690 switch(LOWORD(wParam))
1692 case SB_LINELEFT:
1693 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1694 TRUE );
1695 break;
1696 case SB_LINERIGHT:
1697 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1698 TRUE );
1699 break;
1700 case SB_PAGELEFT:
1701 page = descr->width / descr->column_width;
1702 if (page < 1) page = 1;
1703 LISTBOX_SetTopItem( wnd, descr,
1704 descr->top_item - page * descr->page_size, TRUE );
1705 break;
1706 case SB_PAGERIGHT:
1707 page = descr->width / descr->column_width;
1708 if (page < 1) page = 1;
1709 LISTBOX_SetTopItem( wnd, descr,
1710 descr->top_item + page * descr->page_size, TRUE );
1711 break;
1712 case SB_THUMBPOSITION:
1713 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1714 TRUE );
1715 break;
1716 case SB_THUMBTRACK:
1717 info.cbSize = sizeof(info);
1718 info.fMask = SIF_TRACKPOS;
1719 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1720 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1721 TRUE );
1722 break;
1723 case SB_LEFT:
1724 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1725 break;
1726 case SB_RIGHT:
1727 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1728 break;
1731 else if (descr->horz_extent)
1733 switch(LOWORD(wParam))
1735 case SB_LINELEFT:
1736 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1737 break;
1738 case SB_LINERIGHT:
1739 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1740 break;
1741 case SB_PAGELEFT:
1742 LISTBOX_SetHorizontalPos( wnd, descr,
1743 descr->horz_pos - descr->width );
1744 break;
1745 case SB_PAGERIGHT:
1746 LISTBOX_SetHorizontalPos( wnd, descr,
1747 descr->horz_pos + descr->width );
1748 break;
1749 case SB_THUMBPOSITION:
1750 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1751 break;
1752 case SB_THUMBTRACK:
1753 info.cbSize = sizeof(info);
1754 info.fMask = SIF_TRACKPOS;
1755 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1756 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1757 break;
1758 case SB_LEFT:
1759 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1760 break;
1761 case SB_RIGHT:
1762 LISTBOX_SetHorizontalPos( wnd, descr,
1763 descr->horz_extent - descr->width );
1764 break;
1767 return 0;
1770 static LRESULT LISTBOX_HandleMouseWheel(WND *wnd, LB_DESCR *descr,WPARAM wParam, LPARAM lParam )
1772 short gcWheelDelta = 0;
1773 UINT pulScrollLines = 3;
1775 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1777 gcWheelDelta -= (short) HIWORD(wParam);
1779 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1781 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1782 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1783 LISTBOX_SetTopItem( wnd, descr, descr->top_item + cLineScroll, TRUE );
1785 return 0;
1788 /***********************************************************************
1789 * LISTBOX_HandleLButtonDown
1791 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1792 WPARAM wParam, INT x, INT y )
1794 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1795 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1796 wnd->hwndSelf, x, y, index );
1797 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1798 if (index != -1)
1800 if (descr->style & LBS_EXTENDEDSEL)
1802 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1803 if (wParam & MK_CONTROL)
1805 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1806 LISTBOX_SetSelection( wnd, descr, index,
1807 !descr->items[index].selected,
1808 (descr->style & LBS_NOTIFY) != 0);
1810 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1812 else
1814 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1815 LISTBOX_SetSelection( wnd, descr, index,
1816 (!(descr->style & LBS_MULTIPLESEL) ||
1817 !descr->items[index].selected),
1818 (descr->style & LBS_NOTIFY) != 0 );
1822 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1823 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1824 : descr->lphc->self->hwndSelf ) ;
1826 descr->captured = TRUE;
1827 SetCapture( wnd->hwndSelf );
1828 if (index != -1 && !descr->lphc)
1830 if (descr->style & LBS_NOTIFY )
1831 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1832 MAKELPARAM( x, y ) );
1833 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1835 POINT pt;
1837 pt.x = x;
1838 pt.y = y;
1840 if (DragDetect( wnd->hwndSelf, pt ))
1841 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1844 return 0;
1848 /*************************************************************************
1849 * LISTBOX_HandleLButtonDownCombo [Internal]
1851 * Process LButtonDown message for the ComboListBox
1853 * PARAMS
1854 * pWnd [I] The windows internal structure
1855 * pDescr [I] The ListBox internal structure
1856 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1857 * x [I] X Mouse Coordinate
1858 * y [I] Y Mouse Coordinate
1860 * RETURNS
1861 * 0 since we are processing the WM_LBUTTONDOWN Message
1863 * NOTES
1864 * This function is only to be used when a ListBox is a ComboListBox
1867 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1868 WPARAM wParam, INT x, INT y)
1870 RECT clientRect, screenRect;
1871 POINT mousePos;
1873 mousePos.x = x;
1874 mousePos.y = y;
1876 GetClientRect(pWnd->hwndSelf, &clientRect);
1878 if(PtInRect(&clientRect, mousePos))
1880 /* MousePos is in client, resume normal processing */
1881 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1883 else
1885 POINT screenMousePos;
1886 HWND hWndOldCapture;
1888 /* Check the Non-Client Area */
1889 screenMousePos = mousePos;
1890 hWndOldCapture = GetCapture();
1891 ReleaseCapture();
1892 GetWindowRect(pWnd->hwndSelf, &screenRect);
1893 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1895 if(!PtInRect(&screenRect, screenMousePos))
1897 /* Close The Drop Down */
1898 SEND_NOTIFICATION( pWnd, pDescr, LBN_SELCANCEL );
1899 return 0;
1901 else
1903 /* Check to see the NC is a scrollbar */
1904 INT nHitTestType=0;
1905 /* Check Vertical scroll bar */
1906 if (pWnd->dwStyle & WS_VSCROLL)
1908 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1909 if (PtInRect( &clientRect, mousePos ))
1911 nHitTestType = HTVSCROLL;
1914 /* Check horizontal scroll bar */
1915 if (pWnd->dwStyle & WS_HSCROLL)
1917 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1918 if (PtInRect( &clientRect, mousePos ))
1920 nHitTestType = HTHSCROLL;
1923 /* Windows sends this message when a scrollbar is clicked
1926 if(nHitTestType != 0)
1928 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1929 MAKELONG(screenMousePos.x, screenMousePos.y));
1931 /* Resume the Capture after scrolling is complete
1933 if(hWndOldCapture != 0)
1935 SetCapture(hWndOldCapture);
1939 return 0;
1942 /***********************************************************************
1943 * LISTBOX_HandleLButtonUp
1945 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1947 if (LISTBOX_Timer != LB_TIMER_NONE)
1948 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1949 LISTBOX_Timer = LB_TIMER_NONE;
1950 if (descr->captured)
1952 descr->captured = FALSE;
1953 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1954 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
1955 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1957 return 0;
1961 /***********************************************************************
1962 * LISTBOX_HandleTimer
1964 * Handle scrolling upon a timer event.
1965 * Return TRUE if scrolling should continue.
1967 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1968 INT index, TIMER_DIRECTION dir )
1970 switch(dir)
1972 case LB_TIMER_UP:
1973 if (descr->top_item) index = descr->top_item - 1;
1974 else index = 0;
1975 break;
1976 case LB_TIMER_LEFT:
1977 if (descr->top_item) index -= descr->page_size;
1978 break;
1979 case LB_TIMER_DOWN:
1980 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1981 if (index == descr->focus_item) index++;
1982 if (index >= descr->nb_items) index = descr->nb_items - 1;
1983 break;
1984 case LB_TIMER_RIGHT:
1985 if (index + descr->page_size < descr->nb_items)
1986 index += descr->page_size;
1987 break;
1988 case LB_TIMER_NONE:
1989 break;
1991 if (index == descr->focus_item) return FALSE;
1992 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1993 return TRUE;
1997 /***********************************************************************
1998 * LISTBOX_HandleSystemTimer
2000 * WM_SYSTIMER handler.
2002 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
2004 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
2006 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2007 LISTBOX_Timer = LB_TIMER_NONE;
2009 return 0;
2013 /***********************************************************************
2014 * LISTBOX_HandleMouseMove
2016 * WM_MOUSEMOVE handler.
2018 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
2019 INT x, INT y )
2021 INT index;
2022 TIMER_DIRECTION dir;
2024 if (!descr->captured) return;
2026 if (descr->style & LBS_MULTICOLUMN)
2028 if (y < 0) y = 0;
2029 else if (y >= descr->item_height * descr->page_size)
2030 y = descr->item_height * descr->page_size - 1;
2032 if (x < 0)
2034 dir = LB_TIMER_LEFT;
2035 x = 0;
2037 else if (x >= descr->width)
2039 dir = LB_TIMER_RIGHT;
2040 x = descr->width - 1;
2042 else dir = LB_TIMER_NONE; /* inside */
2044 else
2046 if (y < 0) dir = LB_TIMER_UP; /* above */
2047 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2048 else dir = LB_TIMER_NONE; /* inside */
2051 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2052 if (index == -1) index = descr->focus_item;
2053 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2055 /* Start/stop the system timer */
2057 if (dir != LB_TIMER_NONE)
2058 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2059 else if (LISTBOX_Timer != LB_TIMER_NONE)
2060 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2061 LISTBOX_Timer = dir;
2065 /***********************************************************************
2066 * LISTBOX_HandleKeyDown
2068 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2070 INT caret = -1;
2071 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2072 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2073 bForceSelection = FALSE; /* only for single select list */
2075 if (descr->style & LBS_WANTKEYBOARDINPUT)
2077 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2078 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2079 wnd->hwndSelf );
2080 if (caret == -2) return 0;
2082 if (caret == -1) switch(wParam)
2084 case VK_LEFT:
2085 if (descr->style & LBS_MULTICOLUMN)
2087 bForceSelection = FALSE;
2088 if (descr->focus_item >= descr->page_size)
2089 caret = descr->focus_item - descr->page_size;
2090 break;
2092 /* fall through */
2093 case VK_UP:
2094 caret = descr->focus_item - 1;
2095 if (caret < 0) caret = 0;
2096 break;
2097 case VK_RIGHT:
2098 if (descr->style & LBS_MULTICOLUMN)
2100 bForceSelection = FALSE;
2101 if (descr->focus_item + descr->page_size < descr->nb_items)
2102 caret = descr->focus_item + descr->page_size;
2103 break;
2105 /* fall through */
2106 case VK_DOWN:
2107 caret = descr->focus_item + 1;
2108 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2109 break;
2111 case VK_PRIOR:
2112 if (descr->style & LBS_MULTICOLUMN)
2114 INT page = descr->width / descr->column_width;
2115 if (page < 1) page = 1;
2116 caret = descr->focus_item - (page * descr->page_size) + 1;
2118 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2119 if (caret < 0) caret = 0;
2120 break;
2121 case VK_NEXT:
2122 if (descr->style & LBS_MULTICOLUMN)
2124 INT page = descr->width / descr->column_width;
2125 if (page < 1) page = 1;
2126 caret = descr->focus_item + (page * descr->page_size) - 1;
2128 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2129 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2130 break;
2131 case VK_HOME:
2132 caret = 0;
2133 break;
2134 case VK_END:
2135 caret = descr->nb_items - 1;
2136 break;
2137 case VK_SPACE:
2138 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2139 else if (descr->style & LBS_MULTIPLESEL)
2141 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2142 !descr->items[descr->focus_item].selected,
2143 (descr->style & LBS_NOTIFY) != 0 );
2145 break;
2146 default:
2147 bForceSelection = FALSE;
2149 if (bForceSelection) /* focused item is used instead of key */
2150 caret = descr->focus_item;
2151 if (caret >= 0)
2153 if ((descr->style & LBS_EXTENDEDSEL) &&
2154 !(GetKeyState( VK_SHIFT ) & 0x8000))
2155 descr->anchor_item = caret;
2156 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2157 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2158 if (descr->style & LBS_NOTIFY)
2160 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2162 /* make sure that combo parent doesn't hide us */
2163 descr->lphc->wState |= CBF_NOROLLUP;
2165 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2168 return 0;
2172 /***********************************************************************
2173 * LISTBOX_HandleChar
2175 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2176 WPARAM wParam )
2178 INT caret = -1;
2179 char str[2];
2181 str[0] = wParam & 0xff;
2182 str[1] = '\0';
2184 if (descr->style & LBS_WANTKEYBOARDINPUT)
2186 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2187 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2188 wnd->hwndSelf );
2189 if (caret == -2) return 0;
2191 if (caret == -1)
2192 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2193 if (caret != -1)
2195 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2196 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2197 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2198 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2199 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2201 return 0;
2205 /***********************************************************************
2206 * LISTBOX_Create
2208 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2210 LB_DESCR *descr;
2211 MEASUREITEMSTRUCT mis;
2212 RECT rect;
2214 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2215 return FALSE;
2216 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2218 HeapFree( GetProcessHeap(), 0, descr );
2219 return FALSE;
2221 GetClientRect( wnd->hwndSelf, &rect );
2222 descr->owner = GetParent( wnd->hwndSelf );
2223 descr->style = wnd->dwStyle;
2224 descr->width = rect.right - rect.left;
2225 descr->height = rect.bottom - rect.top;
2226 descr->items = NULL;
2227 descr->nb_items = 0;
2228 descr->top_item = 0;
2229 descr->selected_item = -1;
2230 descr->focus_item = 0;
2231 descr->anchor_item = -1;
2232 descr->item_height = 1;
2233 descr->page_size = 1;
2234 descr->column_width = 150;
2235 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2236 descr->horz_pos = 0;
2237 descr->nb_tabs = 0;
2238 descr->tabs = NULL;
2239 descr->caret_on = TRUE;
2240 descr->captured = FALSE;
2241 descr->font = 0;
2242 descr->locale = 0; /* FIXME */
2243 descr->lphc = lphc;
2245 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2246 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2248 /* Win95 document "List Box Differences" from MSDN:
2249 If a list box in a version 3.x application has either the
2250 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2251 horizontal and vertical scroll bars.
2253 descr->style |= WS_VSCROLL | WS_HSCROLL;
2256 if( lphc )
2258 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2259 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2260 descr->owner = lphc->self->hwndSelf;
2263 *(LB_DESCR **)wnd->wExtra = descr;
2265 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2267 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2268 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2269 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2270 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2272 if (descr->style & LBS_OWNERDRAWFIXED)
2274 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2276 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2277 descr->item_height = lphc->fixedOwnerDrawHeight;
2279 else
2281 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2283 mis.CtlType = ODT_LISTBOX;
2284 mis.CtlID = id;
2285 mis.itemID = -1;
2286 mis.itemWidth = 0;
2287 mis.itemData = 0;
2288 mis.itemHeight = descr->item_height;
2289 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2290 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2294 return TRUE;
2298 /***********************************************************************
2299 * LISTBOX_Destroy
2301 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2303 LISTBOX_ResetContent( wnd, descr );
2304 HeapDestroy( descr->heap );
2305 HeapFree( GetProcessHeap(), 0, descr );
2306 wnd->wExtra[0] = 0;
2307 return TRUE;
2311 /***********************************************************************
2312 * ListBoxWndProc
2314 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2315 WPARAM wParam, LPARAM lParam )
2317 LRESULT ret;
2318 LB_DESCR *descr;
2319 HWND hwnd = wnd->hwndSelf;
2321 if (!wnd) return 0;
2322 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2324 switch (msg)
2326 case WM_CREATE:
2328 if (!LISTBOX_Create( wnd, NULL ))
2329 return -1;
2330 TRACE("creating wnd=%04x descr=%p\n",
2331 hwnd, *(LB_DESCR **)wnd->wExtra );
2332 return 0;
2334 case WM_NCCREATE:
2337 * When a listbox is not in a combobox and the look
2338 * is win95, the WS_BORDER style is replaced with
2339 * the WS_EX_CLIENTEDGE style.
2341 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2342 (wnd->dwStyle & WS_BORDER) )
2344 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2345 wnd->dwStyle &= ~ WS_BORDER;
2350 /* Ignore all other messages before we get a WM_CREATE */
2351 return DefWindowProcA( hwnd, msg, wParam, lParam );
2354 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2355 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2356 switch(msg)
2358 case LB_RESETCONTENT16:
2359 case LB_RESETCONTENT:
2360 LISTBOX_ResetContent( wnd, descr );
2361 return 0;
2363 case LB_ADDSTRING16:
2364 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2365 /* fall through */
2366 case LB_ADDSTRING:
2367 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2368 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2370 case LB_INSERTSTRING16:
2371 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2372 wParam = (INT)(INT16)wParam;
2373 /* fall through */
2374 case LB_INSERTSTRING:
2375 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2377 case LB_ADDFILE16:
2378 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2379 /* fall through */
2380 case LB_ADDFILE:
2381 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2382 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2384 case LB_DELETESTRING16:
2385 case LB_DELETESTRING:
2386 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2387 return descr->nb_items;
2388 else
2389 return LB_ERR;
2391 case LB_GETITEMDATA16:
2392 case LB_GETITEMDATA:
2393 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2394 return LB_ERR;
2395 return descr->items[wParam].data;
2397 case LB_SETITEMDATA16:
2398 case LB_SETITEMDATA:
2399 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2400 return LB_ERR;
2401 descr->items[wParam].data = (DWORD)lParam;
2402 return LB_OKAY;
2404 case LB_GETCOUNT16:
2405 case LB_GETCOUNT:
2406 return descr->nb_items;
2408 case LB_GETTEXT16:
2409 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2410 /* fall through */
2411 case LB_GETTEXT:
2412 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2414 case LB_GETTEXTLEN16:
2415 /* fall through */
2416 case LB_GETTEXTLEN:
2417 if (wParam >= descr->nb_items)
2418 return LB_ERR;
2419 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2420 : sizeof(DWORD));
2422 case LB_GETCURSEL16:
2423 case LB_GETCURSEL:
2424 if (descr->nb_items==0)
2425 return LB_ERR;
2426 if (!IS_MULTISELECT(descr))
2427 return descr->selected_item;
2428 /* else */
2429 if (descr->selected_item!=-1)
2430 return descr->selected_item;
2431 /* else */
2432 return descr->focus_item;
2433 /* otherwise, if the user tries to move the selection with the */
2434 /* arrow keys, we will give the application something to choke on */
2435 case LB_GETTOPINDEX16:
2436 case LB_GETTOPINDEX:
2437 return descr->top_item;
2439 case LB_GETITEMHEIGHT16:
2440 case LB_GETITEMHEIGHT:
2441 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2443 case LB_SETITEMHEIGHT16:
2444 lParam = LOWORD(lParam);
2445 /* fall through */
2446 case LB_SETITEMHEIGHT:
2447 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2449 case LB_ITEMFROMPOINT:
2451 POINT pt;
2452 RECT rect;
2454 pt.x = LOWORD(lParam);
2455 pt.y = HIWORD(lParam);
2456 rect.left = 0;
2457 rect.top = 0;
2458 rect.right = descr->width;
2459 rect.bottom = descr->height;
2461 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2462 !PtInRect( &rect, pt ) );
2465 case LB_SETCARETINDEX16:
2466 case LB_SETCARETINDEX:
2467 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2468 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2469 return LB_ERR;
2470 else if (ISWIN31)
2471 return wParam;
2472 else
2473 return LB_OKAY;
2475 case LB_GETCARETINDEX16:
2476 case LB_GETCARETINDEX:
2477 return descr->focus_item;
2479 case LB_SETTOPINDEX16:
2480 case LB_SETTOPINDEX:
2481 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2483 case LB_SETCOLUMNWIDTH16:
2484 case LB_SETCOLUMNWIDTH:
2485 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2487 case LB_GETITEMRECT16:
2489 RECT rect;
2490 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2491 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2493 return ret;
2495 case LB_GETITEMRECT:
2496 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2498 case LB_FINDSTRING16:
2499 wParam = (INT)(INT16)wParam;
2500 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2501 /* fall through */
2502 case LB_FINDSTRING:
2503 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2505 case LB_FINDSTRINGEXACT16:
2506 wParam = (INT)(INT16)wParam;
2507 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2508 /* fall through */
2509 case LB_FINDSTRINGEXACT:
2510 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2512 case LB_SELECTSTRING16:
2513 wParam = (INT)(INT16)wParam;
2514 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2515 /* fall through */
2516 case LB_SELECTSTRING:
2518 INT index = LISTBOX_FindString( wnd, descr, wParam,
2519 (LPCSTR)lParam, FALSE );
2520 if (index == LB_ERR)
2521 return LB_ERR;
2522 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2523 return index;
2526 case LB_GETSEL16:
2527 wParam = (INT)(INT16)wParam;
2528 /* fall through */
2529 case LB_GETSEL:
2530 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2531 return LB_ERR;
2532 return descr->items[wParam].selected;
2534 case LB_SETSEL16:
2535 lParam = (INT)(INT16)lParam;
2536 /* fall through */
2537 case LB_SETSEL:
2538 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2540 case LB_SETCURSEL16:
2541 wParam = (INT)(INT16)wParam;
2542 /* fall through */
2543 case LB_SETCURSEL:
2544 if (IS_MULTISELECT(descr)) return LB_ERR;
2545 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2546 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2548 case LB_GETSELCOUNT16:
2549 case LB_GETSELCOUNT:
2550 return LISTBOX_GetSelCount( wnd, descr );
2552 case LB_GETSELITEMS16:
2553 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2554 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2556 case LB_GETSELITEMS:
2557 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2559 case LB_SELITEMRANGE16:
2560 case LB_SELITEMRANGE:
2561 if (LOWORD(lParam) <= HIWORD(lParam))
2562 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2563 HIWORD(lParam), wParam );
2564 else
2565 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2566 LOWORD(lParam), wParam );
2568 case LB_SELITEMRANGEEX16:
2569 case LB_SELITEMRANGEEX:
2570 if ((INT)lParam >= (INT)wParam)
2571 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2572 else
2573 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2575 case LB_GETHORIZONTALEXTENT16:
2576 case LB_GETHORIZONTALEXTENT:
2577 return descr->horz_extent;
2579 case LB_SETHORIZONTALEXTENT16:
2580 case LB_SETHORIZONTALEXTENT:
2581 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2583 case LB_GETANCHORINDEX16:
2584 case LB_GETANCHORINDEX:
2585 return descr->anchor_item;
2587 case LB_SETANCHORINDEX16:
2588 wParam = (INT)(INT16)wParam;
2589 /* fall through */
2590 case LB_SETANCHORINDEX:
2591 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2592 return LB_ERR;
2593 descr->anchor_item = (INT)wParam;
2594 return LB_OKAY;
2596 case LB_DIR16:
2597 return LISTBOX_Directory( wnd, descr, wParam,
2598 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2600 case LB_DIR:
2601 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2603 case LB_GETLOCALE:
2604 return descr->locale;
2606 case LB_SETLOCALE:
2607 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2608 return LB_OKAY;
2610 case LB_INITSTORAGE:
2611 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2613 case LB_SETCOUNT:
2614 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2616 case LB_SETTABSTOPS16:
2617 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2618 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2620 case LB_SETTABSTOPS:
2621 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2623 case LB_CARETON16:
2624 case LB_CARETON:
2625 if (descr->caret_on)
2626 return LB_OKAY;
2627 descr->caret_on = TRUE;
2628 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2629 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2630 return LB_OKAY;
2632 case LB_CARETOFF16:
2633 case LB_CARETOFF:
2634 if (!descr->caret_on)
2635 return LB_OKAY;
2636 descr->caret_on = FALSE;
2637 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2638 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2639 return LB_OKAY;
2641 case WM_DESTROY:
2642 return LISTBOX_Destroy( wnd, descr );
2644 case WM_ENABLE:
2645 InvalidateRect( hwnd, NULL, TRUE );
2646 return 0;
2648 case WM_SETREDRAW:
2649 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2650 return 0;
2652 case WM_GETDLGCODE:
2653 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2655 case WM_PAINT:
2657 PAINTSTRUCT ps;
2658 HDC hdc = ( wParam ) ? ((HDC)wParam)
2659 : BeginPaint( hwnd, &ps );
2660 ret = LISTBOX_Paint( wnd, descr, hdc );
2661 if( !wParam ) EndPaint( hwnd, &ps );
2663 return ret;
2664 case WM_SIZE:
2665 LISTBOX_UpdateSize( wnd, descr );
2666 return 0;
2667 case WM_GETFONT:
2668 return descr->font;
2669 case WM_SETFONT:
2670 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2671 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2672 return 0;
2673 case WM_SETFOCUS:
2674 descr->caret_on = TRUE;
2675 if (descr->focus_item != -1)
2676 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2677 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2678 return 0;
2679 case WM_KILLFOCUS:
2680 if ((descr->focus_item != -1) && descr->caret_on)
2681 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2682 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2683 return 0;
2684 case WM_HSCROLL:
2685 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2686 case WM_VSCROLL:
2687 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2688 case WM_MOUSEWHEEL:
2689 if (wParam & (MK_SHIFT | MK_CONTROL))
2690 return DefWindowProcA( hwnd, msg, wParam, lParam );
2691 return LISTBOX_HandleMouseWheel( wnd, descr, wParam, lParam );
2692 case WM_LBUTTONDOWN:
2693 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2694 (INT16)LOWORD(lParam),
2695 (INT16)HIWORD(lParam) );
2696 case WM_LBUTTONDBLCLK:
2697 if (descr->style & LBS_NOTIFY)
2698 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2699 return 0;
2700 case WM_MOUSEMOVE:
2701 if (GetCapture() == hwnd)
2702 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2703 (INT16)HIWORD(lParam) );
2704 return 0;
2705 case WM_LBUTTONUP:
2706 return LISTBOX_HandleLButtonUp( wnd, descr );
2707 case WM_KEYDOWN:
2708 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2709 case WM_CHAR:
2710 return LISTBOX_HandleChar( wnd, descr, wParam );
2711 case WM_SYSTIMER:
2712 return LISTBOX_HandleSystemTimer( wnd, descr );
2713 case WM_ERASEBKGND:
2714 if (IS_OWNERDRAW(descr))
2716 RECT rect;
2717 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2718 wParam, (LPARAM)wnd->hwndSelf );
2719 GetClientRect(hwnd, &rect);
2720 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2722 return 1;
2723 case WM_DROPFILES:
2724 if( !descr->lphc )
2725 return SendMessageA( descr->owner, msg, wParam, lParam );
2726 break;
2728 case WM_DROPOBJECT:
2729 case WM_QUERYDROPOBJECT:
2730 case WM_DRAGSELECT:
2731 case WM_DRAGMOVE:
2732 if( !descr->lphc )
2734 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2735 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2736 dragInfo->pt.y );
2737 return SendMessageA( descr->owner, msg, wParam, lParam );
2739 break;
2741 default:
2742 if ((msg >= WM_USER) && (msg < 0xc000))
2743 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2744 hwnd, msg, wParam, lParam );
2745 return DefWindowProcA( hwnd, msg, wParam, lParam );
2747 return 0;
2750 /***********************************************************************
2751 * ListBoxWndProc
2753 * This is just a wrapper for the real wndproc, it only does window locking
2754 * and unlocking.
2756 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2757 WPARAM wParam, LPARAM lParam )
2759 WND* wndPtr = WIN_FindWndPtr( hwnd );
2760 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2762 WIN_ReleaseWndPtr(wndPtr);
2763 return res;
2766 /***********************************************************************
2767 * COMBO_Directory
2769 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2771 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2773 if( wnd )
2775 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2776 if( descr )
2778 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2780 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2781 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2782 WIN_ReleaseWndPtr(wnd);
2783 return lRet;
2785 WIN_ReleaseWndPtr(wnd);
2787 return CB_ERR;
2790 /***********************************************************************
2791 * ComboLBWndProc_locked
2793 * The real combo listbox wndproc, but called with locked WND struct.
2795 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2796 WPARAM wParam, LPARAM lParam )
2798 LRESULT lRet = 0;
2799 HWND hwnd = wnd->hwndSelf;
2801 if (wnd)
2803 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2805 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2806 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2808 if( descr || msg == WM_CREATE )
2810 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2812 switch( msg )
2814 case WM_CREATE:
2815 #define lpcs ((LPCREATESTRUCTA)lParam)
2816 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2817 (UINT)lpcs->lpCreateParams);
2819 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2820 #undef lpcs
2821 return LISTBOX_Create( wnd, lphc );
2822 case WM_MOUSEMOVE:
2823 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2824 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2826 POINT mousePos;
2827 BOOL captured;
2828 RECT clientRect;
2830 mousePos.x = (INT16)LOWORD(lParam);
2831 mousePos.y = (INT16)HIWORD(lParam);
2834 * If we are in a dropdown combobox, we simulate that
2835 * the mouse is captured to show the tracking of the item.
2837 GetClientRect(hwnd, &clientRect);
2839 if (PtInRect( &clientRect, mousePos ))
2841 captured = descr->captured;
2842 descr->captured = TRUE;
2844 LISTBOX_HandleMouseMove( wnd, descr,
2845 mousePos.x, mousePos.y);
2847 descr->captured = captured;
2850 else
2852 LISTBOX_HandleMouseMove( wnd, descr,
2853 mousePos.x, mousePos.y);
2856 return 0;
2859 else
2862 * If we are in Win3.1 look, go with the default behavior.
2864 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2866 case WM_LBUTTONUP:
2867 if (TWEAK_WineLook > WIN31_LOOK)
2869 POINT mousePos;
2870 RECT clientRect;
2873 * If the mouse button "up" is not in the listbox,
2874 * we make sure there is no selection by re-selecting the
2875 * item that was selected when the listbox was made visible.
2877 mousePos.x = (INT16)LOWORD(lParam);
2878 mousePos.y = (INT16)HIWORD(lParam);
2880 GetClientRect(hwnd, &clientRect);
2883 * When the user clicks outside the combobox and the focus
2884 * is lost, the owning combobox will send a fake buttonup with
2885 * 0xFFFFFFF as the mouse location, we must also revert the
2886 * selection to the original selection.
2888 if ( (lParam == 0xFFFFFFFF) ||
2889 (!PtInRect( &clientRect, mousePos )) )
2891 LISTBOX_MoveCaret( wnd,
2892 descr,
2893 lphc->droppedIndex,
2894 FALSE );
2897 return LISTBOX_HandleLButtonUp( wnd, descr );
2898 case WM_LBUTTONDOWN:
2899 return LISTBOX_HandleLButtonDownCombo(wnd, descr, wParam,
2900 (INT16)LOWORD(lParam),
2901 (INT16)HIWORD(lParam) );
2902 case WM_MOUSEACTIVATE:
2903 return MA_NOACTIVATE;
2904 case WM_NCACTIVATE:
2905 return FALSE;
2906 case WM_KEYDOWN:
2907 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2909 /* for some reason(?) Windows makes it possible to
2910 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2912 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2913 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2914 && (wParam == VK_DOWN || wParam == VK_UP)) )
2916 COMBO_FlipListbox( lphc, FALSE );
2917 return 0;
2920 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2922 case LB_SETCURSEL16:
2923 case LB_SETCURSEL:
2924 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2925 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2926 return lRet;
2927 case WM_NCDESTROY:
2928 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2929 lphc->hWndLBox = 0;
2930 /* fall through */
2932 default:
2933 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2936 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2938 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2940 return lRet;
2943 /***********************************************************************
2944 * ComboLBWndProc
2946 * NOTE: in Windows, winproc address of the ComboLBox is the same
2947 * as that of the Listbox.
2949 * This is just a wrapper for the real wndproc, it only does window locking
2950 * and unlocking.
2952 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2953 WPARAM wParam, LPARAM lParam )
2955 WND *wnd = WIN_FindWndPtr( hwnd );
2956 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2958 WIN_ReleaseWndPtr(wnd);
2959 return res;