Correct usage of a scratch array in X11DRV_PolyBezier.
[wine.git] / controls / listbox.c
bloba986fa2706ec9517eebd1a0800dbbc9f60819353
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
5 */
7 #include <string.h>
8 #include <stdlib.h>
9 #include "wine/winuser16.h"
10 #include "wine/winbase16.h"
11 #include "winuser.h"
12 #include "winerror.h"
13 #include "drive.h"
14 #include "heap.h"
15 #include "spy.h"
16 #include "win.h"
17 #include "combo.h"
18 #include "debugtools.h"
19 #include "tweak.h"
20 #include "winversion.h"
22 DEFAULT_DEBUG_CHANNEL(listbox)
23 DECLARE_DEBUG_CHANNEL(combo)
25 /* Unimplemented yet:
26 * - LBS_NOSEL
27 * - LBS_USETABSTOPS
28 * - Unicode
29 * - Locale handling
32 /* Items array granularity */
33 #define LB_ARRAY_GRANULARITY 16
35 /* Scrolling timeout in ms */
36 #define LB_SCROLL_TIMEOUT 50
38 /* Listbox system timer id */
39 #define LB_TIMER_ID 2
41 /* Item structure */
42 typedef struct
44 LPSTR str; /* Item text */
45 BOOL selected; /* Is item selected? */
46 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
47 DWORD data; /* User data */
48 } LB_ITEMDATA;
50 /* Listbox structure */
51 typedef struct
53 HANDLE heap; /* Heap for this listbox */
54 HWND owner; /* Owner window to send notifications to */
55 UINT style; /* Window style */
56 INT width; /* Window width */
57 INT height; /* Window height */
58 LB_ITEMDATA *items; /* Array of items */
59 INT nb_items; /* Number of items */
60 INT top_item; /* Top visible item */
61 INT selected_item; /* Selected item */
62 INT focus_item; /* Item that has the focus */
63 INT anchor_item; /* Anchor item for extended selection */
64 INT item_height; /* Default item height */
65 INT page_size; /* Items per listbox page */
66 INT column_width; /* Column width for multi-column listboxes */
67 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
68 INT horz_pos; /* Horizontal position */
69 INT nb_tabs; /* Number of tabs in array */
70 INT *tabs; /* Array of tabs */
71 BOOL caret_on; /* Is caret on? */
72 BOOL captured; /* Is mouse captured? */
73 HFONT font; /* Current font */
74 LCID locale; /* Current locale for string comparisons */
75 LPHEADCOMBO lphc; /* ComboLBox */
76 } LB_DESCR;
79 #define IS_OWNERDRAW(descr) \
80 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
82 #define HAS_STRINGS(descr) \
83 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
86 #define IS_MULTISELECT(descr) \
87 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
89 #define SEND_NOTIFICATION(wnd,descr,code) \
90 (SendMessageA( (descr)->owner, WM_COMMAND, \
91 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
93 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
95 /* Current timer status */
96 typedef enum
98 LB_TIMER_NONE,
99 LB_TIMER_UP,
100 LB_TIMER_LEFT,
101 LB_TIMER_DOWN,
102 LB_TIMER_RIGHT
103 } TIMER_DIRECTION;
105 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
108 /***********************************************************************
109 * LISTBOX_Dump
111 void LISTBOX_Dump( WND *wnd )
113 INT i;
114 LB_ITEMDATA *item;
115 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
117 TRACE( "Listbox:\n" );
118 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
119 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
120 descr->top_item );
121 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
123 TRACE( "%4d: %-40s %d %08lx %3d\n",
124 i, item->str, item->selected, item->data, item->height );
129 /***********************************************************************
130 * LISTBOX_GetCurrentPageSize
132 * Return the current page size
134 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
136 INT i, height;
137 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
138 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
140 if ((height += descr->items[i].height) > descr->height) break;
142 if (i == descr->top_item) return 1;
143 else return i - descr->top_item;
147 /***********************************************************************
148 * LISTBOX_GetMaxTopIndex
150 * Return the maximum possible index for the top of the listbox.
152 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
154 INT max, page;
156 if (descr->style & LBS_OWNERDRAWVARIABLE)
158 page = descr->height;
159 for (max = descr->nb_items - 1; max >= 0; max--)
160 if ((page -= descr->items[max].height) < 0) break;
161 if (max < descr->nb_items - 1) max++;
163 else if (descr->style & LBS_MULTICOLUMN)
165 if ((page = descr->width / descr->column_width) < 1) page = 1;
166 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
167 max = (max - page) * descr->page_size;
169 else
171 max = descr->nb_items - descr->page_size;
173 if (max < 0) max = 0;
174 return max;
178 /***********************************************************************
179 * LISTBOX_UpdateScroll
181 * Update the scrollbars. Should be called whenever the content
182 * of the listbox changes.
184 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
186 SCROLLINFO info;
188 if (!(descr->style & WS_VSCROLL)) return;
189 /* It is important that we check descr->style, and not wnd->dwStyle,
190 for WS_VSCROLL, as the former is exactly the one passed in
191 argument to CreateWindow.
192 In Windows (and from now on in Wine :) a listbox created
193 with such a style (no WS_SCROLL) does not update
194 the scrollbar with listbox-related data, thus letting
195 the programmer use it for his/her own purposes. */
197 if (descr->style & LBS_NOREDRAW) return;
198 info.cbSize = sizeof(info);
200 if (descr->style & LBS_MULTICOLUMN)
202 info.nMin = 0;
203 info.nMax = (descr->nb_items - 1) / descr->page_size;
204 info.nPos = descr->top_item / descr->page_size;
205 info.nPage = descr->width / descr->column_width;
206 if (info.nPage < 1) info.nPage = 1;
207 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
208 if (descr->style & LBS_DISABLENOSCROLL)
209 info.fMask |= SIF_DISABLENOSCROLL;
210 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
211 info.nMax = 0;
212 info.fMask = SIF_RANGE;
213 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
215 else
217 info.nMin = 0;
218 info.nMax = descr->nb_items - 1;
219 info.nPos = descr->top_item;
220 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
221 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
222 if (descr->style & LBS_DISABLENOSCROLL)
223 info.fMask |= SIF_DISABLENOSCROLL;
224 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
226 if (descr->horz_extent)
228 info.nMin = 0;
229 info.nMax = descr->horz_extent - 1;
230 info.nPos = descr->horz_pos;
231 info.nPage = descr->width;
232 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
233 if (descr->style & LBS_DISABLENOSCROLL)
234 info.fMask |= SIF_DISABLENOSCROLL;
235 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
241 /***********************************************************************
242 * LISTBOX_SetTopItem
244 * Set the top item of the listbox, scrolling up or down if necessary.
246 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
247 BOOL scroll )
249 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
250 if (index > max) index = max;
251 if (index < 0) index = 0;
252 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
253 if (descr->top_item == index) return LB_OKAY;
254 if (descr->style & LBS_MULTICOLUMN)
256 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
257 if (scroll && (abs(diff) < descr->width))
258 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
259 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
261 else
262 scroll = FALSE;
264 else if (scroll)
266 INT diff;
267 if (descr->style & LBS_OWNERDRAWVARIABLE)
269 INT i;
270 diff = 0;
271 if (index > descr->top_item)
273 for (i = index - 1; i >= descr->top_item; i--)
274 diff -= descr->items[i].height;
276 else
278 for (i = index; i < descr->top_item; i++)
279 diff += descr->items[i].height;
282 else
283 diff = (descr->top_item - index) * descr->item_height;
285 if (abs(diff) < descr->height)
286 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
287 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
288 else
289 scroll = FALSE;
291 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
292 descr->top_item = index;
293 LISTBOX_UpdateScroll( wnd, descr );
294 return LB_OKAY;
298 /***********************************************************************
299 * LISTBOX_UpdatePage
301 * Update the page size. Should be called when the size of
302 * the client area or the item height changes.
304 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
306 INT page_size;
308 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
309 page_size = 1;
310 if (page_size == descr->page_size) return;
311 descr->page_size = page_size;
312 if (descr->style & LBS_MULTICOLUMN)
313 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
314 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
318 /***********************************************************************
319 * LISTBOX_UpdateSize
321 * Update the size of the listbox. Should be called when the size of
322 * the client area changes.
324 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
326 RECT rect;
328 GetClientRect( wnd->hwndSelf, &rect );
329 descr->width = rect.right - rect.left;
330 descr->height = rect.bottom - rect.top;
331 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
333 if ((descr->height > descr->item_height) &&
334 (descr->height % descr->item_height))
336 TRACE("[%04x]: changing height %d -> %d\n",
337 wnd->hwndSelf, descr->height,
338 descr->height - descr->height%descr->item_height );
339 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
340 wnd->rectWindow.right - wnd->rectWindow.left,
341 wnd->rectWindow.bottom - wnd->rectWindow.top -
342 (descr->height % descr->item_height),
343 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
344 return;
347 TRACE("[%04x]: new size = %d,%d\n",
348 wnd->hwndSelf, descr->width, descr->height );
349 LISTBOX_UpdatePage( wnd, descr );
350 LISTBOX_UpdateScroll( wnd, descr );
354 /***********************************************************************
355 * LISTBOX_GetItemRect
357 * Get the rectangle enclosing an item, in listbox client coordinates.
358 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
360 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
361 RECT *rect )
363 /* Index <= 0 is legal even on empty listboxes */
364 if (index && (index >= descr->nb_items)) return -1;
365 SetRect( rect, 0, 0, descr->width, descr->height );
366 if (descr->style & LBS_MULTICOLUMN)
368 INT col = (index / descr->page_size) -
369 (descr->top_item / descr->page_size);
370 rect->left += col * descr->column_width;
371 rect->right = rect->left + descr->column_width;
372 rect->top += (index % descr->page_size) * descr->item_height;
373 rect->bottom = rect->top + descr->item_height;
375 else if (descr->style & LBS_OWNERDRAWVARIABLE)
377 INT i;
378 rect->right += descr->horz_pos;
379 if ((index >= 0) && (index < descr->nb_items))
381 if (index < descr->top_item)
383 for (i = descr->top_item-1; i >= index; i--)
384 rect->top -= descr->items[i].height;
386 else
388 for (i = descr->top_item; i < index; i++)
389 rect->top += descr->items[i].height;
391 rect->bottom = rect->top + descr->items[index].height;
395 else
397 rect->top += (index - descr->top_item) * descr->item_height;
398 rect->bottom = rect->top + descr->item_height;
399 rect->right += descr->horz_pos;
402 return ((rect->left < descr->width) && (rect->right > 0) &&
403 (rect->top < descr->height) && (rect->bottom > 0));
407 /***********************************************************************
408 * LISTBOX_GetItemFromPoint
410 * Return the item nearest from point (x,y) (in client coordinates).
412 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
413 INT x, INT y )
415 INT index = descr->top_item;
417 if (!descr->nb_items) return -1; /* No items */
418 if (descr->style & LBS_OWNERDRAWVARIABLE)
420 INT pos = 0;
421 if (y >= 0)
423 while (index < descr->nb_items)
425 if ((pos += descr->items[index].height) > y) break;
426 index++;
429 else
431 while (index > 0)
433 index--;
434 if ((pos -= descr->items[index].height) <= y) break;
438 else if (descr->style & LBS_MULTICOLUMN)
440 if (y >= descr->item_height * descr->page_size) return -1;
441 if (y >= 0) index += y / descr->item_height;
442 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
443 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
445 else
447 index += (y / descr->item_height);
449 if (index < 0) return 0;
450 if (index >= descr->nb_items) return -1;
451 return index;
455 /***********************************************************************
456 * LISTBOX_PaintItem
458 * Paint an item.
460 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
461 const RECT *rect, INT index, UINT action )
463 LB_ITEMDATA *item = NULL;
464 if (index < descr->nb_items) item = &descr->items[index];
466 if (IS_OWNERDRAW(descr))
468 DRAWITEMSTRUCT dis;
469 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
471 if (!item)
473 if (action == ODA_FOCUS)
474 DrawFocusRect( hdc, rect );
475 else
476 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
477 return;
479 dis.CtlType = ODT_LISTBOX;
480 dis.CtlID = id;
481 dis.hwndItem = wnd->hwndSelf;
482 dis.itemAction = action;
483 dis.hDC = hdc;
484 dis.itemID = index;
485 dis.itemState = 0;
486 if (item && item->selected) dis.itemState |= ODS_SELECTED;
487 if ((descr->focus_item == index) &&
488 (descr->caret_on) &&
489 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
490 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
491 dis.itemData = item ? item->data : 0;
492 dis.rcItem = *rect;
493 TRACE("[%04x]: drawitem %d (%s) action=%02x "
494 "state=%02x rect=%d,%d-%d,%d\n",
495 wnd->hwndSelf, index, item ? item->str : "", action,
496 dis.itemState, rect->left, rect->top,
497 rect->right, rect->bottom );
498 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
500 else
502 COLORREF oldText = 0, oldBk = 0;
504 if (action == ODA_FOCUS)
506 DrawFocusRect( hdc, rect );
507 return;
509 if (item && item->selected)
511 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
512 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
515 TRACE("[%04x]: painting %d (%s) action=%02x "
516 "rect=%d,%d-%d,%d\n",
517 wnd->hwndSelf, index, item ? item->str : "", action,
518 rect->left, rect->top, rect->right, rect->bottom );
519 if (!item)
520 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
521 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
522 else if (!(descr->style & LBS_USETABSTOPS))
523 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
524 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
525 strlen(item->str), NULL );
526 else
528 /* Output empty string to paint background in the full width. */
529 ExtTextOutA( hdc, rect->left + 1, rect->top + 1,
530 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
531 TabbedTextOutA( hdc, rect->left + 1 , rect->top + 1,
532 item->str, strlen(item->str),
533 descr->nb_tabs, descr->tabs, 0);
535 if (item && item->selected)
537 SetBkColor( hdc, oldBk );
538 SetTextColor( hdc, oldText );
540 if ((descr->focus_item == index) &&
541 (descr->caret_on) &&
542 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
547 /***********************************************************************
548 * LISTBOX_SetRedraw
550 * Change the redraw flag.
552 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
554 if (on)
556 if (!(descr->style & LBS_NOREDRAW)) return;
557 descr->style &= ~LBS_NOREDRAW;
558 LISTBOX_UpdateScroll( wnd, descr );
560 else descr->style |= LBS_NOREDRAW;
564 /***********************************************************************
565 * LISTBOX_RepaintItem
567 * Repaint a single item synchronously.
569 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
570 UINT action )
572 HDC hdc;
573 RECT rect;
574 HFONT oldFont = 0;
575 HBRUSH hbrush, oldBrush = 0;
577 /* Do not repaint the item if the item is not visible */
578 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
580 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
581 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
582 if (descr->font) oldFont = SelectObject( hdc, descr->font );
583 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
584 hdc, (LPARAM)wnd->hwndSelf );
585 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
586 if (wnd->dwStyle & WS_DISABLED)
587 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
588 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
589 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
590 if (oldFont) SelectObject( hdc, oldFont );
591 if (oldBrush) SelectObject( hdc, oldBrush );
592 ReleaseDC( wnd->hwndSelf, hdc );
596 /***********************************************************************
597 * LISTBOX_InitStorage
599 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
600 DWORD bytes )
602 LB_ITEMDATA *item;
604 nb_items += LB_ARRAY_GRANULARITY - 1;
605 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
606 if (descr->items)
607 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
608 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
609 nb_items * sizeof(LB_ITEMDATA) )))
611 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
612 return LB_ERRSPACE;
614 descr->items = item;
615 return LB_OKAY;
619 /***********************************************************************
620 * LISTBOX_SetTabStops
622 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
623 LPINT tabs, BOOL short_ints )
625 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
626 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
627 if (!(descr->nb_tabs = count))
629 descr->tabs = NULL;
630 return TRUE;
632 /* FIXME: count = 1 */
633 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
634 descr->nb_tabs * sizeof(INT) )))
635 return FALSE;
636 if (short_ints)
638 INT i;
639 LPINT16 p = (LPINT16)tabs;
641 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
642 for (i = 0; i < descr->nb_tabs; i++) {
643 descr->tabs[i] = *p++<<1; /* FIXME */
644 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
646 if (TRACE_ON(listbox)) DPRINTF("\n");
648 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
649 /* FIXME: repaint the window? */
650 return TRUE;
654 /***********************************************************************
655 * LISTBOX_GetText
657 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
658 LPSTR buffer )
660 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
661 if (HAS_STRINGS(descr))
663 if (!buffer)
664 return strlen(descr->items[index].str);
665 lstrcpyA( buffer, descr->items[index].str );
666 return strlen(buffer);
667 } else {
668 if (buffer)
669 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
670 return sizeof(DWORD);
675 /***********************************************************************
676 * LISTBOX_FindStringPos
678 * Find the nearest string located before a given string in sort order.
679 * If 'exact' is TRUE, return an error if we don't get an exact match.
681 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
682 BOOL exact )
684 INT index, min, max, res = -1;
686 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
687 min = 0;
688 max = descr->nb_items;
689 while (min != max)
691 index = (min + max) / 2;
692 if (HAS_STRINGS(descr))
693 res = lstrcmpiA( descr->items[index].str, str );
694 else
696 COMPAREITEMSTRUCT cis;
697 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
699 cis.CtlType = ODT_LISTBOX;
700 cis.CtlID = id;
701 cis.hwndItem = wnd->hwndSelf;
702 cis.itemID1 = index;
703 cis.itemData1 = descr->items[index].data;
704 cis.itemID2 = -1;
705 cis.itemData2 = (DWORD)str;
706 cis.dwLocaleId = descr->locale;
707 res = SendMessageA( descr->owner, WM_COMPAREITEM,
708 id, (LPARAM)&cis );
710 if (!res) return index;
711 if (res > 0) max = index;
712 else min = index + 1;
714 return exact ? -1 : max;
718 /***********************************************************************
719 * LISTBOX_FindFileStrPos
721 * Find the nearest string located before a given string in directory
722 * sort order (i.e. first files, then directories, then drives).
724 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
726 INT min, max, res = -1;
728 if (!HAS_STRINGS(descr))
729 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
730 min = 0;
731 max = descr->nb_items;
732 while (min != max)
734 INT index = (min + max) / 2;
735 const char *p = descr->items[index].str;
736 if (*p == '[') /* drive or directory */
738 if (*str != '[') res = -1;
739 else if (p[1] == '-') /* drive */
741 if (str[1] == '-') res = str[2] - p[2];
742 else res = -1;
744 else /* directory */
746 if (str[1] == '-') res = 1;
747 else res = lstrcmpiA( str, p );
750 else /* filename */
752 if (*str == '[') res = 1;
753 else res = lstrcmpiA( str, p );
755 if (!res) return index;
756 if (res < 0) max = index;
757 else min = index + 1;
759 return max;
763 /***********************************************************************
764 * LISTBOX_FindString
766 * Find the item beginning with a given string.
768 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
769 LPCSTR str, BOOL exact )
771 INT i;
772 LB_ITEMDATA *item;
774 if (start >= descr->nb_items) start = -1;
775 item = descr->items + start + 1;
776 if (HAS_STRINGS(descr))
778 if (!str || ! str[0] ) return LB_ERR;
779 if (exact)
781 for (i = start + 1; i < descr->nb_items; i++, item++)
782 if (!lstrcmpiA( str, item->str )) return i;
783 for (i = 0, item = descr->items; i <= start; i++, item++)
784 if (!lstrcmpiA( str, item->str )) return i;
786 else
788 /* Special case for drives and directories: ignore prefix */
789 #define CHECK_DRIVE(item) \
790 if ((item)->str[0] == '[') \
792 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
793 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
794 return i; \
797 INT len = strlen(str);
798 for (i = start + 1; i < descr->nb_items; i++, item++)
800 if (!lstrncmpiA( str, item->str, len )) return i;
801 CHECK_DRIVE(item);
803 for (i = 0, item = descr->items; i <= start; i++, item++)
805 if (!lstrncmpiA( str, item->str, len )) return i;
806 CHECK_DRIVE(item);
808 #undef CHECK_DRIVE
811 else
813 if (exact && (descr->style & LBS_SORT))
814 /* If sorted, use a WM_COMPAREITEM binary search */
815 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
817 /* Otherwise use a linear search */
818 for (i = start + 1; i < descr->nb_items; i++, item++)
819 if (item->data == (DWORD)str) return i;
820 for (i = 0, item = descr->items; i <= start; i++, item++)
821 if (item->data == (DWORD)str) return i;
823 return LB_ERR;
827 /***********************************************************************
828 * LISTBOX_GetSelCount
830 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
832 INT i, count;
833 LB_ITEMDATA *item = descr->items;
835 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
836 for (i = count = 0; i < descr->nb_items; i++, item++)
837 if (item->selected) count++;
838 return count;
842 /***********************************************************************
843 * LISTBOX_GetSelItems16
845 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
846 LPINT16 array )
848 INT i, count;
849 LB_ITEMDATA *item = descr->items;
851 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
852 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
853 if (item->selected) array[count++] = (INT16)i;
854 return count;
858 /***********************************************************************
859 * LISTBOX_GetSelItems32
861 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
862 LPINT array )
864 INT i, count;
865 LB_ITEMDATA *item = descr->items;
867 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
868 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
869 if (item->selected) array[count++] = i;
870 return count;
874 /***********************************************************************
875 * LISTBOX_Paint
877 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
879 INT i, col_pos = descr->page_size - 1;
880 RECT rect;
881 HFONT oldFont = 0;
882 HBRUSH hbrush, oldBrush = 0;
884 SetRect( &rect, 0, 0, descr->width, descr->height );
885 if (descr->style & LBS_NOREDRAW) return 0;
886 if (descr->style & LBS_MULTICOLUMN)
887 rect.right = rect.left + descr->column_width;
888 else if (descr->horz_pos)
890 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
891 rect.right += descr->horz_pos;
894 if (descr->font) oldFont = SelectObject( hdc, descr->font );
895 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
896 hdc, (LPARAM)wnd->hwndSelf );
897 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
898 if (wnd->dwStyle & WS_DISABLED)
899 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
901 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
902 (GetFocus() == wnd->hwndSelf))
904 /* Special case for empty listbox: paint focus rect */
905 rect.bottom = rect.top + descr->item_height;
906 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
907 ODA_FOCUS );
908 rect.top = rect.bottom;
911 for (i = descr->top_item; i < descr->nb_items; i++)
913 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
914 rect.bottom = rect.top + descr->item_height;
915 else
916 rect.bottom = rect.top + descr->items[i].height;
918 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
919 rect.top = rect.bottom;
921 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
923 if (!IS_OWNERDRAW(descr))
925 /* Clear the bottom of the column */
926 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
927 if (rect.top < descr->height)
929 rect.bottom = descr->height;
930 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
931 &rect, NULL, 0, NULL );
935 /* Go to the next column */
936 rect.left += descr->column_width;
937 rect.right += descr->column_width;
938 rect.top = 0;
939 col_pos = descr->page_size - 1;
941 else
943 col_pos--;
944 if (rect.top >= descr->height) break;
948 if (!IS_OWNERDRAW(descr))
950 /* Clear the remainder of the client area */
951 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
952 if (rect.top < descr->height)
954 rect.bottom = descr->height;
955 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
956 &rect, NULL, 0, NULL );
958 if (rect.right < descr->width)
960 rect.left = rect.right;
961 rect.right = descr->width;
962 rect.top = 0;
963 rect.bottom = descr->height;
964 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
965 &rect, NULL, 0, NULL );
968 if (oldFont) SelectObject( hdc, oldFont );
969 if (oldBrush) SelectObject( hdc, oldBrush );
970 return 0;
974 /***********************************************************************
975 * LISTBOX_InvalidateItems
977 * Invalidate all items from a given item. If the specified item is not
978 * visible, nothing happens.
980 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
982 RECT rect;
984 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
986 rect.bottom = descr->height;
987 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
988 if (descr->style & LBS_MULTICOLUMN)
990 /* Repaint the other columns */
991 rect.left = rect.right;
992 rect.right = descr->width;
993 rect.top = 0;
994 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1000 /***********************************************************************
1001 * LISTBOX_GetItemHeight
1003 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1005 if (descr->style & LBS_OWNERDRAWVARIABLE)
1007 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1008 return descr->items[index].height;
1010 else return descr->item_height;
1014 /***********************************************************************
1015 * LISTBOX_SetItemHeight
1017 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1018 UINT height )
1020 if (!height) height = 1;
1022 if (descr->style & LBS_OWNERDRAWVARIABLE)
1024 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1025 TRACE("[%04x]: item %d height = %d\n",
1026 wnd->hwndSelf, index, height );
1027 descr->items[index].height = height;
1028 LISTBOX_UpdateScroll( wnd, descr );
1029 LISTBOX_InvalidateItems( wnd, descr, index );
1031 else if (height != descr->item_height)
1033 TRACE("[%04x]: new height = %d\n",
1034 wnd->hwndSelf, height );
1035 descr->item_height = height;
1036 LISTBOX_UpdatePage( wnd, descr );
1037 LISTBOX_UpdateScroll( wnd, descr );
1038 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1040 return LB_OKAY;
1044 /***********************************************************************
1045 * LISTBOX_SetHorizontalPos
1047 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1049 INT diff;
1051 if (pos > descr->horz_extent - descr->width)
1052 pos = descr->horz_extent - descr->width;
1053 if (pos < 0) pos = 0;
1054 if (!(diff = descr->horz_pos - pos)) return;
1055 TRACE("[%04x]: new horz pos = %d\n",
1056 wnd->hwndSelf, pos );
1057 descr->horz_pos = pos;
1058 LISTBOX_UpdateScroll( wnd, descr );
1059 if (abs(diff) < descr->width)
1060 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1061 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1062 else
1063 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1067 /***********************************************************************
1068 * LISTBOX_SetHorizontalExtent
1070 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1071 UINT extent )
1073 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1074 return LB_OKAY;
1075 if (extent <= 0) extent = 1;
1076 if (extent == descr->horz_extent) return LB_OKAY;
1077 TRACE("[%04x]: new horz extent = %d\n",
1078 wnd->hwndSelf, extent );
1079 descr->horz_extent = extent;
1080 if (descr->horz_pos > extent - descr->width)
1081 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1082 else
1083 LISTBOX_UpdateScroll( wnd, descr );
1084 return LB_OKAY;
1088 /***********************************************************************
1089 * LISTBOX_SetColumnWidth
1091 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1093 width += 2; /* For left and right margin */
1094 if (width == descr->column_width) return LB_OKAY;
1095 TRACE("[%04x]: new column width = %d\n",
1096 wnd->hwndSelf, width );
1097 descr->column_width = width;
1098 LISTBOX_UpdatePage( wnd, descr );
1099 return LB_OKAY;
1103 /***********************************************************************
1104 * LISTBOX_SetFont
1106 * Returns the item height.
1108 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1110 HDC hdc;
1111 HFONT oldFont = 0;
1112 TEXTMETRICA tm;
1114 descr->font = font;
1116 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1118 ERR("unable to get DC.\n" );
1119 return 16;
1121 if (font) oldFont = SelectObject( hdc, font );
1122 GetTextMetricsA( hdc, &tm );
1123 if (oldFont) SelectObject( hdc, oldFont );
1124 ReleaseDC( wnd->hwndSelf, hdc );
1125 if (!IS_OWNERDRAW(descr))
1126 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1127 return tm.tmHeight ;
1131 /***********************************************************************
1132 * LISTBOX_MakeItemVisible
1134 * Make sure that a given item is partially or fully visible.
1136 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1137 BOOL fully )
1139 INT top;
1141 if (index <= descr->top_item) top = index;
1142 else if (descr->style & LBS_MULTICOLUMN)
1144 INT cols = descr->width;
1145 if (!fully) cols += descr->column_width - 1;
1146 if (cols >= descr->column_width) cols /= descr->column_width;
1147 else cols = 1;
1148 if (index < descr->top_item + (descr->page_size * cols)) return;
1149 top = index - descr->page_size * (cols - 1);
1151 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1153 INT height = fully ? descr->items[index].height : 1;
1154 for (top = index; top > descr->top_item; top--)
1155 if ((height += descr->items[top-1].height) > descr->height) break;
1157 else
1159 if (index < descr->top_item + descr->page_size) return;
1160 if (!fully && (index == descr->top_item + descr->page_size) &&
1161 (descr->height > (descr->page_size * descr->item_height))) return;
1162 top = index - descr->page_size + 1;
1164 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1168 /***********************************************************************
1169 * LISTBOX_SelectItemRange
1171 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1173 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1174 INT last, BOOL on )
1176 INT i;
1178 /* A few sanity checks */
1180 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1181 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1182 if (last == -1) last = descr->nb_items - 1;
1183 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1184 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1185 /* selected_item reflects last selected/unselected item on multiple sel */
1186 descr->selected_item = last;
1188 if (on) /* Turn selection on */
1190 for (i = first; i <= last; i++)
1192 if (descr->items[i].selected) continue;
1193 descr->items[i].selected = TRUE;
1194 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1197 else /* Turn selection off */
1199 for (i = first; i <= last; i++)
1201 if (!descr->items[i].selected) continue;
1202 descr->items[i].selected = FALSE;
1203 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1206 return LB_OKAY;
1210 /***********************************************************************
1211 * LISTBOX_SetCaretIndex
1213 * NOTES
1214 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1217 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1218 BOOL fully_visible )
1220 INT oldfocus = descr->focus_item;
1222 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1223 if (index == oldfocus) return LB_OKAY;
1224 descr->focus_item = index;
1225 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1226 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1228 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1229 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1230 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1232 return LB_OKAY;
1236 /***********************************************************************
1237 * LISTBOX_SetSelection
1239 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1240 BOOL on, BOOL send_notify )
1242 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1243 if (descr->style & LBS_MULTIPLESEL)
1245 if (index == -1) /* Select all items */
1246 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1247 else /* Only one item */
1248 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1250 else
1252 INT oldsel = descr->selected_item;
1253 if (index == oldsel) return LB_OKAY;
1254 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1255 if (index != -1) descr->items[index].selected = TRUE;
1256 descr->selected_item = index;
1257 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, ODA_SELECT);
1258 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1259 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1260 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1261 else
1262 if( descr->lphc ) /* set selection change flag for parent combo */
1263 descr->lphc->wState |= CBF_SELCHANGE;
1265 return LB_OKAY;
1269 /***********************************************************************
1270 * LISTBOX_MoveCaret
1272 * Change the caret position and extend the selection to the new caret.
1274 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1275 BOOL fully_visible )
1277 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1278 if (descr->style & LBS_EXTENDEDSEL)
1280 if (descr->anchor_item != -1)
1282 INT first = MIN( descr->focus_item, descr->anchor_item );
1283 INT last = MAX( descr->focus_item, descr->anchor_item );
1284 if (first > 0)
1285 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1286 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1287 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1290 else if (!(descr->style & LBS_MULTIPLESEL))
1292 /* Set selection to new caret item */
1293 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1298 /***********************************************************************
1299 * LISTBOX_InsertItem
1301 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1302 LPSTR str, DWORD data )
1304 LB_ITEMDATA *item;
1305 INT max_items;
1306 INT oldfocus = descr->focus_item;
1308 if (index == -1) index = descr->nb_items;
1309 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1310 if (!descr->items) max_items = 0;
1311 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1312 if (descr->nb_items == max_items)
1314 /* We need to grow the array */
1315 max_items += LB_ARRAY_GRANULARITY;
1316 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1317 max_items * sizeof(LB_ITEMDATA) )))
1319 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1320 return LB_ERRSPACE;
1322 descr->items = item;
1325 /* Insert the item structure */
1327 item = &descr->items[index];
1328 if (index < descr->nb_items)
1329 RtlMoveMemory( item + 1, item,
1330 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1331 item->str = str;
1332 item->data = data;
1333 item->height = 0;
1334 item->selected = FALSE;
1335 descr->nb_items++;
1337 /* Get item height */
1339 if (descr->style & LBS_OWNERDRAWVARIABLE)
1341 MEASUREITEMSTRUCT mis;
1342 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1344 mis.CtlType = ODT_LISTBOX;
1345 mis.CtlID = id;
1346 mis.itemID = index;
1347 mis.itemData = descr->items[index].data;
1348 mis.itemHeight = descr->item_height;
1349 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1350 item->height = mis.itemHeight ? mis.itemHeight : 1;
1351 TRACE("[%04x]: measure item %d (%s) = %d\n",
1352 wnd->hwndSelf, index, str ? str : "", item->height );
1355 /* Repaint the items */
1357 LISTBOX_UpdateScroll( wnd, descr );
1358 LISTBOX_InvalidateItems( wnd, descr, index );
1360 /* Move selection and focused item */
1361 /* If listbox was empty, set focus to the first item */
1362 if (descr->nb_items == 1)
1363 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1364 /* single select don't change selection index in win31 */
1365 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1367 descr->selected_item++;
1368 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1370 else
1372 if (index <= descr->selected_item)
1374 descr->selected_item++;
1375 descr->focus_item = oldfocus; /* focus not changed */
1378 return LB_OKAY;
1382 /***********************************************************************
1383 * LISTBOX_InsertString
1385 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1386 LPCSTR str )
1388 LPSTR new_str = NULL;
1389 DWORD data = 0;
1390 LRESULT ret;
1392 if (HAS_STRINGS(descr))
1394 if (!str) str="";
1395 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1397 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1398 return LB_ERRSPACE;
1401 else data = (DWORD)str;
1403 if (index == -1) index = descr->nb_items;
1404 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1406 if (new_str) HeapFree( descr->heap, 0, new_str );
1407 return ret;
1410 TRACE("[%04x]: added item %d '%s'\n",
1411 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1412 return index;
1416 /***********************************************************************
1417 * LISTBOX_DeleteItem
1419 * Delete the content of an item. 'index' must be a valid index.
1421 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1423 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1424 * while Win95 sends it for all items with user data.
1425 * It's probably better to send it too often than not
1426 * often enough, so this is what we do here.
1428 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1430 DELETEITEMSTRUCT dis;
1431 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1433 dis.CtlType = ODT_LISTBOX;
1434 dis.CtlID = id;
1435 dis.itemID = index;
1436 dis.hwndItem = wnd->hwndSelf;
1437 dis.itemData = descr->items[index].data;
1438 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1440 if (HAS_STRINGS(descr) && descr->items[index].str)
1441 HeapFree( descr->heap, 0, descr->items[index].str );
1445 /***********************************************************************
1446 * LISTBOX_RemoveItem
1448 * Remove an item from the listbox and delete its content.
1450 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1452 LB_ITEMDATA *item;
1453 INT max_items;
1455 if (index == -1) index = descr->nb_items - 1;
1456 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1457 LISTBOX_DeleteItem( wnd, descr, index );
1459 /* Remove the item */
1461 item = &descr->items[index];
1462 if (index < descr->nb_items-1)
1463 RtlMoveMemory( item, item + 1,
1464 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1465 descr->nb_items--;
1466 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1468 /* Shrink the item array if possible */
1470 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1471 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1473 max_items -= LB_ARRAY_GRANULARITY;
1474 item = HeapReAlloc( descr->heap, 0, descr->items,
1475 max_items * sizeof(LB_ITEMDATA) );
1476 if (item) descr->items = item;
1478 /* Repaint the items */
1480 LISTBOX_UpdateScroll( wnd, descr );
1481 /* if we removed the scrollbar, reset the top of the list
1482 (correct for owner-drawn ???) */
1483 if (descr->nb_items == descr->page_size)
1484 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1486 /* Move selection and focused item */
1487 if (!IS_MULTISELECT(descr))
1489 if (index == descr->selected_item)
1490 descr->selected_item = -1;
1491 else if (index < descr->selected_item)
1493 descr->selected_item--;
1494 if (ISWIN31) /* win 31 do not change the selected item number */
1495 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1498 LISTBOX_InvalidateItems( wnd, descr, index );
1499 if (descr->focus_item >= descr->nb_items)
1501 descr->focus_item = descr->nb_items - 1;
1502 if (descr->focus_item < 0) descr->focus_item = 0;
1504 return LB_OKAY;
1508 /***********************************************************************
1509 * LISTBOX_ResetContent
1511 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1513 INT i;
1515 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1516 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1517 descr->nb_items = 0;
1518 descr->top_item = 0;
1519 descr->selected_item = -1;
1520 descr->focus_item = 0;
1521 descr->anchor_item = -1;
1522 descr->items = NULL;
1523 LISTBOX_UpdateScroll( wnd, descr );
1524 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1528 /***********************************************************************
1529 * LISTBOX_SetCount
1531 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1533 LRESULT ret;
1535 if (HAS_STRINGS(descr)) return LB_ERR;
1536 /* FIXME: this is far from optimal... */
1537 if (count > descr->nb_items)
1539 while (count > descr->nb_items)
1540 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1541 return ret;
1543 else if (count < descr->nb_items)
1545 while (count < descr->nb_items)
1546 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1547 return ret;
1549 return LB_OKAY;
1553 /***********************************************************************
1554 * LISTBOX_Directory
1556 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1557 LPCSTR filespec, BOOL long_names )
1559 HANDLE handle;
1560 LRESULT ret = LB_OKAY;
1561 WIN32_FIND_DATAA entry;
1562 int pos;
1564 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1566 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1568 else
1572 char buffer[270];
1573 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1575 if (!(attrib & DDL_DIRECTORY) ||
1576 !strcmp( entry.cAlternateFileName, "." )) continue;
1577 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1578 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1580 else /* not a directory */
1582 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1583 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1585 if ((attrib & DDL_EXCLUSIVE) &&
1586 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1587 continue;
1588 #undef ATTRIBS
1589 if (long_names) strcpy( buffer, entry.cFileName );
1590 else strcpy( buffer, entry.cAlternateFileName );
1592 if (!long_names) CharLowerA( buffer );
1593 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1594 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1595 break;
1596 } while (FindNextFileA( handle, &entry ));
1597 FindClose( handle );
1600 if ((ret >= 0) && (attrib & DDL_DRIVES))
1602 char buffer[] = "[-a-]";
1603 int drive;
1604 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1606 if (!DRIVE_IsValid(drive)) continue;
1607 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1608 break;
1611 return ret;
1615 /***********************************************************************
1616 * LISTBOX_HandleVScroll
1618 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1619 WPARAM wParam, LPARAM lParam )
1621 SCROLLINFO info;
1623 if (descr->style & LBS_MULTICOLUMN) return 0;
1624 switch(LOWORD(wParam))
1626 case SB_LINEUP:
1627 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1628 break;
1629 case SB_LINEDOWN:
1630 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1631 break;
1632 case SB_PAGEUP:
1633 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1634 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1635 break;
1636 case SB_PAGEDOWN:
1637 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1638 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1639 break;
1640 case SB_THUMBPOSITION:
1641 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1642 break;
1643 case SB_THUMBTRACK:
1644 info.cbSize = sizeof(info);
1645 info.fMask = SIF_TRACKPOS;
1646 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1647 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1648 break;
1649 case SB_TOP:
1650 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1651 break;
1652 case SB_BOTTOM:
1653 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1654 break;
1656 return 0;
1660 /***********************************************************************
1661 * LISTBOX_HandleHScroll
1663 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1664 WPARAM wParam, LPARAM lParam )
1666 SCROLLINFO info;
1667 INT page;
1669 if (descr->style & LBS_MULTICOLUMN)
1671 switch(LOWORD(wParam))
1673 case SB_LINELEFT:
1674 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1675 TRUE );
1676 break;
1677 case SB_LINERIGHT:
1678 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1679 TRUE );
1680 break;
1681 case SB_PAGELEFT:
1682 page = descr->width / descr->column_width;
1683 if (page < 1) page = 1;
1684 LISTBOX_SetTopItem( wnd, descr,
1685 descr->top_item - page * descr->page_size, TRUE );
1686 break;
1687 case SB_PAGERIGHT:
1688 page = descr->width / descr->column_width;
1689 if (page < 1) page = 1;
1690 LISTBOX_SetTopItem( wnd, descr,
1691 descr->top_item + page * descr->page_size, TRUE );
1692 break;
1693 case SB_THUMBPOSITION:
1694 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1695 TRUE );
1696 break;
1697 case SB_THUMBTRACK:
1698 info.cbSize = sizeof(info);
1699 info.fMask = SIF_TRACKPOS;
1700 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1701 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1702 TRUE );
1703 break;
1704 case SB_LEFT:
1705 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1706 break;
1707 case SB_RIGHT:
1708 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1709 break;
1712 else if (descr->horz_extent)
1714 switch(LOWORD(wParam))
1716 case SB_LINELEFT:
1717 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1718 break;
1719 case SB_LINERIGHT:
1720 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1721 break;
1722 case SB_PAGELEFT:
1723 LISTBOX_SetHorizontalPos( wnd, descr,
1724 descr->horz_pos - descr->width );
1725 break;
1726 case SB_PAGERIGHT:
1727 LISTBOX_SetHorizontalPos( wnd, descr,
1728 descr->horz_pos + descr->width );
1729 break;
1730 case SB_THUMBPOSITION:
1731 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1732 break;
1733 case SB_THUMBTRACK:
1734 info.cbSize = sizeof(info);
1735 info.fMask = SIF_TRACKPOS;
1736 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1737 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1738 break;
1739 case SB_LEFT:
1740 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1741 break;
1742 case SB_RIGHT:
1743 LISTBOX_SetHorizontalPos( wnd, descr,
1744 descr->horz_extent - descr->width );
1745 break;
1748 return 0;
1752 /***********************************************************************
1753 * LISTBOX_HandleLButtonDown
1755 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1756 WPARAM wParam, INT x, INT y )
1758 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1759 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1760 wnd->hwndSelf, x, y, index );
1761 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1762 if (index != -1)
1764 if (descr->style & LBS_EXTENDEDSEL)
1766 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1767 if (wParam & MK_CONTROL)
1769 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1770 LISTBOX_SetSelection( wnd, descr, index,
1771 !descr->items[index].selected, FALSE );
1773 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1775 else
1777 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1778 LISTBOX_SetSelection( wnd, descr, index,
1779 (!(descr->style & LBS_MULTIPLESEL) ||
1780 !descr->items[index].selected), FALSE );
1784 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1785 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1786 : descr->lphc->self->hwndSelf ) ;
1788 descr->captured = TRUE;
1789 SetCapture( wnd->hwndSelf );
1790 if (index != -1 && !descr->lphc)
1792 if (descr->style & LBS_NOTIFY )
1793 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1794 MAKELPARAM( x, y ) );
1795 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1797 POINT pt;
1799 pt.x = x;
1800 pt.y = y;
1802 if (DragDetect( wnd->hwndSelf, pt ))
1803 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1806 return 0;
1810 /*************************************************************************
1811 * LISTBOX_HandleLButtonDownCombo [Internal]
1813 * Process LButtonDown message for the ComboListBox
1815 * PARAMS
1816 * pWnd [I] The windows internal structure
1817 * pDescr [I] The ListBox internal structure
1818 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1819 * x [I] X Mouse Coordinate
1820 * y [I] Y Mouse Coordinate
1822 * RETURNS
1823 * 0 since we are processing the WM_LBUTTONDOWN Message
1825 * NOTES
1826 * This function is only to be used when a ListBox is a ComboListBox
1829 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1830 WPARAM wParam, INT x, INT y)
1832 RECT clientRect, screenRect;
1833 POINT mousePos;
1835 mousePos.x = x;
1836 mousePos.y = y;
1838 GetClientRect(pWnd->hwndSelf, &clientRect);
1840 if(PtInRect(&clientRect, mousePos))
1842 /* MousePos is in client, resume normal processing */
1843 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1845 else
1847 POINT screenMousePos;
1848 HWND hWndOldCapture;
1850 /* Check the Non-Client Area */
1851 screenMousePos = mousePos;
1852 hWndOldCapture = GetCapture();
1853 ReleaseCapture();
1854 GetWindowRect(pWnd->hwndSelf, &screenRect);
1855 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1857 if(!PtInRect(&screenRect, screenMousePos))
1859 /* Close The Drop Down */
1860 SEND_NOTIFICATION( pWnd, pDescr, LBN_SELCANCEL );
1861 return 0;
1863 else
1865 /* Check to see the NC is a scrollbar */
1866 INT nHitTestType=0;
1867 /* Check Vertical scroll bar */
1868 if (pWnd->dwStyle & WS_VSCROLL)
1870 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1871 if (PtInRect( &clientRect, mousePos ))
1873 nHitTestType = HTVSCROLL;
1876 /* Check horizontal scroll bar */
1877 if (pWnd->dwStyle & WS_HSCROLL)
1879 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1880 if (PtInRect( &clientRect, mousePos ))
1882 nHitTestType = HTHSCROLL;
1885 /* Windows sends this message when a scrollbar is clicked
1888 if(nHitTestType != 0)
1890 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1891 MAKELONG(screenMousePos.x, screenMousePos.y));
1893 /* Resume the Capture after scrolling is complete
1895 if(hWndOldCapture != 0)
1897 SetCapture(hWndOldCapture);
1901 return 0;
1904 /***********************************************************************
1905 * LISTBOX_HandleLButtonUp
1907 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1909 if (LISTBOX_Timer != LB_TIMER_NONE)
1910 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1911 LISTBOX_Timer = LB_TIMER_NONE;
1912 if (descr->captured)
1914 descr->captured = FALSE;
1915 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1916 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
1917 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1919 return 0;
1923 /***********************************************************************
1924 * LISTBOX_HandleTimer
1926 * Handle scrolling upon a timer event.
1927 * Return TRUE if scrolling should continue.
1929 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1930 INT index, TIMER_DIRECTION dir )
1932 switch(dir)
1934 case LB_TIMER_UP:
1935 if (descr->top_item) index = descr->top_item - 1;
1936 else index = 0;
1937 break;
1938 case LB_TIMER_LEFT:
1939 if (descr->top_item) index -= descr->page_size;
1940 break;
1941 case LB_TIMER_DOWN:
1942 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1943 if (index == descr->focus_item) index++;
1944 if (index >= descr->nb_items) index = descr->nb_items - 1;
1945 break;
1946 case LB_TIMER_RIGHT:
1947 if (index + descr->page_size < descr->nb_items)
1948 index += descr->page_size;
1949 break;
1950 case LB_TIMER_NONE:
1951 break;
1953 if (index == descr->focus_item) return FALSE;
1954 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1955 return TRUE;
1959 /***********************************************************************
1960 * LISTBOX_HandleSystemTimer
1962 * WM_SYSTIMER handler.
1964 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1966 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1968 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1969 LISTBOX_Timer = LB_TIMER_NONE;
1971 return 0;
1975 /***********************************************************************
1976 * LISTBOX_HandleMouseMove
1978 * WM_MOUSEMOVE handler.
1980 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1981 INT x, INT y )
1983 INT index;
1984 TIMER_DIRECTION dir;
1986 if (!descr->captured) return;
1988 if (descr->style & LBS_MULTICOLUMN)
1990 if (y < 0) y = 0;
1991 else if (y >= descr->item_height * descr->page_size)
1992 y = descr->item_height * descr->page_size - 1;
1994 if (x < 0)
1996 dir = LB_TIMER_LEFT;
1997 x = 0;
1999 else if (x >= descr->width)
2001 dir = LB_TIMER_RIGHT;
2002 x = descr->width - 1;
2004 else dir = LB_TIMER_NONE; /* inside */
2006 else
2008 if (y < 0) dir = LB_TIMER_UP; /* above */
2009 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2010 else dir = LB_TIMER_NONE; /* inside */
2013 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2014 if (index == -1) index = descr->focus_item;
2015 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2017 /* Start/stop the system timer */
2019 if (dir != LB_TIMER_NONE)
2020 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2021 else if (LISTBOX_Timer != LB_TIMER_NONE)
2022 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2023 LISTBOX_Timer = dir;
2027 /***********************************************************************
2028 * LISTBOX_HandleKeyDown
2030 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2032 INT caret = -1;
2033 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2034 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2035 bForceSelection = FALSE; /* only for single select list */
2037 if (descr->style & LBS_WANTKEYBOARDINPUT)
2039 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2040 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2041 wnd->hwndSelf );
2042 if (caret == -2) return 0;
2044 if (caret == -1) switch(wParam)
2046 case VK_LEFT:
2047 if (descr->style & LBS_MULTICOLUMN)
2049 bForceSelection = FALSE;
2050 if (descr->focus_item >= descr->page_size)
2051 caret = descr->focus_item - descr->page_size;
2052 break;
2054 /* fall through */
2055 case VK_UP:
2056 caret = descr->focus_item - 1;
2057 if (caret < 0) caret = 0;
2058 break;
2059 case VK_RIGHT:
2060 if (descr->style & LBS_MULTICOLUMN)
2062 bForceSelection = FALSE;
2063 if (descr->focus_item + descr->page_size < descr->nb_items)
2064 caret = descr->focus_item + descr->page_size;
2065 break;
2067 /* fall through */
2068 case VK_DOWN:
2069 caret = descr->focus_item + 1;
2070 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2071 break;
2073 case VK_PRIOR:
2074 if (descr->style & LBS_MULTICOLUMN)
2076 INT page = descr->width / descr->column_width;
2077 if (page < 1) page = 1;
2078 caret = descr->focus_item - (page * descr->page_size) + 1;
2080 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2081 if (caret < 0) caret = 0;
2082 break;
2083 case VK_NEXT:
2084 if (descr->style & LBS_MULTICOLUMN)
2086 INT page = descr->width / descr->column_width;
2087 if (page < 1) page = 1;
2088 caret = descr->focus_item + (page * descr->page_size) - 1;
2090 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2091 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2092 break;
2093 case VK_HOME:
2094 caret = 0;
2095 break;
2096 case VK_END:
2097 caret = descr->nb_items - 1;
2098 break;
2099 case VK_SPACE:
2100 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2101 else if (descr->style & LBS_MULTIPLESEL)
2103 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2104 !descr->items[descr->focus_item].selected,
2105 (descr->style & LBS_NOTIFY) != 0 );
2107 break;
2108 default:
2109 bForceSelection = FALSE;
2111 if (bForceSelection) /* focused item is used instead of key */
2112 caret = descr->focus_item;
2113 if (caret >= 0)
2115 if ((descr->style & LBS_EXTENDEDSEL) &&
2116 !(GetKeyState( VK_SHIFT ) & 0x8000))
2117 descr->anchor_item = caret;
2118 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2119 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2120 if (descr->style & LBS_NOTIFY)
2122 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2124 /* make sure that combo parent doesn't hide us */
2125 descr->lphc->wState |= CBF_NOROLLUP;
2127 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2130 return 0;
2134 /***********************************************************************
2135 * LISTBOX_HandleChar
2137 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2138 WPARAM wParam )
2140 INT caret = -1;
2141 char str[2];
2143 str[0] = wParam & 0xff;
2144 str[1] = '\0';
2146 if (descr->style & LBS_WANTKEYBOARDINPUT)
2148 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2149 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2150 wnd->hwndSelf );
2151 if (caret == -2) return 0;
2153 if (caret == -1)
2154 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2155 if (caret != -1)
2157 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2158 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2159 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2160 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2161 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2163 return 0;
2167 /***********************************************************************
2168 * LISTBOX_Create
2170 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2172 LB_DESCR *descr;
2173 MEASUREITEMSTRUCT mis;
2174 RECT rect;
2176 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2177 return FALSE;
2178 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2180 HeapFree( GetProcessHeap(), 0, descr );
2181 return FALSE;
2183 GetClientRect( wnd->hwndSelf, &rect );
2184 descr->owner = GetParent( wnd->hwndSelf );
2185 descr->style = wnd->dwStyle;
2186 descr->width = rect.right - rect.left;
2187 descr->height = rect.bottom - rect.top;
2188 descr->items = NULL;
2189 descr->nb_items = 0;
2190 descr->top_item = 0;
2191 descr->selected_item = -1;
2192 descr->focus_item = 0;
2193 descr->anchor_item = -1;
2194 descr->item_height = 1;
2195 descr->page_size = 1;
2196 descr->column_width = 150;
2197 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2198 descr->horz_pos = 0;
2199 descr->nb_tabs = 0;
2200 descr->tabs = NULL;
2201 descr->caret_on = TRUE;
2202 descr->captured = FALSE;
2203 descr->font = 0;
2204 descr->locale = 0; /* FIXME */
2205 descr->lphc = lphc;
2207 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2208 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2210 /* Win95 document "List Box Differences" from MSDN:
2211 If a list box in a version 3.x application has either the
2212 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2213 horizontal and vertical scroll bars.
2215 descr->style |= WS_VSCROLL | WS_HSCROLL;
2218 if( lphc )
2220 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2221 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2222 descr->owner = lphc->self->hwndSelf;
2225 *(LB_DESCR **)wnd->wExtra = descr;
2227 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2229 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2230 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2231 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2232 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2234 if (descr->style & LBS_OWNERDRAWFIXED)
2236 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2238 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2239 descr->item_height = lphc->fixedOwnerDrawHeight;
2241 else
2243 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2245 mis.CtlType = ODT_LISTBOX;
2246 mis.CtlID = id;
2247 mis.itemID = -1;
2248 mis.itemWidth = 0;
2249 mis.itemData = 0;
2250 mis.itemHeight = descr->item_height;
2251 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2252 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2256 return TRUE;
2260 /***********************************************************************
2261 * LISTBOX_Destroy
2263 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2265 LISTBOX_ResetContent( wnd, descr );
2266 HeapDestroy( descr->heap );
2267 HeapFree( GetProcessHeap(), 0, descr );
2268 wnd->wExtra[0] = 0;
2269 return TRUE;
2273 /***********************************************************************
2274 * ListBoxWndProc
2276 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2277 WPARAM wParam, LPARAM lParam )
2279 LRESULT ret;
2280 LB_DESCR *descr;
2281 HWND hwnd = wnd->hwndSelf;
2283 if (!wnd) return 0;
2284 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2286 switch (msg)
2288 case WM_CREATE:
2290 if (!LISTBOX_Create( wnd, NULL ))
2291 return -1;
2292 TRACE("creating wnd=%04x descr=%p\n",
2293 hwnd, *(LB_DESCR **)wnd->wExtra );
2294 return 0;
2296 case WM_NCCREATE:
2299 * When a listbox is not in a combobox and the look
2300 * is win95, the WS_BORDER style is replaced with
2301 * the WS_EX_CLIENTEDGE style.
2303 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2304 (wnd->dwStyle & WS_BORDER) )
2306 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2307 wnd->dwStyle &= ~ WS_BORDER;
2312 /* Ignore all other messages before we get a WM_CREATE */
2313 return DefWindowProcA( hwnd, msg, wParam, lParam );
2316 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2317 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2318 switch(msg)
2320 case LB_RESETCONTENT16:
2321 case LB_RESETCONTENT:
2322 LISTBOX_ResetContent( wnd, descr );
2323 return 0;
2325 case LB_ADDSTRING16:
2326 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2327 /* fall through */
2328 case LB_ADDSTRING:
2329 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2330 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2332 case LB_INSERTSTRING16:
2333 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2334 wParam = (INT)(INT16)wParam;
2335 /* fall through */
2336 case LB_INSERTSTRING:
2337 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2339 case LB_ADDFILE16:
2340 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2341 /* fall through */
2342 case LB_ADDFILE:
2343 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2344 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2346 case LB_DELETESTRING16:
2347 case LB_DELETESTRING:
2348 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2349 return descr->nb_items;
2350 else
2351 return LB_ERR;
2353 case LB_GETITEMDATA16:
2354 case LB_GETITEMDATA:
2355 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2356 return LB_ERR;
2357 return descr->items[wParam].data;
2359 case LB_SETITEMDATA16:
2360 case LB_SETITEMDATA:
2361 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2362 return LB_ERR;
2363 descr->items[wParam].data = (DWORD)lParam;
2364 return LB_OKAY;
2366 case LB_GETCOUNT16:
2367 case LB_GETCOUNT:
2368 return descr->nb_items;
2370 case LB_GETTEXT16:
2371 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2372 /* fall through */
2373 case LB_GETTEXT:
2374 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2376 case LB_GETTEXTLEN16:
2377 /* fall through */
2378 case LB_GETTEXTLEN:
2379 if (wParam >= descr->nb_items)
2380 return LB_ERR;
2381 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2382 : sizeof(DWORD));
2384 case LB_GETCURSEL16:
2385 case LB_GETCURSEL:
2386 if (descr->nb_items==0)
2387 return LB_ERR;
2388 if (!IS_MULTISELECT(descr))
2389 return descr->selected_item;
2390 /* else */
2391 if (descr->selected_item!=-1)
2392 return descr->selected_item;
2393 /* else */
2394 return descr->focus_item;
2395 /* otherwise, if the user tries to move the selection with the */
2396 /* arrow keys, we will give the application something to choke on */
2397 case LB_GETTOPINDEX16:
2398 case LB_GETTOPINDEX:
2399 return descr->top_item;
2401 case LB_GETITEMHEIGHT16:
2402 case LB_GETITEMHEIGHT:
2403 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2405 case LB_SETITEMHEIGHT16:
2406 lParam = LOWORD(lParam);
2407 /* fall through */
2408 case LB_SETITEMHEIGHT:
2409 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2411 case LB_ITEMFROMPOINT:
2413 POINT pt;
2414 RECT rect;
2416 pt.x = LOWORD(lParam);
2417 pt.y = HIWORD(lParam);
2418 rect.left = 0;
2419 rect.top = 0;
2420 rect.right = descr->width;
2421 rect.bottom = descr->height;
2423 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2424 !PtInRect( &rect, pt ) );
2427 case LB_SETCARETINDEX16:
2428 case LB_SETCARETINDEX:
2429 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2430 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2431 return LB_ERR;
2432 else if (ISWIN31)
2433 return wParam;
2434 else
2435 return LB_OKAY;
2437 case LB_GETCARETINDEX16:
2438 case LB_GETCARETINDEX:
2439 return descr->focus_item;
2441 case LB_SETTOPINDEX16:
2442 case LB_SETTOPINDEX:
2443 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2445 case LB_SETCOLUMNWIDTH16:
2446 case LB_SETCOLUMNWIDTH:
2447 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2449 case LB_GETITEMRECT16:
2451 RECT rect;
2452 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2453 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2455 return ret;
2457 case LB_GETITEMRECT:
2458 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2460 case LB_FINDSTRING16:
2461 wParam = (INT)(INT16)wParam;
2462 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2463 /* fall through */
2464 case LB_FINDSTRING:
2465 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2467 case LB_FINDSTRINGEXACT16:
2468 wParam = (INT)(INT16)wParam;
2469 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2470 /* fall through */
2471 case LB_FINDSTRINGEXACT:
2472 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2474 case LB_SELECTSTRING16:
2475 wParam = (INT)(INT16)wParam;
2476 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2477 /* fall through */
2478 case LB_SELECTSTRING:
2480 INT index = LISTBOX_FindString( wnd, descr, wParam,
2481 (LPCSTR)lParam, FALSE );
2482 if (index == LB_ERR)
2483 return LB_ERR;
2484 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2485 return index;
2488 case LB_GETSEL16:
2489 wParam = (INT)(INT16)wParam;
2490 /* fall through */
2491 case LB_GETSEL:
2492 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2493 return LB_ERR;
2494 return descr->items[wParam].selected;
2496 case LB_SETSEL16:
2497 lParam = (INT)(INT16)lParam;
2498 /* fall through */
2499 case LB_SETSEL:
2500 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2502 case LB_SETCURSEL16:
2503 wParam = (INT)(INT16)wParam;
2504 /* fall through */
2505 case LB_SETCURSEL:
2506 if (IS_MULTISELECT(descr)) return LB_ERR;
2507 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2508 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2510 case LB_GETSELCOUNT16:
2511 case LB_GETSELCOUNT:
2512 return LISTBOX_GetSelCount( wnd, descr );
2514 case LB_GETSELITEMS16:
2515 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2516 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2518 case LB_GETSELITEMS:
2519 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2521 case LB_SELITEMRANGE16:
2522 case LB_SELITEMRANGE:
2523 if (LOWORD(lParam) <= HIWORD(lParam))
2524 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2525 HIWORD(lParam), wParam );
2526 else
2527 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2528 LOWORD(lParam), wParam );
2530 case LB_SELITEMRANGEEX16:
2531 case LB_SELITEMRANGEEX:
2532 if ((INT)lParam >= (INT)wParam)
2533 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2534 else
2535 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2537 case LB_GETHORIZONTALEXTENT16:
2538 case LB_GETHORIZONTALEXTENT:
2539 return descr->horz_extent;
2541 case LB_SETHORIZONTALEXTENT16:
2542 case LB_SETHORIZONTALEXTENT:
2543 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2545 case LB_GETANCHORINDEX16:
2546 case LB_GETANCHORINDEX:
2547 return descr->anchor_item;
2549 case LB_SETANCHORINDEX16:
2550 wParam = (INT)(INT16)wParam;
2551 /* fall through */
2552 case LB_SETANCHORINDEX:
2553 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2554 return LB_ERR;
2555 descr->anchor_item = (INT)wParam;
2556 return LB_OKAY;
2558 case LB_DIR16:
2559 return LISTBOX_Directory( wnd, descr, wParam,
2560 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2562 case LB_DIR:
2563 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2565 case LB_GETLOCALE:
2566 return descr->locale;
2568 case LB_SETLOCALE:
2569 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2570 return LB_OKAY;
2572 case LB_INITSTORAGE:
2573 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2575 case LB_SETCOUNT:
2576 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2578 case LB_SETTABSTOPS16:
2579 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2580 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2582 case LB_SETTABSTOPS:
2583 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2585 case LB_CARETON16:
2586 case LB_CARETON:
2587 if (descr->caret_on)
2588 return LB_OKAY;
2589 descr->caret_on = TRUE;
2590 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2591 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2592 return LB_OKAY;
2594 case LB_CARETOFF16:
2595 case LB_CARETOFF:
2596 if (!descr->caret_on)
2597 return LB_OKAY;
2598 descr->caret_on = FALSE;
2599 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2600 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2601 return LB_OKAY;
2603 case WM_DESTROY:
2604 return LISTBOX_Destroy( wnd, descr );
2606 case WM_ENABLE:
2607 InvalidateRect( hwnd, NULL, TRUE );
2608 return 0;
2610 case WM_SETREDRAW:
2611 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2612 return 0;
2614 case WM_GETDLGCODE:
2615 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2617 case WM_PAINT:
2619 PAINTSTRUCT ps;
2620 HDC hdc = ( wParam ) ? ((HDC)wParam)
2621 : BeginPaint( hwnd, &ps );
2622 ret = LISTBOX_Paint( wnd, descr, hdc );
2623 if( !wParam ) EndPaint( hwnd, &ps );
2625 return ret;
2626 case WM_SIZE:
2627 LISTBOX_UpdateSize( wnd, descr );
2628 return 0;
2629 case WM_GETFONT:
2630 return descr->font;
2631 case WM_SETFONT:
2632 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2633 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2634 return 0;
2635 case WM_SETFOCUS:
2636 descr->caret_on = TRUE;
2637 if (descr->focus_item != -1)
2638 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2639 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2640 return 0;
2641 case WM_KILLFOCUS:
2642 if ((descr->focus_item != -1) && descr->caret_on)
2643 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2644 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2645 return 0;
2646 case WM_HSCROLL:
2647 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2648 case WM_VSCROLL:
2649 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2650 case WM_LBUTTONDOWN:
2651 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2652 (INT16)LOWORD(lParam),
2653 (INT16)HIWORD(lParam) );
2654 case WM_LBUTTONDBLCLK:
2655 if (descr->style & LBS_NOTIFY)
2656 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2657 return 0;
2658 case WM_MOUSEMOVE:
2659 if (GetCapture() == hwnd)
2660 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2661 (INT16)HIWORD(lParam) );
2662 return 0;
2663 case WM_LBUTTONUP:
2664 return LISTBOX_HandleLButtonUp( wnd, descr );
2665 case WM_KEYDOWN:
2666 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2667 case WM_CHAR:
2668 return LISTBOX_HandleChar( wnd, descr, wParam );
2669 case WM_SYSTIMER:
2670 return LISTBOX_HandleSystemTimer( wnd, descr );
2671 case WM_ERASEBKGND:
2672 if (IS_OWNERDRAW(descr))
2674 RECT rect;
2675 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2676 wParam, (LPARAM)wnd->hwndSelf );
2677 GetClientRect(hwnd, &rect);
2678 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2680 return 1;
2681 case WM_DROPFILES:
2682 if( !descr->lphc )
2683 return SendMessageA( descr->owner, msg, wParam, lParam );
2684 break;
2686 case WM_DROPOBJECT:
2687 case WM_QUERYDROPOBJECT:
2688 case WM_DRAGSELECT:
2689 case WM_DRAGMOVE:
2690 if( !descr->lphc )
2692 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2693 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2694 dragInfo->pt.y );
2695 return SendMessageA( descr->owner, msg, wParam, lParam );
2697 break;
2699 default:
2700 if ((msg >= WM_USER) && (msg < 0xc000))
2701 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2702 hwnd, msg, wParam, lParam );
2703 return DefWindowProcA( hwnd, msg, wParam, lParam );
2705 return 0;
2708 /***********************************************************************
2709 * ListBoxWndProc
2711 * This is just a wrapper for the real wndproc, it only does window locking
2712 * and unlocking.
2714 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2715 WPARAM wParam, LPARAM lParam )
2717 WND* wndPtr = WIN_FindWndPtr( hwnd );
2718 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2720 WIN_ReleaseWndPtr(wndPtr);
2721 return res;
2724 /***********************************************************************
2725 * COMBO_Directory
2727 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2729 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2731 if( wnd )
2733 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2734 if( descr )
2736 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2738 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2739 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2740 WIN_ReleaseWndPtr(wnd);
2741 return lRet;
2743 WIN_ReleaseWndPtr(wnd);
2745 return CB_ERR;
2748 /***********************************************************************
2749 * ComboLBWndProc_locked
2751 * The real combo listbox wndproc, but called with locked WND struct.
2753 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2754 WPARAM wParam, LPARAM lParam )
2756 LRESULT lRet = 0;
2757 HWND hwnd = wnd->hwndSelf;
2759 if (wnd)
2761 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2763 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2764 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2766 if( descr || msg == WM_CREATE )
2768 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2770 switch( msg )
2772 case WM_CREATE:
2773 #define lpcs ((LPCREATESTRUCTA)lParam)
2774 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2775 (UINT)lpcs->lpCreateParams);
2777 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2778 #undef lpcs
2779 return LISTBOX_Create( wnd, lphc );
2780 case WM_MOUSEMOVE:
2781 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2782 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2784 POINT mousePos;
2785 BOOL captured;
2786 RECT clientRect;
2788 mousePos.x = (INT16)LOWORD(lParam);
2789 mousePos.y = (INT16)HIWORD(lParam);
2792 * If we are in a dropdown combobox, we simulate that
2793 * the mouse is captured to show the tracking of the item.
2795 GetClientRect(hwnd, &clientRect);
2797 if (PtInRect( &clientRect, mousePos ))
2799 captured = descr->captured;
2800 descr->captured = TRUE;
2802 LISTBOX_HandleMouseMove( wnd, descr,
2803 mousePos.x, mousePos.y);
2805 descr->captured = captured;
2808 else
2810 LISTBOX_HandleMouseMove( wnd, descr,
2811 mousePos.x, mousePos.y);
2814 return 0;
2817 else
2820 * If we are in Win3.1 look, go with the default behavior.
2822 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2824 case WM_LBUTTONUP:
2825 if (TWEAK_WineLook > WIN31_LOOK)
2827 POINT mousePos;
2828 RECT clientRect;
2831 * If the mouse button "up" is not in the listbox,
2832 * we make sure there is no selection by re-selecting the
2833 * item that was selected when the listbox was made visible.
2835 mousePos.x = (INT16)LOWORD(lParam);
2836 mousePos.y = (INT16)HIWORD(lParam);
2838 GetClientRect(hwnd, &clientRect);
2841 * When the user clicks outside the combobox and the focus
2842 * is lost, the owning combobox will send a fake buttonup with
2843 * 0xFFFFFFF as the mouse location, we must also revert the
2844 * selection to the original selection.
2846 if ( (lParam == 0xFFFFFFFF) ||
2847 (!PtInRect( &clientRect, mousePos )) )
2849 LISTBOX_MoveCaret( wnd,
2850 descr,
2851 lphc->droppedIndex,
2852 FALSE );
2855 return LISTBOX_HandleLButtonUp( wnd, descr );
2856 case WM_LBUTTONDOWN:
2857 return LISTBOX_HandleLButtonDownCombo(wnd, descr, wParam,
2858 (INT16)LOWORD(lParam),
2859 (INT16)HIWORD(lParam) );
2860 case WM_MOUSEACTIVATE:
2861 return MA_NOACTIVATE;
2862 case WM_NCACTIVATE:
2863 return FALSE;
2864 case WM_KEYDOWN:
2865 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2867 /* for some reason(?) Windows makes it possible to
2868 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2870 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2871 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2872 && (wParam == VK_DOWN || wParam == VK_UP)) )
2874 COMBO_FlipListbox( lphc, FALSE );
2875 return 0;
2878 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2880 case LB_SETCURSEL16:
2881 case LB_SETCURSEL:
2882 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2883 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2884 return lRet;
2885 case WM_NCDESTROY:
2886 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2887 lphc->hWndLBox = 0;
2888 /* fall through */
2890 default:
2891 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2894 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2896 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2898 return lRet;
2901 /***********************************************************************
2902 * ComboLBWndProc
2904 * NOTE: in Windows, winproc address of the ComboLBox is the same
2905 * as that of the Listbox.
2907 * This is just a wrapper for the real wndproc, it only does window locking
2908 * and unlocking.
2910 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2911 WPARAM wParam, LPARAM lParam )
2913 WND *wnd = WIN_FindWndPtr( hwnd );
2914 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2916 WIN_ReleaseWndPtr(wnd);
2917 return res;