ioctlsocket() now returns success if the app sets nonblocking mode for
[wine/testsucceed.git] / controls / listbox.c
blob3a1ea7b1469bc2d9f0b23d49ae2761f5d5fb3595
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 "win.h"
20 #include "combo.h"
21 #include "debugtools.h"
22 #include "tweak.h"
24 DEFAULT_DEBUG_CHANNEL(listbox);
25 DECLARE_DEBUG_CHANNEL(combo);
27 /* Unimplemented yet:
28 * - LBS_NOSEL
29 * - LBS_USETABSTOPS
30 * - Unicode
31 * - Locale handling
34 /* Items array granularity */
35 #define LB_ARRAY_GRANULARITY 16
37 /* Scrolling timeout in ms */
38 #define LB_SCROLL_TIMEOUT 50
40 /* Listbox system timer id */
41 #define LB_TIMER_ID 2
43 /* Item structure */
44 typedef struct
46 LPSTR str; /* Item text */
47 BOOL selected; /* Is item selected? */
48 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
49 DWORD data; /* User data */
50 } LB_ITEMDATA;
52 /* Listbox structure */
53 typedef struct
55 HANDLE heap; /* Heap for this listbox */
56 HWND owner; /* Owner window to send notifications to */
57 UINT style; /* Window style */
58 INT width; /* Window width */
59 INT height; /* Window height */
60 LB_ITEMDATA *items; /* Array of items */
61 INT nb_items; /* Number of items */
62 INT top_item; /* Top visible item */
63 INT selected_item; /* Selected item */
64 INT focus_item; /* Item that has the focus */
65 INT anchor_item; /* Anchor item for extended selection */
66 INT item_height; /* Default item height */
67 INT page_size; /* Items per listbox page */
68 INT column_width; /* Column width for multi-column listboxes */
69 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
70 INT horz_pos; /* Horizontal position */
71 INT nb_tabs; /* Number of tabs in array */
72 INT *tabs; /* Array of tabs */
73 BOOL caret_on; /* Is caret on? */
74 BOOL captured; /* Is mouse captured? */
75 HFONT font; /* Current font */
76 LCID locale; /* Current locale for string comparisons */
77 LPHEADCOMBO lphc; /* ComboLBox */
78 } LB_DESCR;
81 #define IS_OWNERDRAW(descr) \
82 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
84 #define HAS_STRINGS(descr) \
85 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
88 #define IS_MULTISELECT(descr) \
89 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
91 #define SEND_NOTIFICATION(wnd,descr,code) \
92 (SendMessageA( (descr)->owner, WM_COMMAND, \
93 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
95 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
97 /* Current timer status */
98 typedef enum
100 LB_TIMER_NONE,
101 LB_TIMER_UP,
102 LB_TIMER_LEFT,
103 LB_TIMER_DOWN,
104 LB_TIMER_RIGHT
105 } TIMER_DIRECTION;
107 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
110 /***********************************************************************
111 * LISTBOX_Dump
113 void LISTBOX_Dump( WND *wnd )
115 INT i;
116 LB_ITEMDATA *item;
117 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
119 TRACE( "Listbox:\n" );
120 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
121 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
122 descr->top_item );
123 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
125 TRACE( "%4d: %-40s %d %08lx %3d\n",
126 i, item->str, item->selected, item->data, item->height );
131 /***********************************************************************
132 * LISTBOX_GetCurrentPageSize
134 * Return the current page size
136 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
138 INT i, height;
139 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
140 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
142 if ((height += descr->items[i].height) > descr->height) break;
144 if (i == descr->top_item) return 1;
145 else return i - descr->top_item;
149 /***********************************************************************
150 * LISTBOX_GetMaxTopIndex
152 * Return the maximum possible index for the top of the listbox.
154 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
156 INT max, page;
158 if (descr->style & LBS_OWNERDRAWVARIABLE)
160 page = descr->height;
161 for (max = descr->nb_items - 1; max >= 0; max--)
162 if ((page -= descr->items[max].height) < 0) break;
163 if (max < descr->nb_items - 1) max++;
165 else if (descr->style & LBS_MULTICOLUMN)
167 if ((page = descr->width / descr->column_width) < 1) page = 1;
168 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
169 max = (max - page) * descr->page_size;
171 else
173 max = descr->nb_items - descr->page_size;
175 if (max < 0) max = 0;
176 return max;
180 /***********************************************************************
181 * LISTBOX_UpdateScroll
183 * Update the scrollbars. Should be called whenever the content
184 * of the listbox changes.
186 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
188 SCROLLINFO info;
190 if (!(descr->style & WS_VSCROLL)) return;
191 /* It is important that we check descr->style, and not wnd->dwStyle,
192 for WS_VSCROLL, as the former is exactly the one passed in
193 argument to CreateWindow.
194 In Windows (and from now on in Wine :) a listbox created
195 with such a style (no WS_SCROLL) does not update
196 the scrollbar with listbox-related data, thus letting
197 the programmer use it for his/her own purposes. */
199 if (descr->style & LBS_NOREDRAW) return;
200 info.cbSize = sizeof(info);
202 if (descr->style & LBS_MULTICOLUMN)
204 info.nMin = 0;
205 info.nMax = (descr->nb_items - 1) / descr->page_size;
206 info.nPos = descr->top_item / descr->page_size;
207 info.nPage = descr->width / descr->column_width;
208 if (info.nPage < 1) info.nPage = 1;
209 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
210 if (descr->style & LBS_DISABLENOSCROLL)
211 info.fMask |= SIF_DISABLENOSCROLL;
212 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
213 info.nMax = 0;
214 info.fMask = SIF_RANGE;
215 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
217 else
219 info.nMin = 0;
220 info.nMax = descr->nb_items - 1;
221 info.nPos = descr->top_item;
222 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
223 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
224 if (descr->style & LBS_DISABLENOSCROLL)
225 info.fMask |= SIF_DISABLENOSCROLL;
226 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
228 if (descr->horz_extent)
230 info.nMin = 0;
231 info.nMax = descr->horz_extent - 1;
232 info.nPos = descr->horz_pos;
233 info.nPage = descr->width;
234 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
235 if (descr->style & LBS_DISABLENOSCROLL)
236 info.fMask |= SIF_DISABLENOSCROLL;
237 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
243 /***********************************************************************
244 * LISTBOX_SetTopItem
246 * Set the top item of the listbox, scrolling up or down if necessary.
248 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
249 BOOL scroll )
251 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
252 if (index > max) index = max;
253 if (index < 0) index = 0;
254 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
255 if (descr->top_item == index) return LB_OKAY;
256 if (descr->style & LBS_MULTICOLUMN)
258 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
259 if (scroll && (abs(diff) < descr->width))
260 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
261 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
263 else
264 scroll = FALSE;
266 else if (scroll)
268 INT diff;
269 if (descr->style & LBS_OWNERDRAWVARIABLE)
271 INT i;
272 diff = 0;
273 if (index > descr->top_item)
275 for (i = index - 1; i >= descr->top_item; i--)
276 diff -= descr->items[i].height;
278 else
280 for (i = index; i < descr->top_item; i++)
281 diff += descr->items[i].height;
284 else
285 diff = (descr->top_item - index) * descr->item_height;
287 if (abs(diff) < descr->height)
288 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
289 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
290 else
291 scroll = FALSE;
293 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
294 descr->top_item = index;
295 LISTBOX_UpdateScroll( wnd, descr );
296 return LB_OKAY;
300 /***********************************************************************
301 * LISTBOX_UpdatePage
303 * Update the page size. Should be called when the size of
304 * the client area or the item height changes.
306 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
308 INT page_size;
310 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
311 page_size = 1;
312 if (page_size == descr->page_size) return;
313 descr->page_size = page_size;
314 if (descr->style & LBS_MULTICOLUMN)
315 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
316 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
320 /***********************************************************************
321 * LISTBOX_UpdateSize
323 * Update the size of the listbox. Should be called when the size of
324 * the client area changes.
326 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
328 RECT rect;
330 GetClientRect( wnd->hwndSelf, &rect );
331 descr->width = rect.right - rect.left;
332 descr->height = rect.bottom - rect.top;
333 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
335 if ((descr->height > descr->item_height) &&
336 (descr->height % descr->item_height))
338 TRACE("[%04x]: changing height %d -> %d\n",
339 wnd->hwndSelf, descr->height,
340 descr->height - descr->height%descr->item_height );
341 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
342 wnd->rectWindow.right - wnd->rectWindow.left,
343 wnd->rectWindow.bottom - wnd->rectWindow.top -
344 (descr->height % descr->item_height),
345 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
346 return;
349 TRACE("[%04x]: new size = %d,%d\n",
350 wnd->hwndSelf, descr->width, descr->height );
351 LISTBOX_UpdatePage( wnd, descr );
352 LISTBOX_UpdateScroll( wnd, descr );
356 /***********************************************************************
357 * LISTBOX_GetItemRect
359 * Get the rectangle enclosing an item, in listbox client coordinates.
360 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
362 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
363 RECT *rect )
365 /* Index <= 0 is legal even on empty listboxes */
366 if (index && (index >= descr->nb_items)) return -1;
367 SetRect( rect, 0, 0, descr->width, descr->height );
368 if (descr->style & LBS_MULTICOLUMN)
370 INT col = (index / descr->page_size) -
371 (descr->top_item / descr->page_size);
372 rect->left += col * descr->column_width;
373 rect->right = rect->left + descr->column_width;
374 rect->top += (index % descr->page_size) * descr->item_height;
375 rect->bottom = rect->top + descr->item_height;
377 else if (descr->style & LBS_OWNERDRAWVARIABLE)
379 INT i;
380 rect->right += descr->horz_pos;
381 if ((index >= 0) && (index < descr->nb_items))
383 if (index < descr->top_item)
385 for (i = descr->top_item-1; i >= index; i--)
386 rect->top -= descr->items[i].height;
388 else
390 for (i = descr->top_item; i < index; i++)
391 rect->top += descr->items[i].height;
393 rect->bottom = rect->top + descr->items[index].height;
397 else
399 rect->top += (index - descr->top_item) * descr->item_height;
400 rect->bottom = rect->top + descr->item_height;
401 rect->right += descr->horz_pos;
404 return ((rect->left < descr->width) && (rect->right > 0) &&
405 (rect->top < descr->height) && (rect->bottom > 0));
409 /***********************************************************************
410 * LISTBOX_GetItemFromPoint
412 * Return the item nearest from point (x,y) (in client coordinates).
414 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
415 INT x, INT y )
417 INT index = descr->top_item;
419 if (!descr->nb_items) return -1; /* No items */
420 if (descr->style & LBS_OWNERDRAWVARIABLE)
422 INT pos = 0;
423 if (y >= 0)
425 while (index < descr->nb_items)
427 if ((pos += descr->items[index].height) > y) break;
428 index++;
431 else
433 while (index > 0)
435 index--;
436 if ((pos -= descr->items[index].height) <= y) break;
440 else if (descr->style & LBS_MULTICOLUMN)
442 if (y >= descr->item_height * descr->page_size) return -1;
443 if (y >= 0) index += y / descr->item_height;
444 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
445 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
447 else
449 index += (y / descr->item_height);
451 if (index < 0) return 0;
452 if (index >= descr->nb_items) return -1;
453 return index;
457 /***********************************************************************
458 * LISTBOX_PaintItem
460 * Paint an item.
462 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
463 const RECT *rect, INT index, UINT action )
465 LB_ITEMDATA *item = NULL;
466 if (index < descr->nb_items) item = &descr->items[index];
468 if (IS_OWNERDRAW(descr))
470 DRAWITEMSTRUCT dis;
471 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
473 if (!item)
475 if (action == ODA_FOCUS)
476 DrawFocusRect( hdc, rect );
477 else
478 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
479 return;
481 dis.CtlType = ODT_LISTBOX;
482 dis.CtlID = id;
483 dis.hwndItem = wnd->hwndSelf;
484 dis.itemAction = action;
485 dis.hDC = hdc;
486 dis.itemID = index;
487 dis.itemState = 0;
488 if (item && item->selected) dis.itemState |= ODS_SELECTED;
489 if ((descr->focus_item == index) &&
490 (descr->caret_on) &&
491 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
492 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
493 dis.itemData = item ? item->data : 0;
494 dis.rcItem = *rect;
495 TRACE("[%04x]: drawitem %d (%s) action=%02x "
496 "state=%02x rect=%d,%d-%d,%d\n",
497 wnd->hwndSelf, index, item ? item->str : "", action,
498 dis.itemState, rect->left, rect->top,
499 rect->right, rect->bottom );
500 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
502 else
504 COLORREF oldText = 0, oldBk = 0;
506 if (action == ODA_FOCUS)
508 DrawFocusRect( hdc, rect );
509 return;
511 if (item && item->selected)
513 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
514 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
517 TRACE("[%04x]: painting %d (%s) action=%02x "
518 "rect=%d,%d-%d,%d\n",
519 wnd->hwndSelf, index, item ? item->str : "", action,
520 rect->left, rect->top, rect->right, rect->bottom );
521 if (!item)
522 ExtTextOutA( hdc, rect->left + 1, rect->top,
523 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
524 else if (!(descr->style & LBS_USETABSTOPS))
525 ExtTextOutA( hdc, rect->left + 1, rect->top,
526 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
527 strlen(item->str), NULL );
528 else
530 /* Output empty string to paint background in the full width. */
531 ExtTextOutA( hdc, rect->left + 1, rect->top,
532 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
533 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
534 item->str, strlen(item->str),
535 descr->nb_tabs, descr->tabs, 0);
537 if (item && item->selected)
539 SetBkColor( hdc, oldBk );
540 SetTextColor( hdc, oldText );
542 if ((descr->focus_item == index) &&
543 (descr->caret_on) &&
544 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
549 /***********************************************************************
550 * LISTBOX_SetRedraw
552 * Change the redraw flag.
554 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
556 if (on)
558 if (!(descr->style & LBS_NOREDRAW)) return;
559 descr->style &= ~LBS_NOREDRAW;
560 LISTBOX_UpdateScroll( wnd, descr );
562 else descr->style |= LBS_NOREDRAW;
566 /***********************************************************************
567 * LISTBOX_RepaintItem
569 * Repaint a single item synchronously.
571 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
572 UINT action )
574 HDC hdc;
575 RECT rect;
576 HFONT oldFont = 0;
577 HBRUSH hbrush, oldBrush = 0;
579 /* Do not repaint the item if the item is not visible */
580 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
582 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
583 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
584 if (descr->font) oldFont = SelectObject( hdc, descr->font );
585 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
586 hdc, (LPARAM)wnd->hwndSelf );
587 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
588 if (wnd->dwStyle & WS_DISABLED)
589 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
590 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
591 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
592 if (oldFont) SelectObject( hdc, oldFont );
593 if (oldBrush) SelectObject( hdc, oldBrush );
594 ReleaseDC( wnd->hwndSelf, hdc );
598 /***********************************************************************
599 * LISTBOX_InitStorage
601 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
602 DWORD bytes )
604 LB_ITEMDATA *item;
606 nb_items += LB_ARRAY_GRANULARITY - 1;
607 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
608 if (descr->items)
609 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
610 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
611 nb_items * sizeof(LB_ITEMDATA) )))
613 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
614 return LB_ERRSPACE;
616 descr->items = item;
617 return LB_OKAY;
621 /***********************************************************************
622 * LISTBOX_SetTabStops
624 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
625 LPINT tabs, BOOL short_ints )
627 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
628 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
629 if (!(descr->nb_tabs = count))
631 descr->tabs = NULL;
632 return TRUE;
634 /* FIXME: count = 1 */
635 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
636 descr->nb_tabs * sizeof(INT) )))
637 return FALSE;
638 if (short_ints)
640 INT i;
641 LPINT16 p = (LPINT16)tabs;
643 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
644 for (i = 0; i < descr->nb_tabs; i++) {
645 descr->tabs[i] = *p++<<1; /* FIXME */
646 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
648 if (TRACE_ON(listbox)) DPRINTF("\n");
650 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
651 /* FIXME: repaint the window? */
652 return TRUE;
656 /***********************************************************************
657 * LISTBOX_GetText
659 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
660 LPSTR buffer )
662 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
663 if (HAS_STRINGS(descr))
665 if (!buffer)
666 return strlen(descr->items[index].str);
667 lstrcpyA( buffer, descr->items[index].str );
668 return strlen(buffer);
669 } else {
670 if (buffer)
671 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
672 return sizeof(DWORD);
677 /***********************************************************************
678 * LISTBOX_FindStringPos
680 * Find the nearest string located before a given string in sort order.
681 * If 'exact' is TRUE, return an error if we don't get an exact match.
683 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
684 BOOL exact )
686 INT index, min, max, res = -1;
688 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
689 min = 0;
690 max = descr->nb_items;
691 while (min != max)
693 index = (min + max) / 2;
694 if (HAS_STRINGS(descr))
695 res = lstrcmpiA( descr->items[index].str, str );
696 else
698 COMPAREITEMSTRUCT cis;
699 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
701 cis.CtlType = ODT_LISTBOX;
702 cis.CtlID = id;
703 cis.hwndItem = wnd->hwndSelf;
704 cis.itemID1 = index;
705 cis.itemData1 = descr->items[index].data;
706 cis.itemID2 = -1;
707 cis.itemData2 = (DWORD)str;
708 cis.dwLocaleId = descr->locale;
709 res = SendMessageA( descr->owner, WM_COMPAREITEM,
710 id, (LPARAM)&cis );
712 if (!res) return index;
713 if (res > 0) max = index;
714 else min = index + 1;
716 return exact ? -1 : max;
720 /***********************************************************************
721 * LISTBOX_FindFileStrPos
723 * Find the nearest string located before a given string in directory
724 * sort order (i.e. first files, then directories, then drives).
726 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
728 INT min, max, res = -1;
730 if (!HAS_STRINGS(descr))
731 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
732 min = 0;
733 max = descr->nb_items;
734 while (min != max)
736 INT index = (min + max) / 2;
737 const char *p = descr->items[index].str;
738 if (*p == '[') /* drive or directory */
740 if (*str != '[') res = -1;
741 else if (p[1] == '-') /* drive */
743 if (str[1] == '-') res = str[2] - p[2];
744 else res = -1;
746 else /* directory */
748 if (str[1] == '-') res = 1;
749 else res = lstrcmpiA( str, p );
752 else /* filename */
754 if (*str == '[') res = 1;
755 else res = lstrcmpiA( str, p );
757 if (!res) return index;
758 if (res < 0) max = index;
759 else min = index + 1;
761 return max;
765 /***********************************************************************
766 * LISTBOX_FindString
768 * Find the item beginning with a given string.
770 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
771 LPCSTR str, BOOL exact )
773 INT i;
774 LB_ITEMDATA *item;
776 if (start >= descr->nb_items) start = -1;
777 item = descr->items + start + 1;
778 if (HAS_STRINGS(descr))
780 if (!str || ! str[0] ) return LB_ERR;
781 if (exact)
783 for (i = start + 1; i < descr->nb_items; i++, item++)
784 if (!lstrcmpiA( str, item->str )) return i;
785 for (i = 0, item = descr->items; i <= start; i++, item++)
786 if (!lstrcmpiA( str, item->str )) return i;
788 else
790 /* Special case for drives and directories: ignore prefix */
791 #define CHECK_DRIVE(item) \
792 if ((item)->str[0] == '[') \
794 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
795 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
796 return i; \
799 INT len = strlen(str);
800 for (i = start + 1; i < descr->nb_items; i++, item++)
802 if (!lstrncmpiA( str, item->str, len )) return i;
803 CHECK_DRIVE(item);
805 for (i = 0, item = descr->items; i <= start; i++, item++)
807 if (!lstrncmpiA( str, item->str, len )) return i;
808 CHECK_DRIVE(item);
810 #undef CHECK_DRIVE
813 else
815 if (exact && (descr->style & LBS_SORT))
816 /* If sorted, use a WM_COMPAREITEM binary search */
817 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
819 /* Otherwise use a linear search */
820 for (i = start + 1; i < descr->nb_items; i++, item++)
821 if (item->data == (DWORD)str) return i;
822 for (i = 0, item = descr->items; i <= start; i++, item++)
823 if (item->data == (DWORD)str) return i;
825 return LB_ERR;
829 /***********************************************************************
830 * LISTBOX_GetSelCount
832 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
834 INT i, count;
835 LB_ITEMDATA *item = descr->items;
837 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
838 for (i = count = 0; i < descr->nb_items; i++, item++)
839 if (item->selected) count++;
840 return count;
844 /***********************************************************************
845 * LISTBOX_GetSelItems16
847 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
848 LPINT16 array )
850 INT i, count;
851 LB_ITEMDATA *item = descr->items;
853 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
854 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
855 if (item->selected) array[count++] = (INT16)i;
856 return count;
860 /***********************************************************************
861 * LISTBOX_GetSelItems32
863 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
864 LPINT array )
866 INT i, count;
867 LB_ITEMDATA *item = descr->items;
869 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
870 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
871 if (item->selected) array[count++] = i;
872 return count;
876 /***********************************************************************
877 * LISTBOX_Paint
879 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
881 INT i, col_pos = descr->page_size - 1;
882 RECT rect;
883 HFONT oldFont = 0;
884 HBRUSH hbrush, oldBrush = 0;
886 SetRect( &rect, 0, 0, descr->width, descr->height );
887 if (descr->style & LBS_NOREDRAW) return 0;
888 if (descr->style & LBS_MULTICOLUMN)
889 rect.right = rect.left + descr->column_width;
890 else if (descr->horz_pos)
892 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
893 rect.right += descr->horz_pos;
896 if (descr->font) oldFont = SelectObject( hdc, descr->font );
897 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
898 hdc, (LPARAM)wnd->hwndSelf );
899 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
900 if (wnd->dwStyle & WS_DISABLED)
901 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
903 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
904 (GetFocus() == wnd->hwndSelf))
906 /* Special case for empty listbox: paint focus rect */
907 rect.bottom = rect.top + descr->item_height;
908 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
909 ODA_FOCUS );
910 rect.top = rect.bottom;
913 for (i = descr->top_item; i < descr->nb_items; i++)
915 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
916 rect.bottom = rect.top + descr->item_height;
917 else
918 rect.bottom = rect.top + descr->items[i].height;
920 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
921 rect.top = rect.bottom;
923 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
925 if (!IS_OWNERDRAW(descr))
927 /* Clear the bottom of the column */
928 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
929 if (rect.top < descr->height)
931 rect.bottom = descr->height;
932 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
933 &rect, NULL, 0, NULL );
937 /* Go to the next column */
938 rect.left += descr->column_width;
939 rect.right += descr->column_width;
940 rect.top = 0;
941 col_pos = descr->page_size - 1;
943 else
945 col_pos--;
946 if (rect.top >= descr->height) break;
950 if (!IS_OWNERDRAW(descr))
952 /* Clear the remainder of the client area */
953 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
954 if (rect.top < descr->height)
956 rect.bottom = descr->height;
957 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
958 &rect, NULL, 0, NULL );
960 if (rect.right < descr->width)
962 rect.left = rect.right;
963 rect.right = descr->width;
964 rect.top = 0;
965 rect.bottom = descr->height;
966 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
967 &rect, NULL, 0, NULL );
970 if (oldFont) SelectObject( hdc, oldFont );
971 if (oldBrush) SelectObject( hdc, oldBrush );
972 return 0;
976 /***********************************************************************
977 * LISTBOX_InvalidateItems
979 * Invalidate all items from a given item. If the specified item is not
980 * visible, nothing happens.
982 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
984 RECT rect;
986 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
988 rect.bottom = descr->height;
989 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
990 if (descr->style & LBS_MULTICOLUMN)
992 /* Repaint the other columns */
993 rect.left = rect.right;
994 rect.right = descr->width;
995 rect.top = 0;
996 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
1002 /***********************************************************************
1003 * LISTBOX_GetItemHeight
1005 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1007 if (descr->style & LBS_OWNERDRAWVARIABLE)
1009 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1010 return descr->items[index].height;
1012 else return descr->item_height;
1016 /***********************************************************************
1017 * LISTBOX_SetItemHeight
1019 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1020 UINT height )
1022 if (!height) height = 1;
1024 if (descr->style & LBS_OWNERDRAWVARIABLE)
1026 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1027 TRACE("[%04x]: item %d height = %d\n",
1028 wnd->hwndSelf, index, height );
1029 descr->items[index].height = height;
1030 LISTBOX_UpdateScroll( wnd, descr );
1031 LISTBOX_InvalidateItems( wnd, descr, index );
1033 else if (height != descr->item_height)
1035 TRACE("[%04x]: new height = %d\n",
1036 wnd->hwndSelf, height );
1037 descr->item_height = height;
1038 LISTBOX_UpdatePage( wnd, descr );
1039 LISTBOX_UpdateScroll( wnd, descr );
1040 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1042 return LB_OKAY;
1046 /***********************************************************************
1047 * LISTBOX_SetHorizontalPos
1049 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1051 INT diff;
1053 if (pos > descr->horz_extent - descr->width)
1054 pos = descr->horz_extent - descr->width;
1055 if (pos < 0) pos = 0;
1056 if (!(diff = descr->horz_pos - pos)) return;
1057 TRACE("[%04x]: new horz pos = %d\n",
1058 wnd->hwndSelf, pos );
1059 descr->horz_pos = pos;
1060 LISTBOX_UpdateScroll( wnd, descr );
1061 if (abs(diff) < descr->width)
1062 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1063 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1064 else
1065 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1069 /***********************************************************************
1070 * LISTBOX_SetHorizontalExtent
1072 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1073 UINT extent )
1075 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1076 return LB_OKAY;
1077 if (extent <= 0) extent = 1;
1078 if (extent == descr->horz_extent) return LB_OKAY;
1079 TRACE("[%04x]: new horz extent = %d\n",
1080 wnd->hwndSelf, extent );
1081 descr->horz_extent = extent;
1082 if (descr->horz_pos > extent - descr->width)
1083 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1084 else
1085 LISTBOX_UpdateScroll( wnd, descr );
1086 return LB_OKAY;
1090 /***********************************************************************
1091 * LISTBOX_SetColumnWidth
1093 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1095 width += 2; /* For left and right margin */
1096 if (width == descr->column_width) return LB_OKAY;
1097 TRACE("[%04x]: new column width = %d\n",
1098 wnd->hwndSelf, width );
1099 descr->column_width = width;
1100 LISTBOX_UpdatePage( wnd, descr );
1101 return LB_OKAY;
1105 /***********************************************************************
1106 * LISTBOX_SetFont
1108 * Returns the item height.
1110 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1112 HDC hdc;
1113 HFONT oldFont = 0;
1114 TEXTMETRICA tm;
1116 descr->font = font;
1118 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1120 ERR("unable to get DC.\n" );
1121 return 16;
1123 if (font) oldFont = SelectObject( hdc, font );
1124 GetTextMetricsA( hdc, &tm );
1125 if (oldFont) SelectObject( hdc, oldFont );
1126 ReleaseDC( wnd->hwndSelf, hdc );
1127 if (!IS_OWNERDRAW(descr))
1128 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1129 return tm.tmHeight ;
1133 /***********************************************************************
1134 * LISTBOX_MakeItemVisible
1136 * Make sure that a given item is partially or fully visible.
1138 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1139 BOOL fully )
1141 INT top;
1143 if (index <= descr->top_item) top = index;
1144 else if (descr->style & LBS_MULTICOLUMN)
1146 INT cols = descr->width;
1147 if (!fully) cols += descr->column_width - 1;
1148 if (cols >= descr->column_width) cols /= descr->column_width;
1149 else cols = 1;
1150 if (index < descr->top_item + (descr->page_size * cols)) return;
1151 top = index - descr->page_size * (cols - 1);
1153 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1155 INT height = fully ? descr->items[index].height : 1;
1156 for (top = index; top > descr->top_item; top--)
1157 if ((height += descr->items[top-1].height) > descr->height) break;
1159 else
1161 if (index < descr->top_item + descr->page_size) return;
1162 if (!fully && (index == descr->top_item + descr->page_size) &&
1163 (descr->height > (descr->page_size * descr->item_height))) return;
1164 top = index - descr->page_size + 1;
1166 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1170 /***********************************************************************
1171 * LISTBOX_SelectItemRange
1173 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1175 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1176 INT last, BOOL on )
1178 INT i;
1180 /* A few sanity checks */
1182 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1183 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1184 if (last == -1) last = descr->nb_items - 1;
1185 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1186 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1187 /* selected_item reflects last selected/unselected item on multiple sel */
1188 descr->selected_item = last;
1190 if (on) /* Turn selection on */
1192 for (i = first; i <= last; i++)
1194 if (descr->items[i].selected) continue;
1195 descr->items[i].selected = TRUE;
1196 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1199 else /* Turn selection off */
1201 for (i = first; i <= last; i++)
1203 if (!descr->items[i].selected) continue;
1204 descr->items[i].selected = FALSE;
1205 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1208 return LB_OKAY;
1212 /***********************************************************************
1213 * LISTBOX_SetCaretIndex
1215 * NOTES
1216 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1219 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1220 BOOL fully_visible )
1222 INT oldfocus = descr->focus_item;
1224 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1225 if (index == oldfocus) return LB_OKAY;
1226 descr->focus_item = index;
1227 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1228 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1230 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1231 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1232 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1234 return LB_OKAY;
1238 /***********************************************************************
1239 * LISTBOX_SetSelection
1241 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1242 BOOL on, BOOL send_notify )
1244 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1246 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1247 if (descr->style & LBS_MULTIPLESEL)
1249 if (index == -1) /* Select all items */
1250 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1251 else /* Only one item */
1252 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1254 else
1256 INT oldsel = descr->selected_item;
1257 if (index == oldsel) return LB_OKAY;
1258 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1259 if (index != -1) descr->items[index].selected = TRUE;
1260 descr->selected_item = index;
1261 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, 0 );
1262 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1263 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1264 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1265 else
1266 if( descr->lphc ) /* set selection change flag for parent combo */
1267 descr->lphc->wState |= CBF_SELCHANGE;
1269 return LB_OKAY;
1273 /***********************************************************************
1274 * LISTBOX_MoveCaret
1276 * Change the caret position and extend the selection to the new caret.
1278 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1279 BOOL fully_visible )
1281 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1282 if (descr->style & LBS_EXTENDEDSEL)
1284 if (descr->anchor_item != -1)
1286 INT first = MIN( descr->focus_item, descr->anchor_item );
1287 INT last = MAX( descr->focus_item, descr->anchor_item );
1288 if (first > 0)
1289 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1290 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1291 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1294 else if (!(descr->style & LBS_MULTIPLESEL))
1296 /* Set selection to new caret item */
1297 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1302 /***********************************************************************
1303 * LISTBOX_InsertItem
1305 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1306 LPSTR str, DWORD data )
1308 LB_ITEMDATA *item;
1309 INT max_items;
1310 INT oldfocus = descr->focus_item;
1312 if (index == -1) index = descr->nb_items;
1313 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1314 if (!descr->items) max_items = 0;
1315 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1316 if (descr->nb_items == max_items)
1318 /* We need to grow the array */
1319 max_items += LB_ARRAY_GRANULARITY;
1320 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1321 max_items * sizeof(LB_ITEMDATA) )))
1323 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1324 return LB_ERRSPACE;
1326 descr->items = item;
1329 /* Insert the item structure */
1331 item = &descr->items[index];
1332 if (index < descr->nb_items)
1333 RtlMoveMemory( item + 1, item,
1334 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1335 item->str = str;
1336 item->data = data;
1337 item->height = 0;
1338 item->selected = FALSE;
1339 descr->nb_items++;
1341 /* Get item height */
1343 if (descr->style & LBS_OWNERDRAWVARIABLE)
1345 MEASUREITEMSTRUCT mis;
1346 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1348 mis.CtlType = ODT_LISTBOX;
1349 mis.CtlID = id;
1350 mis.itemID = index;
1351 mis.itemData = descr->items[index].data;
1352 mis.itemHeight = descr->item_height;
1353 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1354 item->height = mis.itemHeight ? mis.itemHeight : 1;
1355 TRACE("[%04x]: measure item %d (%s) = %d\n",
1356 wnd->hwndSelf, index, str ? str : "", item->height );
1359 /* Repaint the items */
1361 LISTBOX_UpdateScroll( wnd, descr );
1362 LISTBOX_InvalidateItems( wnd, descr, index );
1364 /* Move selection and focused item */
1365 /* If listbox was empty, set focus to the first item */
1366 if (descr->nb_items == 1)
1367 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1368 /* single select don't change selection index in win31 */
1369 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1371 descr->selected_item++;
1372 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1374 else
1376 if (index <= descr->selected_item)
1378 descr->selected_item++;
1379 descr->focus_item = oldfocus; /* focus not changed */
1382 return LB_OKAY;
1386 /***********************************************************************
1387 * LISTBOX_InsertString
1389 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1390 LPCSTR str )
1392 LPSTR new_str = NULL;
1393 DWORD data = 0;
1394 LRESULT ret;
1396 if (HAS_STRINGS(descr))
1398 if (!str) str="";
1399 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1401 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1402 return LB_ERRSPACE;
1405 else data = (DWORD)str;
1407 if (index == -1) index = descr->nb_items;
1408 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1410 if (new_str) HeapFree( descr->heap, 0, new_str );
1411 return ret;
1414 TRACE("[%04x]: added item %d '%s'\n",
1415 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1416 return index;
1420 /***********************************************************************
1421 * LISTBOX_DeleteItem
1423 * Delete the content of an item. 'index' must be a valid index.
1425 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1427 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1428 * while Win95 sends it for all items with user data.
1429 * It's probably better to send it too often than not
1430 * often enough, so this is what we do here.
1432 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1434 DELETEITEMSTRUCT dis;
1435 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1437 dis.CtlType = ODT_LISTBOX;
1438 dis.CtlID = id;
1439 dis.itemID = index;
1440 dis.hwndItem = wnd->hwndSelf;
1441 dis.itemData = descr->items[index].data;
1442 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1444 if (HAS_STRINGS(descr) && descr->items[index].str)
1445 HeapFree( descr->heap, 0, descr->items[index].str );
1449 /***********************************************************************
1450 * LISTBOX_RemoveItem
1452 * Remove an item from the listbox and delete its content.
1454 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1456 LB_ITEMDATA *item;
1457 INT max_items;
1459 if (index == -1) index = descr->nb_items - 1;
1460 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1461 LISTBOX_DeleteItem( wnd, descr, index );
1463 /* Remove the item */
1465 item = &descr->items[index];
1466 if (index < descr->nb_items-1)
1467 RtlMoveMemory( item, item + 1,
1468 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1469 descr->nb_items--;
1470 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1472 /* Shrink the item array if possible */
1474 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1475 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1477 max_items -= LB_ARRAY_GRANULARITY;
1478 item = HeapReAlloc( descr->heap, 0, descr->items,
1479 max_items * sizeof(LB_ITEMDATA) );
1480 if (item) descr->items = item;
1482 /* Repaint the items */
1484 LISTBOX_UpdateScroll( wnd, descr );
1485 /* if we removed the scrollbar, reset the top of the list
1486 (correct for owner-drawn ???) */
1487 if (descr->nb_items == descr->page_size)
1488 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1490 /* Move selection and focused item */
1491 if (!IS_MULTISELECT(descr))
1493 if (index == descr->selected_item)
1494 descr->selected_item = -1;
1495 else if (index < descr->selected_item)
1497 descr->selected_item--;
1498 if (ISWIN31) /* win 31 do not change the selected item number */
1499 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1502 LISTBOX_InvalidateItems( wnd, descr, index );
1503 if (descr->focus_item >= descr->nb_items)
1505 descr->focus_item = descr->nb_items - 1;
1506 if (descr->focus_item < 0) descr->focus_item = 0;
1508 return LB_OKAY;
1512 /***********************************************************************
1513 * LISTBOX_ResetContent
1515 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1517 INT i;
1519 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1520 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1521 descr->nb_items = 0;
1522 descr->top_item = 0;
1523 descr->selected_item = -1;
1524 descr->focus_item = 0;
1525 descr->anchor_item = -1;
1526 descr->items = NULL;
1527 LISTBOX_UpdateScroll( wnd, descr );
1528 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1532 /***********************************************************************
1533 * LISTBOX_SetCount
1535 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1537 LRESULT ret;
1539 if (HAS_STRINGS(descr)) return LB_ERR;
1540 /* FIXME: this is far from optimal... */
1541 if (count > descr->nb_items)
1543 while (count > descr->nb_items)
1544 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1545 return ret;
1547 else if (count < descr->nb_items)
1549 while (count < descr->nb_items)
1550 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1551 return ret;
1553 return LB_OKAY;
1557 /***********************************************************************
1558 * LISTBOX_Directory
1560 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1561 LPCSTR filespec, BOOL long_names )
1563 HANDLE handle;
1564 LRESULT ret = LB_OKAY;
1565 WIN32_FIND_DATAA entry;
1566 int pos;
1568 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1570 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1572 else
1576 char buffer[270];
1577 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1579 if (!(attrib & DDL_DIRECTORY) ||
1580 !strcmp( entry.cAlternateFileName, "." )) continue;
1581 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1582 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1584 else /* not a directory */
1586 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1587 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1589 if ((attrib & DDL_EXCLUSIVE) &&
1590 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1591 continue;
1592 #undef ATTRIBS
1593 if (long_names) strcpy( buffer, entry.cFileName );
1594 else strcpy( buffer, entry.cAlternateFileName );
1596 if (!long_names) CharLowerA( buffer );
1597 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1598 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1599 break;
1600 } while (FindNextFileA( handle, &entry ));
1601 FindClose( handle );
1604 if ((ret >= 0) && (attrib & DDL_DRIVES))
1606 char buffer[] = "[-a-]";
1607 int drive;
1608 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1610 if (!DRIVE_IsValid(drive)) continue;
1611 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1612 break;
1615 return ret;
1619 /***********************************************************************
1620 * LISTBOX_HandleVScroll
1622 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1623 WPARAM wParam, LPARAM lParam )
1625 SCROLLINFO info;
1627 if (descr->style & LBS_MULTICOLUMN) return 0;
1628 switch(LOWORD(wParam))
1630 case SB_LINEUP:
1631 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1632 break;
1633 case SB_LINEDOWN:
1634 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1635 break;
1636 case SB_PAGEUP:
1637 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1638 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1639 break;
1640 case SB_PAGEDOWN:
1641 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1642 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1643 break;
1644 case SB_THUMBPOSITION:
1645 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1646 break;
1647 case SB_THUMBTRACK:
1648 info.cbSize = sizeof(info);
1649 info.fMask = SIF_TRACKPOS;
1650 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1651 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1652 break;
1653 case SB_TOP:
1654 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1655 break;
1656 case SB_BOTTOM:
1657 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1658 break;
1660 return 0;
1664 /***********************************************************************
1665 * LISTBOX_HandleHScroll
1667 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1668 WPARAM wParam, LPARAM lParam )
1670 SCROLLINFO info;
1671 INT page;
1673 if (descr->style & LBS_MULTICOLUMN)
1675 switch(LOWORD(wParam))
1677 case SB_LINELEFT:
1678 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1679 TRUE );
1680 break;
1681 case SB_LINERIGHT:
1682 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1683 TRUE );
1684 break;
1685 case SB_PAGELEFT:
1686 page = descr->width / descr->column_width;
1687 if (page < 1) page = 1;
1688 LISTBOX_SetTopItem( wnd, descr,
1689 descr->top_item - page * descr->page_size, TRUE );
1690 break;
1691 case SB_PAGERIGHT:
1692 page = descr->width / descr->column_width;
1693 if (page < 1) page = 1;
1694 LISTBOX_SetTopItem( wnd, descr,
1695 descr->top_item + page * descr->page_size, TRUE );
1696 break;
1697 case SB_THUMBPOSITION:
1698 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1699 TRUE );
1700 break;
1701 case SB_THUMBTRACK:
1702 info.cbSize = sizeof(info);
1703 info.fMask = SIF_TRACKPOS;
1704 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1705 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1706 TRUE );
1707 break;
1708 case SB_LEFT:
1709 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1710 break;
1711 case SB_RIGHT:
1712 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1713 break;
1716 else if (descr->horz_extent)
1718 switch(LOWORD(wParam))
1720 case SB_LINELEFT:
1721 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1722 break;
1723 case SB_LINERIGHT:
1724 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1725 break;
1726 case SB_PAGELEFT:
1727 LISTBOX_SetHorizontalPos( wnd, descr,
1728 descr->horz_pos - descr->width );
1729 break;
1730 case SB_PAGERIGHT:
1731 LISTBOX_SetHorizontalPos( wnd, descr,
1732 descr->horz_pos + descr->width );
1733 break;
1734 case SB_THUMBPOSITION:
1735 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1736 break;
1737 case SB_THUMBTRACK:
1738 info.cbSize = sizeof(info);
1739 info.fMask = SIF_TRACKPOS;
1740 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1741 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1742 break;
1743 case SB_LEFT:
1744 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1745 break;
1746 case SB_RIGHT:
1747 LISTBOX_SetHorizontalPos( wnd, descr,
1748 descr->horz_extent - descr->width );
1749 break;
1752 return 0;
1756 /***********************************************************************
1757 * LISTBOX_HandleLButtonDown
1759 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1760 WPARAM wParam, INT x, INT y )
1762 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1763 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1764 wnd->hwndSelf, x, y, index );
1765 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1766 if (index != -1)
1768 if (descr->style & LBS_EXTENDEDSEL)
1770 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1771 if (wParam & MK_CONTROL)
1773 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1774 LISTBOX_SetSelection( wnd, descr, index,
1775 !descr->items[index].selected,
1776 (descr->style & LBS_NOTIFY) != 0);
1778 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1780 else
1782 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1783 LISTBOX_SetSelection( wnd, descr, index,
1784 (!(descr->style & LBS_MULTIPLESEL) ||
1785 !descr->items[index].selected),
1786 (descr->style & LBS_NOTIFY) != 0 );
1790 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1791 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1792 : descr->lphc->self->hwndSelf ) ;
1794 descr->captured = TRUE;
1795 SetCapture( wnd->hwndSelf );
1796 if (index != -1 && !descr->lphc)
1798 if (descr->style & LBS_NOTIFY )
1799 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1800 MAKELPARAM( x, y ) );
1801 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1803 POINT pt;
1805 pt.x = x;
1806 pt.y = y;
1808 if (DragDetect( wnd->hwndSelf, pt ))
1809 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1812 return 0;
1816 /*************************************************************************
1817 * LISTBOX_HandleLButtonDownCombo [Internal]
1819 * Process LButtonDown message for the ComboListBox
1821 * PARAMS
1822 * pWnd [I] The windows internal structure
1823 * pDescr [I] The ListBox internal structure
1824 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1825 * x [I] X Mouse Coordinate
1826 * y [I] Y Mouse Coordinate
1828 * RETURNS
1829 * 0 since we are processing the WM_LBUTTONDOWN Message
1831 * NOTES
1832 * This function is only to be used when a ListBox is a ComboListBox
1835 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1836 WPARAM wParam, INT x, INT y)
1838 RECT clientRect, screenRect;
1839 POINT mousePos;
1841 mousePos.x = x;
1842 mousePos.y = y;
1844 GetClientRect(pWnd->hwndSelf, &clientRect);
1846 if(PtInRect(&clientRect, mousePos))
1848 /* MousePos is in client, resume normal processing */
1849 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1851 else
1853 POINT screenMousePos;
1854 HWND hWndOldCapture;
1856 /* Check the Non-Client Area */
1857 screenMousePos = mousePos;
1858 hWndOldCapture = GetCapture();
1859 ReleaseCapture();
1860 GetWindowRect(pWnd->hwndSelf, &screenRect);
1861 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1863 if(!PtInRect(&screenRect, screenMousePos))
1865 /* Close The Drop Down */
1866 SEND_NOTIFICATION( pWnd, pDescr, LBN_SELCANCEL );
1867 return 0;
1869 else
1871 /* Check to see the NC is a scrollbar */
1872 INT nHitTestType=0;
1873 /* Check Vertical scroll bar */
1874 if (pWnd->dwStyle & WS_VSCROLL)
1876 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1877 if (PtInRect( &clientRect, mousePos ))
1879 nHitTestType = HTVSCROLL;
1882 /* Check horizontal scroll bar */
1883 if (pWnd->dwStyle & WS_HSCROLL)
1885 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1886 if (PtInRect( &clientRect, mousePos ))
1888 nHitTestType = HTHSCROLL;
1891 /* Windows sends this message when a scrollbar is clicked
1894 if(nHitTestType != 0)
1896 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1897 MAKELONG(screenMousePos.x, screenMousePos.y));
1899 /* Resume the Capture after scrolling is complete
1901 if(hWndOldCapture != 0)
1903 SetCapture(hWndOldCapture);
1907 return 0;
1910 /***********************************************************************
1911 * LISTBOX_HandleLButtonUp
1913 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1915 if (LISTBOX_Timer != LB_TIMER_NONE)
1916 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1917 LISTBOX_Timer = LB_TIMER_NONE;
1918 if (descr->captured)
1920 descr->captured = FALSE;
1921 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1922 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
1923 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1925 return 0;
1929 /***********************************************************************
1930 * LISTBOX_HandleTimer
1932 * Handle scrolling upon a timer event.
1933 * Return TRUE if scrolling should continue.
1935 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1936 INT index, TIMER_DIRECTION dir )
1938 switch(dir)
1940 case LB_TIMER_UP:
1941 if (descr->top_item) index = descr->top_item - 1;
1942 else index = 0;
1943 break;
1944 case LB_TIMER_LEFT:
1945 if (descr->top_item) index -= descr->page_size;
1946 break;
1947 case LB_TIMER_DOWN:
1948 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1949 if (index == descr->focus_item) index++;
1950 if (index >= descr->nb_items) index = descr->nb_items - 1;
1951 break;
1952 case LB_TIMER_RIGHT:
1953 if (index + descr->page_size < descr->nb_items)
1954 index += descr->page_size;
1955 break;
1956 case LB_TIMER_NONE:
1957 break;
1959 if (index == descr->focus_item) return FALSE;
1960 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1961 return TRUE;
1965 /***********************************************************************
1966 * LISTBOX_HandleSystemTimer
1968 * WM_SYSTIMER handler.
1970 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1972 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1974 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1975 LISTBOX_Timer = LB_TIMER_NONE;
1977 return 0;
1981 /***********************************************************************
1982 * LISTBOX_HandleMouseMove
1984 * WM_MOUSEMOVE handler.
1986 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1987 INT x, INT y )
1989 INT index;
1990 TIMER_DIRECTION dir;
1992 if (!descr->captured) return;
1994 if (descr->style & LBS_MULTICOLUMN)
1996 if (y < 0) y = 0;
1997 else if (y >= descr->item_height * descr->page_size)
1998 y = descr->item_height * descr->page_size - 1;
2000 if (x < 0)
2002 dir = LB_TIMER_LEFT;
2003 x = 0;
2005 else if (x >= descr->width)
2007 dir = LB_TIMER_RIGHT;
2008 x = descr->width - 1;
2010 else dir = LB_TIMER_NONE; /* inside */
2012 else
2014 if (y < 0) dir = LB_TIMER_UP; /* above */
2015 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2016 else dir = LB_TIMER_NONE; /* inside */
2019 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2020 if (index == -1) index = descr->focus_item;
2021 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2023 /* Start/stop the system timer */
2025 if (dir != LB_TIMER_NONE)
2026 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2027 else if (LISTBOX_Timer != LB_TIMER_NONE)
2028 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2029 LISTBOX_Timer = dir;
2033 /***********************************************************************
2034 * LISTBOX_HandleKeyDown
2036 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2038 INT caret = -1;
2039 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2040 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2041 bForceSelection = FALSE; /* only for single select list */
2043 if (descr->style & LBS_WANTKEYBOARDINPUT)
2045 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2046 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2047 wnd->hwndSelf );
2048 if (caret == -2) return 0;
2050 if (caret == -1) switch(wParam)
2052 case VK_LEFT:
2053 if (descr->style & LBS_MULTICOLUMN)
2055 bForceSelection = FALSE;
2056 if (descr->focus_item >= descr->page_size)
2057 caret = descr->focus_item - descr->page_size;
2058 break;
2060 /* fall through */
2061 case VK_UP:
2062 caret = descr->focus_item - 1;
2063 if (caret < 0) caret = 0;
2064 break;
2065 case VK_RIGHT:
2066 if (descr->style & LBS_MULTICOLUMN)
2068 bForceSelection = FALSE;
2069 if (descr->focus_item + descr->page_size < descr->nb_items)
2070 caret = descr->focus_item + descr->page_size;
2071 break;
2073 /* fall through */
2074 case VK_DOWN:
2075 caret = descr->focus_item + 1;
2076 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2077 break;
2079 case VK_PRIOR:
2080 if (descr->style & LBS_MULTICOLUMN)
2082 INT page = descr->width / descr->column_width;
2083 if (page < 1) page = 1;
2084 caret = descr->focus_item - (page * descr->page_size) + 1;
2086 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2087 if (caret < 0) caret = 0;
2088 break;
2089 case VK_NEXT:
2090 if (descr->style & LBS_MULTICOLUMN)
2092 INT page = descr->width / descr->column_width;
2093 if (page < 1) page = 1;
2094 caret = descr->focus_item + (page * descr->page_size) - 1;
2096 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2097 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2098 break;
2099 case VK_HOME:
2100 caret = 0;
2101 break;
2102 case VK_END:
2103 caret = descr->nb_items - 1;
2104 break;
2105 case VK_SPACE:
2106 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2107 else if (descr->style & LBS_MULTIPLESEL)
2109 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2110 !descr->items[descr->focus_item].selected,
2111 (descr->style & LBS_NOTIFY) != 0 );
2113 break;
2114 default:
2115 bForceSelection = FALSE;
2117 if (bForceSelection) /* focused item is used instead of key */
2118 caret = descr->focus_item;
2119 if (caret >= 0)
2121 if ((descr->style & LBS_EXTENDEDSEL) &&
2122 !(GetKeyState( VK_SHIFT ) & 0x8000))
2123 descr->anchor_item = caret;
2124 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2125 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2126 if (descr->style & LBS_NOTIFY)
2128 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2130 /* make sure that combo parent doesn't hide us */
2131 descr->lphc->wState |= CBF_NOROLLUP;
2133 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2136 return 0;
2140 /***********************************************************************
2141 * LISTBOX_HandleChar
2143 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2144 WPARAM wParam )
2146 INT caret = -1;
2147 char str[2];
2149 str[0] = wParam & 0xff;
2150 str[1] = '\0';
2152 if (descr->style & LBS_WANTKEYBOARDINPUT)
2154 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2155 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2156 wnd->hwndSelf );
2157 if (caret == -2) return 0;
2159 if (caret == -1)
2160 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2161 if (caret != -1)
2163 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2164 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2165 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2166 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2167 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2169 return 0;
2173 /***********************************************************************
2174 * LISTBOX_Create
2176 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2178 LB_DESCR *descr;
2179 MEASUREITEMSTRUCT mis;
2180 RECT rect;
2182 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2183 return FALSE;
2184 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2186 HeapFree( GetProcessHeap(), 0, descr );
2187 return FALSE;
2189 GetClientRect( wnd->hwndSelf, &rect );
2190 descr->owner = GetParent( wnd->hwndSelf );
2191 descr->style = wnd->dwStyle;
2192 descr->width = rect.right - rect.left;
2193 descr->height = rect.bottom - rect.top;
2194 descr->items = NULL;
2195 descr->nb_items = 0;
2196 descr->top_item = 0;
2197 descr->selected_item = -1;
2198 descr->focus_item = 0;
2199 descr->anchor_item = -1;
2200 descr->item_height = 1;
2201 descr->page_size = 1;
2202 descr->column_width = 150;
2203 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2204 descr->horz_pos = 0;
2205 descr->nb_tabs = 0;
2206 descr->tabs = NULL;
2207 descr->caret_on = TRUE;
2208 descr->captured = FALSE;
2209 descr->font = 0;
2210 descr->locale = 0; /* FIXME */
2211 descr->lphc = lphc;
2213 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2214 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2216 /* Win95 document "List Box Differences" from MSDN:
2217 If a list box in a version 3.x application has either the
2218 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2219 horizontal and vertical scroll bars.
2221 descr->style |= WS_VSCROLL | WS_HSCROLL;
2224 if( lphc )
2226 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2227 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2228 descr->owner = lphc->self->hwndSelf;
2231 *(LB_DESCR **)wnd->wExtra = descr;
2233 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2235 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2236 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2237 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2238 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2240 if (descr->style & LBS_OWNERDRAWFIXED)
2242 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2244 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2245 descr->item_height = lphc->fixedOwnerDrawHeight;
2247 else
2249 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2251 mis.CtlType = ODT_LISTBOX;
2252 mis.CtlID = id;
2253 mis.itemID = -1;
2254 mis.itemWidth = 0;
2255 mis.itemData = 0;
2256 mis.itemHeight = descr->item_height;
2257 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2258 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2262 return TRUE;
2266 /***********************************************************************
2267 * LISTBOX_Destroy
2269 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2271 LISTBOX_ResetContent( wnd, descr );
2272 HeapDestroy( descr->heap );
2273 HeapFree( GetProcessHeap(), 0, descr );
2274 wnd->wExtra[0] = 0;
2275 return TRUE;
2279 /***********************************************************************
2280 * ListBoxWndProc
2282 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2283 WPARAM wParam, LPARAM lParam )
2285 LRESULT ret;
2286 LB_DESCR *descr;
2287 HWND hwnd = wnd->hwndSelf;
2289 if (!wnd) return 0;
2290 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2292 switch (msg)
2294 case WM_CREATE:
2296 if (!LISTBOX_Create( wnd, NULL ))
2297 return -1;
2298 TRACE("creating wnd=%04x descr=%p\n",
2299 hwnd, *(LB_DESCR **)wnd->wExtra );
2300 return 0;
2302 case WM_NCCREATE:
2305 * When a listbox is not in a combobox and the look
2306 * is win95, the WS_BORDER style is replaced with
2307 * the WS_EX_CLIENTEDGE style.
2309 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2310 (wnd->dwStyle & WS_BORDER) )
2312 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2313 wnd->dwStyle &= ~ WS_BORDER;
2318 /* Ignore all other messages before we get a WM_CREATE */
2319 return DefWindowProcA( hwnd, msg, wParam, lParam );
2322 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2323 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2324 switch(msg)
2326 case LB_RESETCONTENT16:
2327 case LB_RESETCONTENT:
2328 LISTBOX_ResetContent( wnd, descr );
2329 return 0;
2331 case LB_ADDSTRING16:
2332 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2333 /* fall through */
2334 case LB_ADDSTRING:
2335 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2336 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2338 case LB_INSERTSTRING16:
2339 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2340 wParam = (INT)(INT16)wParam;
2341 /* fall through */
2342 case LB_INSERTSTRING:
2343 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2345 case LB_ADDFILE16:
2346 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2347 /* fall through */
2348 case LB_ADDFILE:
2349 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2350 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2352 case LB_DELETESTRING16:
2353 case LB_DELETESTRING:
2354 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2355 return descr->nb_items;
2356 else
2357 return LB_ERR;
2359 case LB_GETITEMDATA16:
2360 case LB_GETITEMDATA:
2361 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2362 return LB_ERR;
2363 return descr->items[wParam].data;
2365 case LB_SETITEMDATA16:
2366 case LB_SETITEMDATA:
2367 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2368 return LB_ERR;
2369 descr->items[wParam].data = (DWORD)lParam;
2370 return LB_OKAY;
2372 case LB_GETCOUNT16:
2373 case LB_GETCOUNT:
2374 return descr->nb_items;
2376 case LB_GETTEXT16:
2377 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2378 /* fall through */
2379 case LB_GETTEXT:
2380 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2382 case LB_GETTEXTLEN16:
2383 /* fall through */
2384 case LB_GETTEXTLEN:
2385 if (wParam >= descr->nb_items)
2386 return LB_ERR;
2387 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2388 : sizeof(DWORD));
2390 case LB_GETCURSEL16:
2391 case LB_GETCURSEL:
2392 if (descr->nb_items==0)
2393 return LB_ERR;
2394 if (!IS_MULTISELECT(descr))
2395 return descr->selected_item;
2396 /* else */
2397 if (descr->selected_item!=-1)
2398 return descr->selected_item;
2399 /* else */
2400 return descr->focus_item;
2401 /* otherwise, if the user tries to move the selection with the */
2402 /* arrow keys, we will give the application something to choke on */
2403 case LB_GETTOPINDEX16:
2404 case LB_GETTOPINDEX:
2405 return descr->top_item;
2407 case LB_GETITEMHEIGHT16:
2408 case LB_GETITEMHEIGHT:
2409 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2411 case LB_SETITEMHEIGHT16:
2412 lParam = LOWORD(lParam);
2413 /* fall through */
2414 case LB_SETITEMHEIGHT:
2415 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2417 case LB_ITEMFROMPOINT:
2419 POINT pt;
2420 RECT rect;
2422 pt.x = LOWORD(lParam);
2423 pt.y = HIWORD(lParam);
2424 rect.left = 0;
2425 rect.top = 0;
2426 rect.right = descr->width;
2427 rect.bottom = descr->height;
2429 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2430 !PtInRect( &rect, pt ) );
2433 case LB_SETCARETINDEX16:
2434 case LB_SETCARETINDEX:
2435 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2436 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2437 return LB_ERR;
2438 else if (ISWIN31)
2439 return wParam;
2440 else
2441 return LB_OKAY;
2443 case LB_GETCARETINDEX16:
2444 case LB_GETCARETINDEX:
2445 return descr->focus_item;
2447 case LB_SETTOPINDEX16:
2448 case LB_SETTOPINDEX:
2449 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2451 case LB_SETCOLUMNWIDTH16:
2452 case LB_SETCOLUMNWIDTH:
2453 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2455 case LB_GETITEMRECT16:
2457 RECT rect;
2458 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2459 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2461 return ret;
2463 case LB_GETITEMRECT:
2464 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2466 case LB_FINDSTRING16:
2467 wParam = (INT)(INT16)wParam;
2468 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2469 /* fall through */
2470 case LB_FINDSTRING:
2471 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2473 case LB_FINDSTRINGEXACT16:
2474 wParam = (INT)(INT16)wParam;
2475 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2476 /* fall through */
2477 case LB_FINDSTRINGEXACT:
2478 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2480 case LB_SELECTSTRING16:
2481 wParam = (INT)(INT16)wParam;
2482 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2483 /* fall through */
2484 case LB_SELECTSTRING:
2486 INT index = LISTBOX_FindString( wnd, descr, wParam,
2487 (LPCSTR)lParam, FALSE );
2488 if (index == LB_ERR)
2489 return LB_ERR;
2490 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2491 return index;
2494 case LB_GETSEL16:
2495 wParam = (INT)(INT16)wParam;
2496 /* fall through */
2497 case LB_GETSEL:
2498 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2499 return LB_ERR;
2500 return descr->items[wParam].selected;
2502 case LB_SETSEL16:
2503 lParam = (INT)(INT16)lParam;
2504 /* fall through */
2505 case LB_SETSEL:
2506 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2508 case LB_SETCURSEL16:
2509 wParam = (INT)(INT16)wParam;
2510 /* fall through */
2511 case LB_SETCURSEL:
2512 if (IS_MULTISELECT(descr)) return LB_ERR;
2513 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2514 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2516 case LB_GETSELCOUNT16:
2517 case LB_GETSELCOUNT:
2518 return LISTBOX_GetSelCount( wnd, descr );
2520 case LB_GETSELITEMS16:
2521 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2522 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2524 case LB_GETSELITEMS:
2525 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2527 case LB_SELITEMRANGE16:
2528 case LB_SELITEMRANGE:
2529 if (LOWORD(lParam) <= HIWORD(lParam))
2530 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2531 HIWORD(lParam), wParam );
2532 else
2533 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2534 LOWORD(lParam), wParam );
2536 case LB_SELITEMRANGEEX16:
2537 case LB_SELITEMRANGEEX:
2538 if ((INT)lParam >= (INT)wParam)
2539 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2540 else
2541 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2543 case LB_GETHORIZONTALEXTENT16:
2544 case LB_GETHORIZONTALEXTENT:
2545 return descr->horz_extent;
2547 case LB_SETHORIZONTALEXTENT16:
2548 case LB_SETHORIZONTALEXTENT:
2549 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2551 case LB_GETANCHORINDEX16:
2552 case LB_GETANCHORINDEX:
2553 return descr->anchor_item;
2555 case LB_SETANCHORINDEX16:
2556 wParam = (INT)(INT16)wParam;
2557 /* fall through */
2558 case LB_SETANCHORINDEX:
2559 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2560 return LB_ERR;
2561 descr->anchor_item = (INT)wParam;
2562 return LB_OKAY;
2564 case LB_DIR16:
2565 return LISTBOX_Directory( wnd, descr, wParam,
2566 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2568 case LB_DIR:
2569 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2571 case LB_GETLOCALE:
2572 return descr->locale;
2574 case LB_SETLOCALE:
2575 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2576 return LB_OKAY;
2578 case LB_INITSTORAGE:
2579 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2581 case LB_SETCOUNT:
2582 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2584 case LB_SETTABSTOPS16:
2585 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2586 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2588 case LB_SETTABSTOPS:
2589 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2591 case LB_CARETON16:
2592 case LB_CARETON:
2593 if (descr->caret_on)
2594 return LB_OKAY;
2595 descr->caret_on = TRUE;
2596 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2597 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2598 return LB_OKAY;
2600 case LB_CARETOFF16:
2601 case LB_CARETOFF:
2602 if (!descr->caret_on)
2603 return LB_OKAY;
2604 descr->caret_on = FALSE;
2605 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2606 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2607 return LB_OKAY;
2609 case WM_DESTROY:
2610 return LISTBOX_Destroy( wnd, descr );
2612 case WM_ENABLE:
2613 InvalidateRect( hwnd, NULL, TRUE );
2614 return 0;
2616 case WM_SETREDRAW:
2617 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2618 return 0;
2620 case WM_GETDLGCODE:
2621 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2623 case WM_PAINT:
2625 PAINTSTRUCT ps;
2626 HDC hdc = ( wParam ) ? ((HDC)wParam)
2627 : BeginPaint( hwnd, &ps );
2628 ret = LISTBOX_Paint( wnd, descr, hdc );
2629 if( !wParam ) EndPaint( hwnd, &ps );
2631 return ret;
2632 case WM_SIZE:
2633 LISTBOX_UpdateSize( wnd, descr );
2634 return 0;
2635 case WM_GETFONT:
2636 return descr->font;
2637 case WM_SETFONT:
2638 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2639 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2640 return 0;
2641 case WM_SETFOCUS:
2642 descr->caret_on = TRUE;
2643 if (descr->focus_item != -1)
2644 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2645 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2646 return 0;
2647 case WM_KILLFOCUS:
2648 if ((descr->focus_item != -1) && descr->caret_on)
2649 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2650 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2651 return 0;
2652 case WM_HSCROLL:
2653 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2654 case WM_VSCROLL:
2655 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2656 case WM_LBUTTONDOWN:
2657 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2658 (INT16)LOWORD(lParam),
2659 (INT16)HIWORD(lParam) );
2660 case WM_LBUTTONDBLCLK:
2661 if (descr->style & LBS_NOTIFY)
2662 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2663 return 0;
2664 case WM_MOUSEMOVE:
2665 if (GetCapture() == hwnd)
2666 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2667 (INT16)HIWORD(lParam) );
2668 return 0;
2669 case WM_LBUTTONUP:
2670 return LISTBOX_HandleLButtonUp( wnd, descr );
2671 case WM_KEYDOWN:
2672 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2673 case WM_CHAR:
2674 return LISTBOX_HandleChar( wnd, descr, wParam );
2675 case WM_SYSTIMER:
2676 return LISTBOX_HandleSystemTimer( wnd, descr );
2677 case WM_ERASEBKGND:
2678 if (IS_OWNERDRAW(descr))
2680 RECT rect;
2681 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2682 wParam, (LPARAM)wnd->hwndSelf );
2683 GetClientRect(hwnd, &rect);
2684 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2686 return 1;
2687 case WM_DROPFILES:
2688 if( !descr->lphc )
2689 return SendMessageA( descr->owner, msg, wParam, lParam );
2690 break;
2692 case WM_DROPOBJECT:
2693 case WM_QUERYDROPOBJECT:
2694 case WM_DRAGSELECT:
2695 case WM_DRAGMOVE:
2696 if( !descr->lphc )
2698 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2699 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2700 dragInfo->pt.y );
2701 return SendMessageA( descr->owner, msg, wParam, lParam );
2703 break;
2705 default:
2706 if ((msg >= WM_USER) && (msg < 0xc000))
2707 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2708 hwnd, msg, wParam, lParam );
2709 return DefWindowProcA( hwnd, msg, wParam, lParam );
2711 return 0;
2714 /***********************************************************************
2715 * ListBoxWndProc
2717 * This is just a wrapper for the real wndproc, it only does window locking
2718 * and unlocking.
2720 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2721 WPARAM wParam, LPARAM lParam )
2723 WND* wndPtr = WIN_FindWndPtr( hwnd );
2724 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2726 WIN_ReleaseWndPtr(wndPtr);
2727 return res;
2730 /***********************************************************************
2731 * COMBO_Directory
2733 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2735 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2737 if( wnd )
2739 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2740 if( descr )
2742 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2744 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2745 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2746 WIN_ReleaseWndPtr(wnd);
2747 return lRet;
2749 WIN_ReleaseWndPtr(wnd);
2751 return CB_ERR;
2754 /***********************************************************************
2755 * ComboLBWndProc_locked
2757 * The real combo listbox wndproc, but called with locked WND struct.
2759 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2760 WPARAM wParam, LPARAM lParam )
2762 LRESULT lRet = 0;
2763 HWND hwnd = wnd->hwndSelf;
2765 if (wnd)
2767 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2769 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2770 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2772 if( descr || msg == WM_CREATE )
2774 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2776 switch( msg )
2778 case WM_CREATE:
2779 #define lpcs ((LPCREATESTRUCTA)lParam)
2780 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2781 (UINT)lpcs->lpCreateParams);
2783 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2784 #undef lpcs
2785 return LISTBOX_Create( wnd, lphc );
2786 case WM_MOUSEMOVE:
2787 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2788 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2790 POINT mousePos;
2791 BOOL captured;
2792 RECT clientRect;
2794 mousePos.x = (INT16)LOWORD(lParam);
2795 mousePos.y = (INT16)HIWORD(lParam);
2798 * If we are in a dropdown combobox, we simulate that
2799 * the mouse is captured to show the tracking of the item.
2801 GetClientRect(hwnd, &clientRect);
2803 if (PtInRect( &clientRect, mousePos ))
2805 captured = descr->captured;
2806 descr->captured = TRUE;
2808 LISTBOX_HandleMouseMove( wnd, descr,
2809 mousePos.x, mousePos.y);
2811 descr->captured = captured;
2814 else
2816 LISTBOX_HandleMouseMove( wnd, descr,
2817 mousePos.x, mousePos.y);
2820 return 0;
2823 else
2826 * If we are in Win3.1 look, go with the default behavior.
2828 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2830 case WM_LBUTTONUP:
2831 if (TWEAK_WineLook > WIN31_LOOK)
2833 POINT mousePos;
2834 RECT clientRect;
2837 * If the mouse button "up" is not in the listbox,
2838 * we make sure there is no selection by re-selecting the
2839 * item that was selected when the listbox was made visible.
2841 mousePos.x = (INT16)LOWORD(lParam);
2842 mousePos.y = (INT16)HIWORD(lParam);
2844 GetClientRect(hwnd, &clientRect);
2847 * When the user clicks outside the combobox and the focus
2848 * is lost, the owning combobox will send a fake buttonup with
2849 * 0xFFFFFFF as the mouse location, we must also revert the
2850 * selection to the original selection.
2852 if ( (lParam == 0xFFFFFFFF) ||
2853 (!PtInRect( &clientRect, mousePos )) )
2855 LISTBOX_MoveCaret( wnd,
2856 descr,
2857 lphc->droppedIndex,
2858 FALSE );
2861 return LISTBOX_HandleLButtonUp( wnd, descr );
2862 case WM_LBUTTONDOWN:
2863 return LISTBOX_HandleLButtonDownCombo(wnd, descr, wParam,
2864 (INT16)LOWORD(lParam),
2865 (INT16)HIWORD(lParam) );
2866 case WM_MOUSEACTIVATE:
2867 return MA_NOACTIVATE;
2868 case WM_NCACTIVATE:
2869 return FALSE;
2870 case WM_KEYDOWN:
2871 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2873 /* for some reason(?) Windows makes it possible to
2874 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2876 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2877 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2878 && (wParam == VK_DOWN || wParam == VK_UP)) )
2880 COMBO_FlipListbox( lphc, FALSE );
2881 return 0;
2884 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2886 case LB_SETCURSEL16:
2887 case LB_SETCURSEL:
2888 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2889 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2890 return lRet;
2891 case WM_NCDESTROY:
2892 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2893 lphc->hWndLBox = 0;
2894 /* fall through */
2896 default:
2897 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2900 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2902 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2904 return lRet;
2907 /***********************************************************************
2908 * ComboLBWndProc
2910 * NOTE: in Windows, winproc address of the ComboLBox is the same
2911 * as that of the Listbox.
2913 * This is just a wrapper for the real wndproc, it only does window locking
2914 * and unlocking.
2916 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2917 WPARAM wParam, LPARAM lParam )
2919 WND *wnd = WIN_FindWndPtr( hwnd );
2920 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2922 WIN_ReleaseWndPtr(wnd);
2923 return res;