Avoid infinite loop in WINPOS_FindIconPos if the width of the parent
[wine.git] / controls / listbox.c
blobbebcb00d319e2e5a7c815c2d79e7d824fe95ca80
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"
21 DEFAULT_DEBUG_CHANNEL(listbox)
22 DECLARE_DEBUG_CHANNEL(combo)
24 /* Unimplemented yet:
25 * - LBS_NOSEL
26 * - LBS_USETABSTOPS
27 * - Unicode
28 * - Locale handling
31 /* Items array granularity */
32 #define LB_ARRAY_GRANULARITY 16
34 /* Scrolling timeout in ms */
35 #define LB_SCROLL_TIMEOUT 50
37 /* Listbox system timer id */
38 #define LB_TIMER_ID 2
40 /* Item structure */
41 typedef struct
43 LPSTR str; /* Item text */
44 BOOL selected; /* Is item selected? */
45 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
46 DWORD data; /* User data */
47 } LB_ITEMDATA;
49 /* Listbox structure */
50 typedef struct
52 HANDLE heap; /* Heap for this listbox */
53 HWND owner; /* Owner window to send notifications to */
54 UINT style; /* Window style */
55 INT width; /* Window width */
56 INT height; /* Window height */
57 LB_ITEMDATA *items; /* Array of items */
58 INT nb_items; /* Number of items */
59 INT top_item; /* Top visible item */
60 INT selected_item; /* Selected item */
61 INT focus_item; /* Item that has the focus */
62 INT anchor_item; /* Anchor item for extended selection */
63 INT item_height; /* Default item height */
64 INT page_size; /* Items per listbox page */
65 INT column_width; /* Column width for multi-column listboxes */
66 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
67 INT horz_pos; /* Horizontal position */
68 INT nb_tabs; /* Number of tabs in array */
69 INT *tabs; /* Array of tabs */
70 BOOL caret_on; /* Is caret on? */
71 BOOL captured; /* Is mouse captured? */
72 HFONT font; /* Current font */
73 LCID locale; /* Current locale for string comparisons */
74 LPHEADCOMBO lphc; /* ComboLBox */
75 } LB_DESCR;
78 #define IS_OWNERDRAW(descr) \
79 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
81 #define HAS_STRINGS(descr) \
82 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
85 #define IS_MULTISELECT(descr) \
86 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
88 #define SEND_NOTIFICATION(wnd,descr,code) \
89 (SendMessageA( (descr)->owner, WM_COMMAND, \
90 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
92 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
94 /* Current timer status */
95 typedef enum
97 LB_TIMER_NONE,
98 LB_TIMER_UP,
99 LB_TIMER_LEFT,
100 LB_TIMER_DOWN,
101 LB_TIMER_RIGHT
102 } TIMER_DIRECTION;
104 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
107 /***********************************************************************
108 * LISTBOX_Dump
110 void LISTBOX_Dump( WND *wnd )
112 INT i;
113 LB_ITEMDATA *item;
114 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
116 TRACE( "Listbox:\n" );
117 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
118 wnd->hwndSelf, (UINT)descr, descr->heap, descr->nb_items,
119 descr->top_item );
120 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
122 TRACE( "%4d: %-40s %d %08lx %3d\n",
123 i, item->str, item->selected, item->data, item->height );
128 /***********************************************************************
129 * LISTBOX_GetCurrentPageSize
131 * Return the current page size
133 static INT LISTBOX_GetCurrentPageSize( WND *wnd, LB_DESCR *descr )
135 INT i, height;
136 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
137 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
139 if ((height += descr->items[i].height) > descr->height) break;
141 if (i == descr->top_item) return 1;
142 else return i - descr->top_item;
146 /***********************************************************************
147 * LISTBOX_GetMaxTopIndex
149 * Return the maximum possible index for the top of the listbox.
151 static INT LISTBOX_GetMaxTopIndex( WND *wnd, LB_DESCR *descr )
153 INT max, page;
155 if (descr->style & LBS_OWNERDRAWVARIABLE)
157 page = descr->height;
158 for (max = descr->nb_items - 1; max >= 0; max--)
159 if ((page -= descr->items[max].height) < 0) break;
160 if (max < descr->nb_items - 1) max++;
162 else if (descr->style & LBS_MULTICOLUMN)
164 if ((page = descr->width / descr->column_width) < 1) page = 1;
165 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
166 max = (max - page) * descr->page_size;
168 else
170 max = descr->nb_items - descr->page_size;
172 if (max < 0) max = 0;
173 return max;
177 /***********************************************************************
178 * LISTBOX_UpdateScroll
180 * Update the scrollbars. Should be called whenever the content
181 * of the listbox changes.
183 static void LISTBOX_UpdateScroll( WND *wnd, LB_DESCR *descr )
185 SCROLLINFO info;
187 if (!(descr->style & WS_VSCROLL)) return;
188 /* It is important that we check descr->style, and not wnd->dwStyle,
189 for WS_VSCROLL, as the former is exactly the one passed in
190 argument to CreateWindow.
191 In Windows (and from now on in Wine :) a listbox created
192 with such a style (no WS_SCROLL) does not update
193 the scrollbar with listbox-related data, thus letting
194 the programmer use it for his/her own purposes. */
196 if (descr->style & LBS_NOREDRAW) return;
197 info.cbSize = sizeof(info);
199 if (descr->style & LBS_MULTICOLUMN)
201 info.nMin = 0;
202 info.nMax = (descr->nb_items - 1) / descr->page_size;
203 info.nPos = descr->top_item / descr->page_size;
204 info.nPage = descr->width / descr->column_width;
205 if (info.nPage < 1) info.nPage = 1;
206 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
207 if (descr->style & LBS_DISABLENOSCROLL)
208 info.fMask |= SIF_DISABLENOSCROLL;
209 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
210 info.nMax = 0;
211 info.fMask = SIF_RANGE;
212 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
214 else
216 info.nMin = 0;
217 info.nMax = descr->nb_items - 1;
218 info.nPos = descr->top_item;
219 info.nPage = LISTBOX_GetCurrentPageSize( wnd, descr );
220 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
221 if (descr->style & LBS_DISABLENOSCROLL)
222 info.fMask |= SIF_DISABLENOSCROLL;
223 SetScrollInfo( wnd->hwndSelf, SB_VERT, &info, TRUE );
225 if (descr->horz_extent)
227 info.nMin = 0;
228 info.nMax = descr->horz_extent - 1;
229 info.nPos = descr->horz_pos;
230 info.nPage = descr->width;
231 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
232 if (descr->style & LBS_DISABLENOSCROLL)
233 info.fMask |= SIF_DISABLENOSCROLL;
234 SetScrollInfo( wnd->hwndSelf, SB_HORZ, &info, TRUE );
240 /***********************************************************************
241 * LISTBOX_SetTopItem
243 * Set the top item of the listbox, scrolling up or down if necessary.
245 static LRESULT LISTBOX_SetTopItem( WND *wnd, LB_DESCR *descr, INT index,
246 BOOL scroll )
248 INT max = LISTBOX_GetMaxTopIndex( wnd, descr );
249 if (index > max) index = max;
250 if (index < 0) index = 0;
251 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
252 if (descr->top_item == index) return LB_OKAY;
253 if (descr->style & LBS_MULTICOLUMN)
255 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
256 if (scroll && (abs(diff) < descr->width))
257 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
258 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
260 else
261 scroll = FALSE;
263 else if (scroll)
265 INT diff;
266 if (descr->style & LBS_OWNERDRAWVARIABLE)
268 INT i;
269 diff = 0;
270 if (index > descr->top_item)
272 for (i = index - 1; i >= descr->top_item; i--)
273 diff -= descr->items[i].height;
275 else
277 for (i = index; i < descr->top_item; i++)
278 diff += descr->items[i].height;
281 else
282 diff = (descr->top_item - index) * descr->item_height;
284 if (abs(diff) < descr->height)
285 ScrollWindowEx( wnd->hwndSelf, 0, diff, NULL, NULL, 0, NULL,
286 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
287 else
288 scroll = FALSE;
290 if (!scroll) InvalidateRect( wnd->hwndSelf, NULL, TRUE );
291 descr->top_item = index;
292 LISTBOX_UpdateScroll( wnd, descr );
293 return LB_OKAY;
297 /***********************************************************************
298 * LISTBOX_UpdatePage
300 * Update the page size. Should be called when the size of
301 * the client area or the item height changes.
303 static void LISTBOX_UpdatePage( WND *wnd, LB_DESCR *descr )
305 INT page_size;
307 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
308 page_size = 1;
309 if (page_size == descr->page_size) return;
310 descr->page_size = page_size;
311 if (descr->style & LBS_MULTICOLUMN)
312 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
313 LISTBOX_SetTopItem( wnd, descr, descr->top_item, FALSE );
317 /***********************************************************************
318 * LISTBOX_UpdateSize
320 * Update the size of the listbox. Should be called when the size of
321 * the client area changes.
323 static void LISTBOX_UpdateSize( WND *wnd, LB_DESCR *descr )
325 RECT rect;
327 GetClientRect( wnd->hwndSelf, &rect );
328 descr->width = rect.right - rect.left;
329 descr->height = rect.bottom - rect.top;
330 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !IS_OWNERDRAW(descr))
332 if ((descr->height > descr->item_height) &&
333 (descr->height % descr->item_height))
335 TRACE("[%04x]: changing height %d -> %d\n",
336 wnd->hwndSelf, descr->height,
337 descr->height - descr->height%descr->item_height );
338 SetWindowPos( wnd->hwndSelf, 0, 0, 0,
339 wnd->rectWindow.right - wnd->rectWindow.left,
340 wnd->rectWindow.bottom - wnd->rectWindow.top -
341 (descr->height % descr->item_height),
342 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
343 return;
346 TRACE("[%04x]: new size = %d,%d\n",
347 wnd->hwndSelf, descr->width, descr->height );
348 LISTBOX_UpdatePage( wnd, descr );
349 LISTBOX_UpdateScroll( wnd, descr );
353 /***********************************************************************
354 * LISTBOX_GetItemRect
356 * Get the rectangle enclosing an item, in listbox client coordinates.
357 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
359 static LRESULT LISTBOX_GetItemRect( WND *wnd, LB_DESCR *descr, INT index,
360 RECT *rect )
362 /* Index <= 0 is legal even on empty listboxes */
363 if (index && (index >= descr->nb_items)) return -1;
364 SetRect( rect, 0, 0, descr->width, descr->height );
365 if (descr->style & LBS_MULTICOLUMN)
367 INT col = (index / descr->page_size) -
368 (descr->top_item / descr->page_size);
369 rect->left += col * descr->column_width;
370 rect->right = rect->left + descr->column_width;
371 rect->top += (index % descr->page_size) * descr->item_height;
372 rect->bottom = rect->top + descr->item_height;
374 else if (descr->style & LBS_OWNERDRAWVARIABLE)
376 INT i;
377 rect->right += descr->horz_pos;
378 if ((index >= 0) && (index < descr->nb_items))
380 if (index < descr->top_item)
382 for (i = descr->top_item-1; i >= index; i--)
383 rect->top -= descr->items[i].height;
385 else
387 for (i = descr->top_item; i < index; i++)
388 rect->top += descr->items[i].height;
390 rect->bottom = rect->top + descr->items[index].height;
394 else
396 rect->top += (index - descr->top_item) * descr->item_height;
397 rect->bottom = rect->top + descr->item_height;
398 rect->right += descr->horz_pos;
401 return ((rect->left < descr->width) && (rect->right > 0) &&
402 (rect->top < descr->height) && (rect->bottom > 0));
406 /***********************************************************************
407 * LISTBOX_GetItemFromPoint
409 * Return the item nearest from point (x,y) (in client coordinates).
411 static INT LISTBOX_GetItemFromPoint( WND *wnd, LB_DESCR *descr,
412 INT x, INT y )
414 INT index = descr->top_item;
416 if (!descr->nb_items) return -1; /* No items */
417 if (descr->style & LBS_OWNERDRAWVARIABLE)
419 INT pos = 0;
420 if (y >= 0)
422 while (index < descr->nb_items)
424 if ((pos += descr->items[index].height) > y) break;
425 index++;
428 else
430 while (index > 0)
432 index--;
433 if ((pos -= descr->items[index].height) <= y) break;
437 else if (descr->style & LBS_MULTICOLUMN)
439 if (y >= descr->item_height * descr->page_size) return -1;
440 if (y >= 0) index += y / descr->item_height;
441 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
442 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
444 else
446 index += (y / descr->item_height);
448 if (index < 0) return 0;
449 if (index >= descr->nb_items) return -1;
450 return index;
454 /***********************************************************************
455 * LISTBOX_PaintItem
457 * Paint an item.
459 static void LISTBOX_PaintItem( WND *wnd, LB_DESCR *descr, HDC hdc,
460 const RECT *rect, INT index, UINT action )
462 LB_ITEMDATA *item = NULL;
463 if (index < descr->nb_items) item = &descr->items[index];
465 if (IS_OWNERDRAW(descr))
467 DRAWITEMSTRUCT dis;
468 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
470 if (!item)
472 if (action == ODA_FOCUS)
473 DrawFocusRect( hdc, rect );
474 else
475 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
476 return;
478 dis.CtlType = ODT_LISTBOX;
479 dis.CtlID = id;
480 dis.hwndItem = wnd->hwndSelf;
481 dis.itemAction = action;
482 dis.hDC = hdc;
483 dis.itemID = index;
484 dis.itemState = 0;
485 if (item && item->selected) dis.itemState |= ODS_SELECTED;
486 if ((descr->focus_item == index) &&
487 (descr->caret_on) &&
488 (GetFocus() == wnd->hwndSelf)) dis.itemState |= ODS_FOCUS;
489 if (wnd->dwStyle & WS_DISABLED) dis.itemState |= ODS_DISABLED;
490 dis.itemData = item ? item->data : 0;
491 dis.rcItem = *rect;
492 TRACE("[%04x]: drawitem %d (%s) action=%02x "
493 "state=%02x rect=%d,%d-%d,%d\n",
494 wnd->hwndSelf, index, item ? item->str : "", action,
495 dis.itemState, rect->left, rect->top,
496 rect->right, rect->bottom );
497 SendMessageA(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
499 else
501 COLORREF oldText = 0, oldBk = 0;
503 if (action == ODA_FOCUS)
505 DrawFocusRect( hdc, rect );
506 return;
508 if (item && item->selected)
510 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
511 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
514 TRACE("[%04x]: painting %d (%s) action=%02x "
515 "rect=%d,%d-%d,%d\n",
516 wnd->hwndSelf, index, item ? item->str : "", action,
517 rect->left, rect->top, rect->right, rect->bottom );
518 if (!item)
519 ExtTextOutA( hdc, rect->left + 1, rect->top,
520 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
521 else if (!(descr->style & LBS_USETABSTOPS))
522 ExtTextOutA( hdc, rect->left + 1, rect->top,
523 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
524 strlen(item->str), NULL );
525 else
527 /* Output empty string to paint background in the full width. */
528 ExtTextOutA( hdc, rect->left + 1, rect->top,
529 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
530 TabbedTextOutA( hdc, rect->left + 1 , rect->top,
531 item->str, strlen(item->str),
532 descr->nb_tabs, descr->tabs, 0);
534 if (item && item->selected)
536 SetBkColor( hdc, oldBk );
537 SetTextColor( hdc, oldText );
539 if ((descr->focus_item == index) &&
540 (descr->caret_on) &&
541 (GetFocus() == wnd->hwndSelf)) DrawFocusRect( hdc, rect );
546 /***********************************************************************
547 * LISTBOX_SetRedraw
549 * Change the redraw flag.
551 static void LISTBOX_SetRedraw( WND *wnd, LB_DESCR *descr, BOOL on )
553 if (on)
555 if (!(descr->style & LBS_NOREDRAW)) return;
556 descr->style &= ~LBS_NOREDRAW;
557 LISTBOX_UpdateScroll( wnd, descr );
559 else descr->style |= LBS_NOREDRAW;
563 /***********************************************************************
564 * LISTBOX_RepaintItem
566 * Repaint a single item synchronously.
568 static void LISTBOX_RepaintItem( WND *wnd, LB_DESCR *descr, INT index,
569 UINT action )
571 HDC hdc;
572 RECT rect;
573 HFONT oldFont = 0;
574 HBRUSH hbrush, oldBrush = 0;
576 /* Do not repaint the item if the item is not visible */
577 if ((descr->style & LBS_NOREDRAW) || !IsWindowVisible(wnd->hwndSelf)) return;
579 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) != 1) return;
580 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE ))) return;
581 if (descr->font) oldFont = SelectObject( hdc, descr->font );
582 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
583 hdc, (LPARAM)wnd->hwndSelf );
584 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
585 if (wnd->dwStyle & WS_DISABLED)
586 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
587 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
588 LISTBOX_PaintItem( wnd, descr, hdc, &rect, index, action );
589 if (oldFont) SelectObject( hdc, oldFont );
590 if (oldBrush) SelectObject( hdc, oldBrush );
591 ReleaseDC( wnd->hwndSelf, hdc );
595 /***********************************************************************
596 * LISTBOX_InitStorage
598 static LRESULT LISTBOX_InitStorage( WND *wnd, LB_DESCR *descr, INT nb_items,
599 DWORD bytes )
601 LB_ITEMDATA *item;
603 nb_items += LB_ARRAY_GRANULARITY - 1;
604 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
605 if (descr->items)
606 nb_items += HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
607 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
608 nb_items * sizeof(LB_ITEMDATA) )))
610 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
611 return LB_ERRSPACE;
613 descr->items = item;
614 return LB_OKAY;
618 /***********************************************************************
619 * LISTBOX_SetTabStops
621 static BOOL LISTBOX_SetTabStops( WND *wnd, LB_DESCR *descr, INT count,
622 LPINT tabs, BOOL short_ints )
624 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
625 if (descr->tabs) HeapFree( descr->heap, 0, descr->tabs );
626 if (!(descr->nb_tabs = count))
628 descr->tabs = NULL;
629 return TRUE;
631 /* FIXME: count = 1 */
632 if (!(descr->tabs = (INT *)HeapAlloc( descr->heap, 0,
633 descr->nb_tabs * sizeof(INT) )))
634 return FALSE;
635 if (short_ints)
637 INT i;
638 LPINT16 p = (LPINT16)tabs;
640 TRACE("[%04x]: settabstops ", wnd->hwndSelf );
641 for (i = 0; i < descr->nb_tabs; i++) {
642 descr->tabs[i] = *p++<<1; /* FIXME */
643 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
645 if (TRACE_ON(listbox)) DPRINTF("\n");
647 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
648 /* FIXME: repaint the window? */
649 return TRUE;
653 /***********************************************************************
654 * LISTBOX_GetText
656 static LRESULT LISTBOX_GetText( WND *wnd, LB_DESCR *descr, INT index,
657 LPSTR buffer )
659 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
660 if (HAS_STRINGS(descr))
662 if (!buffer)
663 return strlen(descr->items[index].str);
664 lstrcpyA( buffer, descr->items[index].str );
665 return strlen(buffer);
666 } else {
667 if (buffer)
668 *((LPDWORD)buffer)=*(LPDWORD)(&descr->items[index].data);
669 return sizeof(DWORD);
674 /***********************************************************************
675 * LISTBOX_FindStringPos
677 * Find the nearest string located before a given string in sort order.
678 * If 'exact' is TRUE, return an error if we don't get an exact match.
680 static INT LISTBOX_FindStringPos( WND *wnd, LB_DESCR *descr, LPCSTR str,
681 BOOL exact )
683 INT index, min, max, res = -1;
685 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
686 min = 0;
687 max = descr->nb_items;
688 while (min != max)
690 index = (min + max) / 2;
691 if (HAS_STRINGS(descr))
692 res = lstrcmpiA( descr->items[index].str, str );
693 else
695 COMPAREITEMSTRUCT cis;
696 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
698 cis.CtlType = ODT_LISTBOX;
699 cis.CtlID = id;
700 cis.hwndItem = wnd->hwndSelf;
701 cis.itemID1 = index;
702 cis.itemData1 = descr->items[index].data;
703 cis.itemID2 = -1;
704 cis.itemData2 = (DWORD)str;
705 cis.dwLocaleId = descr->locale;
706 res = SendMessageA( descr->owner, WM_COMPAREITEM,
707 id, (LPARAM)&cis );
709 if (!res) return index;
710 if (res > 0) max = index;
711 else min = index + 1;
713 return exact ? -1 : max;
717 /***********************************************************************
718 * LISTBOX_FindFileStrPos
720 * Find the nearest string located before a given string in directory
721 * sort order (i.e. first files, then directories, then drives).
723 static INT LISTBOX_FindFileStrPos( WND *wnd, LB_DESCR *descr, LPCSTR str )
725 INT min, max, res = -1;
727 if (!HAS_STRINGS(descr))
728 return LISTBOX_FindStringPos( wnd, descr, str, FALSE );
729 min = 0;
730 max = descr->nb_items;
731 while (min != max)
733 INT index = (min + max) / 2;
734 const char *p = descr->items[index].str;
735 if (*p == '[') /* drive or directory */
737 if (*str != '[') res = -1;
738 else if (p[1] == '-') /* drive */
740 if (str[1] == '-') res = str[2] - p[2];
741 else res = -1;
743 else /* directory */
745 if (str[1] == '-') res = 1;
746 else res = lstrcmpiA( str, p );
749 else /* filename */
751 if (*str == '[') res = 1;
752 else res = lstrcmpiA( str, p );
754 if (!res) return index;
755 if (res < 0) max = index;
756 else min = index + 1;
758 return max;
762 /***********************************************************************
763 * LISTBOX_FindString
765 * Find the item beginning with a given string.
767 static INT LISTBOX_FindString( WND *wnd, LB_DESCR *descr, INT start,
768 LPCSTR str, BOOL exact )
770 INT i;
771 LB_ITEMDATA *item;
773 if (start >= descr->nb_items) start = -1;
774 item = descr->items + start + 1;
775 if (HAS_STRINGS(descr))
777 if (!str || ! str[0] ) return LB_ERR;
778 if (exact)
780 for (i = start + 1; i < descr->nb_items; i++, item++)
781 if (!lstrcmpiA( str, item->str )) return i;
782 for (i = 0, item = descr->items; i <= start; i++, item++)
783 if (!lstrcmpiA( str, item->str )) return i;
785 else
787 /* Special case for drives and directories: ignore prefix */
788 #define CHECK_DRIVE(item) \
789 if ((item)->str[0] == '[') \
791 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
792 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
793 return i; \
796 INT len = strlen(str);
797 for (i = start + 1; i < descr->nb_items; i++, item++)
799 if (!lstrncmpiA( str, item->str, len )) return i;
800 CHECK_DRIVE(item);
802 for (i = 0, item = descr->items; i <= start; i++, item++)
804 if (!lstrncmpiA( str, item->str, len )) return i;
805 CHECK_DRIVE(item);
807 #undef CHECK_DRIVE
810 else
812 if (exact && (descr->style & LBS_SORT))
813 /* If sorted, use a WM_COMPAREITEM binary search */
814 return LISTBOX_FindStringPos( wnd, descr, str, TRUE );
816 /* Otherwise use a linear search */
817 for (i = start + 1; i < descr->nb_items; i++, item++)
818 if (item->data == (DWORD)str) return i;
819 for (i = 0, item = descr->items; i <= start; i++, item++)
820 if (item->data == (DWORD)str) return i;
822 return LB_ERR;
826 /***********************************************************************
827 * LISTBOX_GetSelCount
829 static LRESULT LISTBOX_GetSelCount( WND *wnd, LB_DESCR *descr )
831 INT i, count;
832 LB_ITEMDATA *item = descr->items;
834 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
835 for (i = count = 0; i < descr->nb_items; i++, item++)
836 if (item->selected) count++;
837 return count;
841 /***********************************************************************
842 * LISTBOX_GetSelItems16
844 static LRESULT LISTBOX_GetSelItems16( WND *wnd, LB_DESCR *descr, INT16 max,
845 LPINT16 array )
847 INT i, count;
848 LB_ITEMDATA *item = descr->items;
850 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
851 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
852 if (item->selected) array[count++] = (INT16)i;
853 return count;
857 /***********************************************************************
858 * LISTBOX_GetSelItems32
860 static LRESULT LISTBOX_GetSelItems( WND *wnd, LB_DESCR *descr, INT max,
861 LPINT array )
863 INT i, count;
864 LB_ITEMDATA *item = descr->items;
866 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
867 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
868 if (item->selected) array[count++] = i;
869 return count;
873 /***********************************************************************
874 * LISTBOX_Paint
876 static LRESULT LISTBOX_Paint( WND *wnd, LB_DESCR *descr, HDC hdc )
878 INT i, col_pos = descr->page_size - 1;
879 RECT rect;
880 HFONT oldFont = 0;
881 HBRUSH hbrush, oldBrush = 0;
883 SetRect( &rect, 0, 0, descr->width, descr->height );
884 if (descr->style & LBS_NOREDRAW) return 0;
885 if (descr->style & LBS_MULTICOLUMN)
886 rect.right = rect.left + descr->column_width;
887 else if (descr->horz_pos)
889 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
890 rect.right += descr->horz_pos;
893 if (descr->font) oldFont = SelectObject( hdc, descr->font );
894 hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
895 hdc, (LPARAM)wnd->hwndSelf );
896 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
897 if (wnd->dwStyle & WS_DISABLED)
898 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
900 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
901 (GetFocus() == wnd->hwndSelf))
903 /* Special case for empty listbox: paint focus rect */
904 rect.bottom = rect.top + descr->item_height;
905 LISTBOX_PaintItem( wnd, descr, hdc, &rect, descr->focus_item,
906 ODA_FOCUS );
907 rect.top = rect.bottom;
910 for (i = descr->top_item; i < descr->nb_items; i++)
912 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
913 rect.bottom = rect.top + descr->item_height;
914 else
915 rect.bottom = rect.top + descr->items[i].height;
917 LISTBOX_PaintItem( wnd, descr, hdc, &rect, i, ODA_DRAWENTIRE );
918 rect.top = rect.bottom;
920 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
922 if (!IS_OWNERDRAW(descr))
924 /* Clear the bottom of the column */
925 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
926 if (rect.top < descr->height)
928 rect.bottom = descr->height;
929 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
930 &rect, NULL, 0, NULL );
934 /* Go to the next column */
935 rect.left += descr->column_width;
936 rect.right += descr->column_width;
937 rect.top = 0;
938 col_pos = descr->page_size - 1;
940 else
942 col_pos--;
943 if (rect.top >= descr->height) break;
947 if (!IS_OWNERDRAW(descr))
949 /* Clear the remainder of the client area */
950 SetBkColor( hdc, GetSysColor( COLOR_WINDOW ) );
951 if (rect.top < descr->height)
953 rect.bottom = descr->height;
954 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
955 &rect, NULL, 0, NULL );
957 if (rect.right < descr->width)
959 rect.left = rect.right;
960 rect.right = descr->width;
961 rect.top = 0;
962 rect.bottom = descr->height;
963 ExtTextOutA( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
964 &rect, NULL, 0, NULL );
967 if (oldFont) SelectObject( hdc, oldFont );
968 if (oldBrush) SelectObject( hdc, oldBrush );
969 return 0;
973 /***********************************************************************
974 * LISTBOX_InvalidateItems
976 * Invalidate all items from a given item. If the specified item is not
977 * visible, nothing happens.
979 static void LISTBOX_InvalidateItems( WND *wnd, LB_DESCR *descr, INT index )
981 RECT rect;
983 if (LISTBOX_GetItemRect( wnd, descr, index, &rect ) == 1)
985 rect.bottom = descr->height;
986 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
987 if (descr->style & LBS_MULTICOLUMN)
989 /* Repaint the other columns */
990 rect.left = rect.right;
991 rect.right = descr->width;
992 rect.top = 0;
993 InvalidateRect( wnd->hwndSelf, &rect, TRUE );
999 /***********************************************************************
1000 * LISTBOX_GetItemHeight
1002 static LRESULT LISTBOX_GetItemHeight( WND *wnd, LB_DESCR *descr, INT index )
1004 if (descr->style & LBS_OWNERDRAWVARIABLE)
1006 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1007 return descr->items[index].height;
1009 else return descr->item_height;
1013 /***********************************************************************
1014 * LISTBOX_SetItemHeight
1016 static LRESULT LISTBOX_SetItemHeight( WND *wnd, LB_DESCR *descr, INT index,
1017 UINT height )
1019 if (!height) height = 1;
1021 if (descr->style & LBS_OWNERDRAWVARIABLE)
1023 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1024 TRACE("[%04x]: item %d height = %d\n",
1025 wnd->hwndSelf, index, height );
1026 descr->items[index].height = height;
1027 LISTBOX_UpdateScroll( wnd, descr );
1028 LISTBOX_InvalidateItems( wnd, descr, index );
1030 else if (height != descr->item_height)
1032 TRACE("[%04x]: new height = %d\n",
1033 wnd->hwndSelf, height );
1034 descr->item_height = height;
1035 LISTBOX_UpdatePage( wnd, descr );
1036 LISTBOX_UpdateScroll( wnd, descr );
1037 InvalidateRect( wnd->hwndSelf, 0, TRUE );
1039 return LB_OKAY;
1043 /***********************************************************************
1044 * LISTBOX_SetHorizontalPos
1046 static void LISTBOX_SetHorizontalPos( WND *wnd, LB_DESCR *descr, INT pos )
1048 INT diff;
1050 if (pos > descr->horz_extent - descr->width)
1051 pos = descr->horz_extent - descr->width;
1052 if (pos < 0) pos = 0;
1053 if (!(diff = descr->horz_pos - pos)) return;
1054 TRACE("[%04x]: new horz pos = %d\n",
1055 wnd->hwndSelf, pos );
1056 descr->horz_pos = pos;
1057 LISTBOX_UpdateScroll( wnd, descr );
1058 if (abs(diff) < descr->width)
1059 ScrollWindowEx( wnd->hwndSelf, diff, 0, NULL, NULL, 0, NULL,
1060 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1061 else
1062 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1066 /***********************************************************************
1067 * LISTBOX_SetHorizontalExtent
1069 static LRESULT LISTBOX_SetHorizontalExtent( WND *wnd, LB_DESCR *descr,
1070 UINT extent )
1072 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1073 return LB_OKAY;
1074 if (extent <= 0) extent = 1;
1075 if (extent == descr->horz_extent) return LB_OKAY;
1076 TRACE("[%04x]: new horz extent = %d\n",
1077 wnd->hwndSelf, extent );
1078 descr->horz_extent = extent;
1079 if (descr->horz_pos > extent - descr->width)
1080 LISTBOX_SetHorizontalPos( wnd, descr, extent - descr->width );
1081 else
1082 LISTBOX_UpdateScroll( wnd, descr );
1083 return LB_OKAY;
1087 /***********************************************************************
1088 * LISTBOX_SetColumnWidth
1090 static LRESULT LISTBOX_SetColumnWidth( WND *wnd, LB_DESCR *descr, UINT width)
1092 width += 2; /* For left and right margin */
1093 if (width == descr->column_width) return LB_OKAY;
1094 TRACE("[%04x]: new column width = %d\n",
1095 wnd->hwndSelf, width );
1096 descr->column_width = width;
1097 LISTBOX_UpdatePage( wnd, descr );
1098 return LB_OKAY;
1102 /***********************************************************************
1103 * LISTBOX_SetFont
1105 * Returns the item height.
1107 static INT LISTBOX_SetFont( WND *wnd, LB_DESCR *descr, HFONT font )
1109 HDC hdc;
1110 HFONT oldFont = 0;
1111 TEXTMETRICA tm;
1113 descr->font = font;
1115 if (!(hdc = GetDCEx( wnd->hwndSelf, 0, DCX_CACHE )))
1117 ERR("unable to get DC.\n" );
1118 return 16;
1120 if (font) oldFont = SelectObject( hdc, font );
1121 GetTextMetricsA( hdc, &tm );
1122 if (oldFont) SelectObject( hdc, oldFont );
1123 ReleaseDC( wnd->hwndSelf, hdc );
1124 if (!IS_OWNERDRAW(descr))
1125 LISTBOX_SetItemHeight( wnd, descr, 0, tm.tmHeight );
1126 return tm.tmHeight ;
1130 /***********************************************************************
1131 * LISTBOX_MakeItemVisible
1133 * Make sure that a given item is partially or fully visible.
1135 static void LISTBOX_MakeItemVisible( WND *wnd, LB_DESCR *descr, INT index,
1136 BOOL fully )
1138 INT top;
1140 if (index <= descr->top_item) top = index;
1141 else if (descr->style & LBS_MULTICOLUMN)
1143 INT cols = descr->width;
1144 if (!fully) cols += descr->column_width - 1;
1145 if (cols >= descr->column_width) cols /= descr->column_width;
1146 else cols = 1;
1147 if (index < descr->top_item + (descr->page_size * cols)) return;
1148 top = index - descr->page_size * (cols - 1);
1150 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1152 INT height = fully ? descr->items[index].height : 1;
1153 for (top = index; top > descr->top_item; top--)
1154 if ((height += descr->items[top-1].height) > descr->height) break;
1156 else
1158 if (index < descr->top_item + descr->page_size) return;
1159 if (!fully && (index == descr->top_item + descr->page_size) &&
1160 (descr->height > (descr->page_size * descr->item_height))) return;
1161 top = index - descr->page_size + 1;
1163 LISTBOX_SetTopItem( wnd, descr, top, TRUE );
1167 /***********************************************************************
1168 * LISTBOX_SelectItemRange
1170 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1172 static LRESULT LISTBOX_SelectItemRange( WND *wnd, LB_DESCR *descr, INT first,
1173 INT last, BOOL on )
1175 INT i;
1177 /* A few sanity checks */
1179 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1180 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1181 if (last == -1) last = descr->nb_items - 1;
1182 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1183 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1184 /* selected_item reflects last selected/unselected item on multiple sel */
1185 descr->selected_item = last;
1187 if (on) /* Turn selection on */
1189 for (i = first; i <= last; i++)
1191 if (descr->items[i].selected) continue;
1192 descr->items[i].selected = TRUE;
1193 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1196 else /* Turn selection off */
1198 for (i = first; i <= last; i++)
1200 if (!descr->items[i].selected) continue;
1201 descr->items[i].selected = FALSE;
1202 LISTBOX_RepaintItem( wnd, descr, i, ODA_SELECT );
1205 return LB_OKAY;
1209 /***********************************************************************
1210 * LISTBOX_SetCaretIndex
1212 * NOTES
1213 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1216 static LRESULT LISTBOX_SetCaretIndex( WND *wnd, LB_DESCR *descr, INT index,
1217 BOOL fully_visible )
1219 INT oldfocus = descr->focus_item;
1221 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1222 if (index == oldfocus) return LB_OKAY;
1223 descr->focus_item = index;
1224 if ((oldfocus != -1) && descr->caret_on && (GetFocus() == wnd->hwndSelf))
1225 LISTBOX_RepaintItem( wnd, descr, oldfocus, ODA_FOCUS );
1227 LISTBOX_MakeItemVisible( wnd, descr, index, fully_visible );
1228 if (descr->caret_on && (GetFocus() == wnd->hwndSelf))
1229 LISTBOX_RepaintItem( wnd, descr, index, ODA_FOCUS );
1231 return LB_OKAY;
1235 /***********************************************************************
1236 * LISTBOX_SetSelection
1238 static LRESULT LISTBOX_SetSelection( WND *wnd, LB_DESCR *descr, INT index,
1239 BOOL on, BOOL send_notify )
1241 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1243 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1244 if (descr->style & LBS_MULTIPLESEL)
1246 if (index == -1) /* Select all items */
1247 return LISTBOX_SelectItemRange( wnd, descr, 0, -1, on );
1248 else /* Only one item */
1249 return LISTBOX_SelectItemRange( wnd, descr, index, index, on );
1251 else
1253 INT oldsel = descr->selected_item;
1254 if (index == oldsel) return LB_OKAY;
1255 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1256 if (index != -1) descr->items[index].selected = TRUE;
1257 descr->selected_item = index;
1258 if (oldsel != -1) LISTBOX_RepaintItem( wnd, descr, oldsel, 0 );
1259 if (index != -1) LISTBOX_RepaintItem( wnd, descr, index, ODA_SELECT );
1260 if (send_notify && descr->nb_items) SEND_NOTIFICATION( wnd, descr,
1261 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1262 else
1263 if( descr->lphc ) /* set selection change flag for parent combo */
1264 descr->lphc->wState |= CBF_SELCHANGE;
1266 return LB_OKAY;
1270 /***********************************************************************
1271 * LISTBOX_MoveCaret
1273 * Change the caret position and extend the selection to the new caret.
1275 static void LISTBOX_MoveCaret( WND *wnd, LB_DESCR *descr, INT index,
1276 BOOL fully_visible )
1278 LISTBOX_SetCaretIndex( wnd, descr, index, fully_visible );
1279 if (descr->style & LBS_EXTENDEDSEL)
1281 if (descr->anchor_item != -1)
1283 INT first = MIN( descr->focus_item, descr->anchor_item );
1284 INT last = MAX( descr->focus_item, descr->anchor_item );
1285 if (first > 0)
1286 LISTBOX_SelectItemRange( wnd, descr, 0, first - 1, FALSE );
1287 LISTBOX_SelectItemRange( wnd, descr, last + 1, -1, FALSE );
1288 LISTBOX_SelectItemRange( wnd, descr, first, last, TRUE );
1291 else if (!(descr->style & LBS_MULTIPLESEL))
1293 /* Set selection to new caret item */
1294 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
1299 /***********************************************************************
1300 * LISTBOX_InsertItem
1302 static LRESULT LISTBOX_InsertItem( WND *wnd, LB_DESCR *descr, INT index,
1303 LPSTR str, DWORD data )
1305 LB_ITEMDATA *item;
1306 INT max_items;
1307 INT oldfocus = descr->focus_item;
1309 if (index == -1) index = descr->nb_items;
1310 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1311 if (!descr->items) max_items = 0;
1312 else max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(*item);
1313 if (descr->nb_items == max_items)
1315 /* We need to grow the array */
1316 max_items += LB_ARRAY_GRANULARITY;
1317 if (!(item = HeapReAlloc( descr->heap, 0, descr->items,
1318 max_items * sizeof(LB_ITEMDATA) )))
1320 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1321 return LB_ERRSPACE;
1323 descr->items = item;
1326 /* Insert the item structure */
1328 item = &descr->items[index];
1329 if (index < descr->nb_items)
1330 RtlMoveMemory( item + 1, item,
1331 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1332 item->str = str;
1333 item->data = data;
1334 item->height = 0;
1335 item->selected = FALSE;
1336 descr->nb_items++;
1338 /* Get item height */
1340 if (descr->style & LBS_OWNERDRAWVARIABLE)
1342 MEASUREITEMSTRUCT mis;
1343 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1345 mis.CtlType = ODT_LISTBOX;
1346 mis.CtlID = id;
1347 mis.itemID = index;
1348 mis.itemData = descr->items[index].data;
1349 mis.itemHeight = descr->item_height;
1350 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1351 item->height = mis.itemHeight ? mis.itemHeight : 1;
1352 TRACE("[%04x]: measure item %d (%s) = %d\n",
1353 wnd->hwndSelf, index, str ? str : "", item->height );
1356 /* Repaint the items */
1358 LISTBOX_UpdateScroll( wnd, descr );
1359 LISTBOX_InvalidateItems( wnd, descr, index );
1361 /* Move selection and focused item */
1362 /* If listbox was empty, set focus to the first item */
1363 if (descr->nb_items == 1)
1364 LISTBOX_SetCaretIndex( wnd, descr, 0, FALSE );
1365 /* single select don't change selection index in win31 */
1366 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1368 descr->selected_item++;
1369 LISTBOX_SetSelection( wnd, descr, descr->selected_item-1, TRUE, FALSE );
1371 else
1373 if (index <= descr->selected_item)
1375 descr->selected_item++;
1376 descr->focus_item = oldfocus; /* focus not changed */
1379 return LB_OKAY;
1383 /***********************************************************************
1384 * LISTBOX_InsertString
1386 static LRESULT LISTBOX_InsertString( WND *wnd, LB_DESCR *descr, INT index,
1387 LPCSTR str )
1389 LPSTR new_str = NULL;
1390 DWORD data = 0;
1391 LRESULT ret;
1393 if (HAS_STRINGS(descr))
1395 if (!str) str="";
1396 if (!(new_str = HEAP_strdupA( descr->heap, 0, str )))
1398 SEND_NOTIFICATION( wnd, descr, LBN_ERRSPACE );
1399 return LB_ERRSPACE;
1402 else data = (DWORD)str;
1404 if (index == -1) index = descr->nb_items;
1405 if ((ret = LISTBOX_InsertItem( wnd, descr, index, new_str, data )) != 0)
1407 if (new_str) HeapFree( descr->heap, 0, new_str );
1408 return ret;
1411 TRACE("[%04x]: added item %d '%s'\n",
1412 wnd->hwndSelf, index, HAS_STRINGS(descr) ? new_str : "" );
1413 return index;
1417 /***********************************************************************
1418 * LISTBOX_DeleteItem
1420 * Delete the content of an item. 'index' must be a valid index.
1422 static void LISTBOX_DeleteItem( WND *wnd, LB_DESCR *descr, INT index )
1424 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1425 * while Win95 sends it for all items with user data.
1426 * It's probably better to send it too often than not
1427 * often enough, so this is what we do here.
1429 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1431 DELETEITEMSTRUCT dis;
1432 UINT id = (descr->lphc) ? ID_CB_LISTBOX : wnd->wIDmenu;
1434 dis.CtlType = ODT_LISTBOX;
1435 dis.CtlID = id;
1436 dis.itemID = index;
1437 dis.hwndItem = wnd->hwndSelf;
1438 dis.itemData = descr->items[index].data;
1439 SendMessageA( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1441 if (HAS_STRINGS(descr) && descr->items[index].str)
1442 HeapFree( descr->heap, 0, descr->items[index].str );
1446 /***********************************************************************
1447 * LISTBOX_RemoveItem
1449 * Remove an item from the listbox and delete its content.
1451 static LRESULT LISTBOX_RemoveItem( WND *wnd, LB_DESCR *descr, INT index )
1453 LB_ITEMDATA *item;
1454 INT max_items;
1456 if (index == -1) index = descr->nb_items - 1;
1457 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1458 LISTBOX_DeleteItem( wnd, descr, index );
1460 /* Remove the item */
1462 item = &descr->items[index];
1463 if (index < descr->nb_items-1)
1464 RtlMoveMemory( item, item + 1,
1465 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1466 descr->nb_items--;
1467 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1469 /* Shrink the item array if possible */
1471 max_items = HeapSize( descr->heap, 0, descr->items ) / sizeof(LB_ITEMDATA);
1472 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1474 max_items -= LB_ARRAY_GRANULARITY;
1475 item = HeapReAlloc( descr->heap, 0, descr->items,
1476 max_items * sizeof(LB_ITEMDATA) );
1477 if (item) descr->items = item;
1479 /* Repaint the items */
1481 LISTBOX_UpdateScroll( wnd, descr );
1482 /* if we removed the scrollbar, reset the top of the list
1483 (correct for owner-drawn ???) */
1484 if (descr->nb_items == descr->page_size)
1485 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1487 /* Move selection and focused item */
1488 if (!IS_MULTISELECT(descr))
1490 if (index == descr->selected_item)
1491 descr->selected_item = -1;
1492 else if (index < descr->selected_item)
1494 descr->selected_item--;
1495 if (ISWIN31) /* win 31 do not change the selected item number */
1496 LISTBOX_SetSelection( wnd, descr, descr->selected_item + 1, TRUE, FALSE);
1499 LISTBOX_InvalidateItems( wnd, descr, index );
1500 if (descr->focus_item >= descr->nb_items)
1502 descr->focus_item = descr->nb_items - 1;
1503 if (descr->focus_item < 0) descr->focus_item = 0;
1505 return LB_OKAY;
1509 /***********************************************************************
1510 * LISTBOX_ResetContent
1512 static void LISTBOX_ResetContent( WND *wnd, LB_DESCR *descr )
1514 INT i;
1516 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( wnd, descr, i );
1517 if (descr->items) HeapFree( descr->heap, 0, descr->items );
1518 descr->nb_items = 0;
1519 descr->top_item = 0;
1520 descr->selected_item = -1;
1521 descr->focus_item = 0;
1522 descr->anchor_item = -1;
1523 descr->items = NULL;
1524 LISTBOX_UpdateScroll( wnd, descr );
1525 InvalidateRect( wnd->hwndSelf, NULL, TRUE );
1529 /***********************************************************************
1530 * LISTBOX_SetCount
1532 static LRESULT LISTBOX_SetCount( WND *wnd, LB_DESCR *descr, INT count )
1534 LRESULT ret;
1536 if (HAS_STRINGS(descr)) return LB_ERR;
1537 /* FIXME: this is far from optimal... */
1538 if (count > descr->nb_items)
1540 while (count > descr->nb_items)
1541 if ((ret = LISTBOX_InsertString( wnd, descr, -1, 0 )) < 0)
1542 return ret;
1544 else if (count < descr->nb_items)
1546 while (count < descr->nb_items)
1547 if ((ret = LISTBOX_RemoveItem( wnd, descr, -1 )) < 0)
1548 return ret;
1550 return LB_OKAY;
1554 /***********************************************************************
1555 * LISTBOX_Directory
1557 static LRESULT LISTBOX_Directory( WND *wnd, LB_DESCR *descr, UINT attrib,
1558 LPCSTR filespec, BOOL long_names )
1560 HANDLE handle;
1561 LRESULT ret = LB_OKAY;
1562 WIN32_FIND_DATAA entry;
1563 int pos;
1565 if ((handle = FindFirstFileA(filespec,&entry)) == INVALID_HANDLE_VALUE)
1567 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1569 else
1573 char buffer[270];
1574 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1576 if (!(attrib & DDL_DIRECTORY) ||
1577 !strcmp( entry.cAlternateFileName, "." )) continue;
1578 if (long_names) sprintf( buffer, "[%s]", entry.cFileName );
1579 else sprintf( buffer, "[%s]", entry.cAlternateFileName );
1581 else /* not a directory */
1583 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1584 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1586 if ((attrib & DDL_EXCLUSIVE) &&
1587 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1588 continue;
1589 #undef ATTRIBS
1590 if (long_names) strcpy( buffer, entry.cFileName );
1591 else strcpy( buffer, entry.cAlternateFileName );
1593 if (!long_names) CharLowerA( buffer );
1594 pos = LISTBOX_FindFileStrPos( wnd, descr, buffer );
1595 if ((ret = LISTBOX_InsertString( wnd, descr, pos, buffer )) < 0)
1596 break;
1597 } while (FindNextFileA( handle, &entry ));
1598 FindClose( handle );
1601 if ((ret >= 0) && (attrib & DDL_DRIVES))
1603 char buffer[] = "[-a-]";
1604 int drive;
1605 for (drive = 0; drive < MAX_DOS_DRIVES; drive++, buffer[2]++)
1607 if (!DRIVE_IsValid(drive)) continue;
1608 if ((ret = LISTBOX_InsertString( wnd, descr, -1, buffer )) < 0)
1609 break;
1612 return ret;
1616 /***********************************************************************
1617 * LISTBOX_HandleVScroll
1619 static LRESULT LISTBOX_HandleVScroll( WND *wnd, LB_DESCR *descr,
1620 WPARAM wParam, LPARAM lParam )
1622 SCROLLINFO info;
1624 if (descr->style & LBS_MULTICOLUMN) return 0;
1625 switch(LOWORD(wParam))
1627 case SB_LINEUP:
1628 LISTBOX_SetTopItem( wnd, descr, descr->top_item - 1, TRUE );
1629 break;
1630 case SB_LINEDOWN:
1631 LISTBOX_SetTopItem( wnd, descr, descr->top_item + 1, TRUE );
1632 break;
1633 case SB_PAGEUP:
1634 LISTBOX_SetTopItem( wnd, descr, descr->top_item -
1635 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1636 break;
1637 case SB_PAGEDOWN:
1638 LISTBOX_SetTopItem( wnd, descr, descr->top_item +
1639 LISTBOX_GetCurrentPageSize( wnd, descr ), TRUE );
1640 break;
1641 case SB_THUMBPOSITION:
1642 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam), TRUE );
1643 break;
1644 case SB_THUMBTRACK:
1645 info.cbSize = sizeof(info);
1646 info.fMask = SIF_TRACKPOS;
1647 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1648 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos, TRUE );
1649 break;
1650 case SB_TOP:
1651 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1652 break;
1653 case SB_BOTTOM:
1654 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1655 break;
1657 return 0;
1661 /***********************************************************************
1662 * LISTBOX_HandleHScroll
1664 static LRESULT LISTBOX_HandleHScroll( WND *wnd, LB_DESCR *descr,
1665 WPARAM wParam, LPARAM lParam )
1667 SCROLLINFO info;
1668 INT page;
1670 if (descr->style & LBS_MULTICOLUMN)
1672 switch(LOWORD(wParam))
1674 case SB_LINELEFT:
1675 LISTBOX_SetTopItem( wnd, descr, descr->top_item-descr->page_size,
1676 TRUE );
1677 break;
1678 case SB_LINERIGHT:
1679 LISTBOX_SetTopItem( wnd, descr, descr->top_item+descr->page_size,
1680 TRUE );
1681 break;
1682 case SB_PAGELEFT:
1683 page = descr->width / descr->column_width;
1684 if (page < 1) page = 1;
1685 LISTBOX_SetTopItem( wnd, descr,
1686 descr->top_item - page * descr->page_size, TRUE );
1687 break;
1688 case SB_PAGERIGHT:
1689 page = descr->width / descr->column_width;
1690 if (page < 1) page = 1;
1691 LISTBOX_SetTopItem( wnd, descr,
1692 descr->top_item + page * descr->page_size, TRUE );
1693 break;
1694 case SB_THUMBPOSITION:
1695 LISTBOX_SetTopItem( wnd, descr, HIWORD(wParam)*descr->page_size,
1696 TRUE );
1697 break;
1698 case SB_THUMBTRACK:
1699 info.cbSize = sizeof(info);
1700 info.fMask = SIF_TRACKPOS;
1701 GetScrollInfo( wnd->hwndSelf, SB_VERT, &info );
1702 LISTBOX_SetTopItem( wnd, descr, info.nTrackPos*descr->page_size,
1703 TRUE );
1704 break;
1705 case SB_LEFT:
1706 LISTBOX_SetTopItem( wnd, descr, 0, TRUE );
1707 break;
1708 case SB_RIGHT:
1709 LISTBOX_SetTopItem( wnd, descr, descr->nb_items, TRUE );
1710 break;
1713 else if (descr->horz_extent)
1715 switch(LOWORD(wParam))
1717 case SB_LINELEFT:
1718 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos - 1 );
1719 break;
1720 case SB_LINERIGHT:
1721 LISTBOX_SetHorizontalPos( wnd, descr, descr->horz_pos + 1 );
1722 break;
1723 case SB_PAGELEFT:
1724 LISTBOX_SetHorizontalPos( wnd, descr,
1725 descr->horz_pos - descr->width );
1726 break;
1727 case SB_PAGERIGHT:
1728 LISTBOX_SetHorizontalPos( wnd, descr,
1729 descr->horz_pos + descr->width );
1730 break;
1731 case SB_THUMBPOSITION:
1732 LISTBOX_SetHorizontalPos( wnd, descr, HIWORD(wParam) );
1733 break;
1734 case SB_THUMBTRACK:
1735 info.cbSize = sizeof(info);
1736 info.fMask = SIF_TRACKPOS;
1737 GetScrollInfo( wnd->hwndSelf, SB_HORZ, &info );
1738 LISTBOX_SetHorizontalPos( wnd, descr, info.nTrackPos );
1739 break;
1740 case SB_LEFT:
1741 LISTBOX_SetHorizontalPos( wnd, descr, 0 );
1742 break;
1743 case SB_RIGHT:
1744 LISTBOX_SetHorizontalPos( wnd, descr,
1745 descr->horz_extent - descr->width );
1746 break;
1749 return 0;
1753 /***********************************************************************
1754 * LISTBOX_HandleLButtonDown
1756 static LRESULT LISTBOX_HandleLButtonDown( WND *wnd, LB_DESCR *descr,
1757 WPARAM wParam, INT x, INT y )
1759 INT index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
1760 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1761 wnd->hwndSelf, x, y, index );
1762 if (!descr->caret_on && (GetFocus() == wnd->hwndSelf)) return 0;
1763 if (index != -1)
1765 if (descr->style & LBS_EXTENDEDSEL)
1767 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1768 if (wParam & MK_CONTROL)
1770 LISTBOX_SetCaretIndex( wnd, descr, index, FALSE );
1771 LISTBOX_SetSelection( wnd, descr, index,
1772 !descr->items[index].selected,
1773 (descr->style & LBS_NOTIFY) != 0);
1775 else LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1777 else
1779 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1780 LISTBOX_SetSelection( wnd, descr, index,
1781 (!(descr->style & LBS_MULTIPLESEL) ||
1782 !descr->items[index].selected),
1783 (descr->style & LBS_NOTIFY) != 0 );
1787 if( !descr->lphc ) SetFocus( wnd->hwndSelf );
1788 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit
1789 : descr->lphc->self->hwndSelf ) ;
1791 descr->captured = TRUE;
1792 SetCapture( wnd->hwndSelf );
1793 if (index != -1 && !descr->lphc)
1795 if (descr->style & LBS_NOTIFY )
1796 SendMessageA( descr->owner, WM_LBTRACKPOINT, index,
1797 MAKELPARAM( x, y ) );
1798 if (wnd->dwExStyle & WS_EX_DRAGDETECT)
1800 POINT pt;
1802 pt.x = x;
1803 pt.y = y;
1805 if (DragDetect( wnd->hwndSelf, pt ))
1806 SendMessageA( descr->owner, WM_BEGINDRAG, 0, 0 );
1809 return 0;
1813 /*************************************************************************
1814 * LISTBOX_HandleLButtonDownCombo [Internal]
1816 * Process LButtonDown message for the ComboListBox
1818 * PARAMS
1819 * pWnd [I] The windows internal structure
1820 * pDescr [I] The ListBox internal structure
1821 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
1822 * x [I] X Mouse Coordinate
1823 * y [I] Y Mouse Coordinate
1825 * RETURNS
1826 * 0 since we are processing the WM_LBUTTONDOWN Message
1828 * NOTES
1829 * This function is only to be used when a ListBox is a ComboListBox
1832 static LRESULT LISTBOX_HandleLButtonDownCombo( WND *pWnd, LB_DESCR *pDescr,
1833 WPARAM wParam, INT x, INT y)
1835 RECT clientRect, screenRect;
1836 POINT mousePos;
1838 mousePos.x = x;
1839 mousePos.y = y;
1841 GetClientRect(pWnd->hwndSelf, &clientRect);
1843 if(PtInRect(&clientRect, mousePos))
1845 /* MousePos is in client, resume normal processing */
1846 return LISTBOX_HandleLButtonDown( pWnd, pDescr, wParam, x, y);
1848 else
1850 POINT screenMousePos;
1851 HWND hWndOldCapture;
1853 /* Check the Non-Client Area */
1854 screenMousePos = mousePos;
1855 hWndOldCapture = GetCapture();
1856 ReleaseCapture();
1857 GetWindowRect(pWnd->hwndSelf, &screenRect);
1858 ClientToScreen(pWnd->hwndSelf, &screenMousePos);
1860 if(!PtInRect(&screenRect, screenMousePos))
1862 /* Close The Drop Down */
1863 SEND_NOTIFICATION( pWnd, pDescr, LBN_SELCANCEL );
1864 return 0;
1866 else
1868 /* Check to see the NC is a scrollbar */
1869 INT nHitTestType=0;
1870 /* Check Vertical scroll bar */
1871 if (pWnd->dwStyle & WS_VSCROLL)
1873 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
1874 if (PtInRect( &clientRect, mousePos ))
1876 nHitTestType = HTVSCROLL;
1879 /* Check horizontal scroll bar */
1880 if (pWnd->dwStyle & WS_HSCROLL)
1882 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
1883 if (PtInRect( &clientRect, mousePos ))
1885 nHitTestType = HTHSCROLL;
1888 /* Windows sends this message when a scrollbar is clicked
1891 if(nHitTestType != 0)
1893 SendMessageA(pWnd->hwndSelf, WM_NCLBUTTONDOWN, nHitTestType,
1894 MAKELONG(screenMousePos.x, screenMousePos.y));
1896 /* Resume the Capture after scrolling is complete
1898 if(hWndOldCapture != 0)
1900 SetCapture(hWndOldCapture);
1904 return 0;
1907 /***********************************************************************
1908 * LISTBOX_HandleLButtonUp
1910 static LRESULT LISTBOX_HandleLButtonUp( WND *wnd, LB_DESCR *descr )
1912 if (LISTBOX_Timer != LB_TIMER_NONE)
1913 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1914 LISTBOX_Timer = LB_TIMER_NONE;
1915 if (descr->captured)
1917 descr->captured = FALSE;
1918 if (GetCapture() == wnd->hwndSelf) ReleaseCapture();
1919 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
1920 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
1922 return 0;
1926 /***********************************************************************
1927 * LISTBOX_HandleTimer
1929 * Handle scrolling upon a timer event.
1930 * Return TRUE if scrolling should continue.
1932 static LRESULT LISTBOX_HandleTimer( WND *wnd, LB_DESCR *descr,
1933 INT index, TIMER_DIRECTION dir )
1935 switch(dir)
1937 case LB_TIMER_UP:
1938 if (descr->top_item) index = descr->top_item - 1;
1939 else index = 0;
1940 break;
1941 case LB_TIMER_LEFT:
1942 if (descr->top_item) index -= descr->page_size;
1943 break;
1944 case LB_TIMER_DOWN:
1945 index = descr->top_item + LISTBOX_GetCurrentPageSize( wnd, descr );
1946 if (index == descr->focus_item) index++;
1947 if (index >= descr->nb_items) index = descr->nb_items - 1;
1948 break;
1949 case LB_TIMER_RIGHT:
1950 if (index + descr->page_size < descr->nb_items)
1951 index += descr->page_size;
1952 break;
1953 case LB_TIMER_NONE:
1954 break;
1956 if (index == descr->focus_item) return FALSE;
1957 LISTBOX_MoveCaret( wnd, descr, index, FALSE );
1958 return TRUE;
1962 /***********************************************************************
1963 * LISTBOX_HandleSystemTimer
1965 * WM_SYSTIMER handler.
1967 static LRESULT LISTBOX_HandleSystemTimer( WND *wnd, LB_DESCR *descr )
1969 if (!LISTBOX_HandleTimer( wnd, descr, descr->focus_item, LISTBOX_Timer ))
1971 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
1972 LISTBOX_Timer = LB_TIMER_NONE;
1974 return 0;
1978 /***********************************************************************
1979 * LISTBOX_HandleMouseMove
1981 * WM_MOUSEMOVE handler.
1983 static void LISTBOX_HandleMouseMove( WND *wnd, LB_DESCR *descr,
1984 INT x, INT y )
1986 INT index;
1987 TIMER_DIRECTION dir;
1989 if (!descr->captured) return;
1991 if (descr->style & LBS_MULTICOLUMN)
1993 if (y < 0) y = 0;
1994 else if (y >= descr->item_height * descr->page_size)
1995 y = descr->item_height * descr->page_size - 1;
1997 if (x < 0)
1999 dir = LB_TIMER_LEFT;
2000 x = 0;
2002 else if (x >= descr->width)
2004 dir = LB_TIMER_RIGHT;
2005 x = descr->width - 1;
2007 else dir = LB_TIMER_NONE; /* inside */
2009 else
2011 if (y < 0) dir = LB_TIMER_UP; /* above */
2012 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2013 else dir = LB_TIMER_NONE; /* inside */
2016 index = LISTBOX_GetItemFromPoint( wnd, descr, x, y );
2017 if (index == -1) index = descr->focus_item;
2018 if (!LISTBOX_HandleTimer( wnd, descr, index, dir )) dir = LB_TIMER_NONE;
2020 /* Start/stop the system timer */
2022 if (dir != LB_TIMER_NONE)
2023 SetSystemTimer( wnd->hwndSelf, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2024 else if (LISTBOX_Timer != LB_TIMER_NONE)
2025 KillSystemTimer( wnd->hwndSelf, LB_TIMER_ID );
2026 LISTBOX_Timer = dir;
2030 /***********************************************************************
2031 * LISTBOX_HandleKeyDown
2033 static LRESULT LISTBOX_HandleKeyDown( WND *wnd, LB_DESCR *descr, WPARAM wParam )
2035 INT caret = -1;
2036 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2037 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2038 bForceSelection = FALSE; /* only for single select list */
2040 if (descr->style & LBS_WANTKEYBOARDINPUT)
2042 caret = SendMessageA( descr->owner, WM_VKEYTOITEM,
2043 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2044 wnd->hwndSelf );
2045 if (caret == -2) return 0;
2047 if (caret == -1) switch(wParam)
2049 case VK_LEFT:
2050 if (descr->style & LBS_MULTICOLUMN)
2052 bForceSelection = FALSE;
2053 if (descr->focus_item >= descr->page_size)
2054 caret = descr->focus_item - descr->page_size;
2055 break;
2057 /* fall through */
2058 case VK_UP:
2059 caret = descr->focus_item - 1;
2060 if (caret < 0) caret = 0;
2061 break;
2062 case VK_RIGHT:
2063 if (descr->style & LBS_MULTICOLUMN)
2065 bForceSelection = FALSE;
2066 if (descr->focus_item + descr->page_size < descr->nb_items)
2067 caret = descr->focus_item + descr->page_size;
2068 break;
2070 /* fall through */
2071 case VK_DOWN:
2072 caret = descr->focus_item + 1;
2073 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2074 break;
2076 case VK_PRIOR:
2077 if (descr->style & LBS_MULTICOLUMN)
2079 INT page = descr->width / descr->column_width;
2080 if (page < 1) page = 1;
2081 caret = descr->focus_item - (page * descr->page_size) + 1;
2083 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(wnd,descr)+1;
2084 if (caret < 0) caret = 0;
2085 break;
2086 case VK_NEXT:
2087 if (descr->style & LBS_MULTICOLUMN)
2089 INT page = descr->width / descr->column_width;
2090 if (page < 1) page = 1;
2091 caret = descr->focus_item + (page * descr->page_size) - 1;
2093 else caret = descr->focus_item+LISTBOX_GetCurrentPageSize(wnd,descr)-1;
2094 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2095 break;
2096 case VK_HOME:
2097 caret = 0;
2098 break;
2099 case VK_END:
2100 caret = descr->nb_items - 1;
2101 break;
2102 case VK_SPACE:
2103 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2104 else if (descr->style & LBS_MULTIPLESEL)
2106 LISTBOX_SetSelection( wnd, descr, descr->focus_item,
2107 !descr->items[descr->focus_item].selected,
2108 (descr->style & LBS_NOTIFY) != 0 );
2110 break;
2111 default:
2112 bForceSelection = FALSE;
2114 if (bForceSelection) /* focused item is used instead of key */
2115 caret = descr->focus_item;
2116 if (caret >= 0)
2118 if ((descr->style & LBS_EXTENDEDSEL) &&
2119 !(GetKeyState( VK_SHIFT ) & 0x8000))
2120 descr->anchor_item = caret;
2121 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2122 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2123 if (descr->style & LBS_NOTIFY)
2125 if( descr->lphc && CB_GETTYPE(descr->lphc) != CBS_SIMPLE )
2127 /* make sure that combo parent doesn't hide us */
2128 descr->lphc->wState |= CBF_NOROLLUP;
2130 if (descr->nb_items) SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2133 return 0;
2137 /***********************************************************************
2138 * LISTBOX_HandleChar
2140 static LRESULT LISTBOX_HandleChar( WND *wnd, LB_DESCR *descr,
2141 WPARAM wParam )
2143 INT caret = -1;
2144 char str[2];
2146 str[0] = wParam & 0xff;
2147 str[1] = '\0';
2149 if (descr->style & LBS_WANTKEYBOARDINPUT)
2151 caret = SendMessageA( descr->owner, WM_CHARTOITEM,
2152 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2153 wnd->hwndSelf );
2154 if (caret == -2) return 0;
2156 if (caret == -1)
2157 caret = LISTBOX_FindString( wnd, descr, descr->focus_item, str, FALSE);
2158 if (caret != -1)
2160 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2161 LISTBOX_SetSelection( wnd, descr, caret, TRUE, FALSE);
2162 LISTBOX_MoveCaret( wnd, descr, caret, TRUE );
2163 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2164 SEND_NOTIFICATION( wnd, descr, LBN_SELCHANGE );
2166 return 0;
2170 /***********************************************************************
2171 * LISTBOX_Create
2173 static BOOL LISTBOX_Create( WND *wnd, LPHEADCOMBO lphc )
2175 LB_DESCR *descr;
2176 MEASUREITEMSTRUCT mis;
2177 RECT rect;
2179 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2180 return FALSE;
2181 if (!(descr->heap = HeapCreate( 0, 0x10000, 0 )))
2183 HeapFree( GetProcessHeap(), 0, descr );
2184 return FALSE;
2186 GetClientRect( wnd->hwndSelf, &rect );
2187 descr->owner = GetParent( wnd->hwndSelf );
2188 descr->style = wnd->dwStyle;
2189 descr->width = rect.right - rect.left;
2190 descr->height = rect.bottom - rect.top;
2191 descr->items = NULL;
2192 descr->nb_items = 0;
2193 descr->top_item = 0;
2194 descr->selected_item = -1;
2195 descr->focus_item = 0;
2196 descr->anchor_item = -1;
2197 descr->item_height = 1;
2198 descr->page_size = 1;
2199 descr->column_width = 150;
2200 descr->horz_extent = (wnd->dwStyle & WS_HSCROLL) ? 1 : 0;
2201 descr->horz_pos = 0;
2202 descr->nb_tabs = 0;
2203 descr->tabs = NULL;
2204 descr->caret_on = TRUE;
2205 descr->captured = FALSE;
2206 descr->font = 0;
2207 descr->locale = 0; /* FIXME */
2208 descr->lphc = lphc;
2210 if( ( GetExpWinVer16( wnd->hInstance ) & 0xFF00 ) == 0x0300
2211 && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2213 /* Win95 document "List Box Differences" from MSDN:
2214 If a list box in a version 3.x application has either the
2215 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2216 horizontal and vertical scroll bars.
2218 descr->style |= WS_VSCROLL | WS_HSCROLL;
2221 if( lphc )
2223 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2224 wnd->hwndSelf, descr->owner, lphc->self->hwndSelf );
2225 descr->owner = lphc->self->hwndSelf;
2228 *(LB_DESCR **)wnd->wExtra = descr;
2230 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2232 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2233 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2234 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2235 descr->item_height = LISTBOX_SetFont( wnd, descr, 0 );
2237 if (descr->style & LBS_OWNERDRAWFIXED)
2239 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2241 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2242 descr->item_height = lphc->fixedOwnerDrawHeight;
2244 else
2246 UINT id = (descr->lphc ) ? ID_CB_LISTBOX : wnd->wIDmenu;
2248 mis.CtlType = ODT_LISTBOX;
2249 mis.CtlID = id;
2250 mis.itemID = -1;
2251 mis.itemWidth = 0;
2252 mis.itemData = 0;
2253 mis.itemHeight = descr->item_height;
2254 SendMessageA( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2255 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2259 return TRUE;
2263 /***********************************************************************
2264 * LISTBOX_Destroy
2266 static BOOL LISTBOX_Destroy( WND *wnd, LB_DESCR *descr )
2268 LISTBOX_ResetContent( wnd, descr );
2269 HeapDestroy( descr->heap );
2270 HeapFree( GetProcessHeap(), 0, descr );
2271 wnd->wExtra[0] = 0;
2272 return TRUE;
2276 /***********************************************************************
2277 * ListBoxWndProc
2279 static inline LRESULT WINAPI ListBoxWndProc_locked( WND* wnd, UINT msg,
2280 WPARAM wParam, LPARAM lParam )
2282 LRESULT ret;
2283 LB_DESCR *descr;
2284 HWND hwnd = wnd->hwndSelf;
2286 if (!wnd) return 0;
2287 if (!(descr = *(LB_DESCR **)wnd->wExtra))
2289 switch (msg)
2291 case WM_CREATE:
2293 if (!LISTBOX_Create( wnd, NULL ))
2294 return -1;
2295 TRACE("creating wnd=%04x descr=%p\n",
2296 hwnd, *(LB_DESCR **)wnd->wExtra );
2297 return 0;
2299 case WM_NCCREATE:
2302 * When a listbox is not in a combobox and the look
2303 * is win95, the WS_BORDER style is replaced with
2304 * the WS_EX_CLIENTEDGE style.
2306 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2307 (wnd->dwStyle & WS_BORDER) )
2309 wnd->dwExStyle |= WS_EX_CLIENTEDGE;
2310 wnd->dwStyle &= ~ WS_BORDER;
2315 /* Ignore all other messages before we get a WM_CREATE */
2316 return DefWindowProcA( hwnd, msg, wParam, lParam );
2319 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2320 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2321 switch(msg)
2323 case LB_RESETCONTENT16:
2324 case LB_RESETCONTENT:
2325 LISTBOX_ResetContent( wnd, descr );
2326 return 0;
2328 case LB_ADDSTRING16:
2329 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2330 /* fall through */
2331 case LB_ADDSTRING:
2332 wParam = LISTBOX_FindStringPos( wnd, descr, (LPCSTR)lParam, FALSE );
2333 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2335 case LB_INSERTSTRING16:
2336 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2337 wParam = (INT)(INT16)wParam;
2338 /* fall through */
2339 case LB_INSERTSTRING:
2340 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2342 case LB_ADDFILE16:
2343 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2344 /* fall through */
2345 case LB_ADDFILE:
2346 wParam = LISTBOX_FindFileStrPos( wnd, descr, (LPCSTR)lParam );
2347 return LISTBOX_InsertString( wnd, descr, wParam, (LPCSTR)lParam );
2349 case LB_DELETESTRING16:
2350 case LB_DELETESTRING:
2351 if (LISTBOX_RemoveItem( wnd, descr, wParam) != LB_ERR)
2352 return descr->nb_items;
2353 else
2354 return LB_ERR;
2356 case LB_GETITEMDATA16:
2357 case LB_GETITEMDATA:
2358 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2359 return LB_ERR;
2360 return descr->items[wParam].data;
2362 case LB_SETITEMDATA16:
2363 case LB_SETITEMDATA:
2364 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2365 return LB_ERR;
2366 descr->items[wParam].data = (DWORD)lParam;
2367 return LB_OKAY;
2369 case LB_GETCOUNT16:
2370 case LB_GETCOUNT:
2371 return descr->nb_items;
2373 case LB_GETTEXT16:
2374 lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2375 /* fall through */
2376 case LB_GETTEXT:
2377 return LISTBOX_GetText( wnd, descr, wParam, (LPSTR)lParam );
2379 case LB_GETTEXTLEN16:
2380 /* fall through */
2381 case LB_GETTEXTLEN:
2382 if (wParam >= descr->nb_items)
2383 return LB_ERR;
2384 return (HAS_STRINGS(descr) ? strlen(descr->items[wParam].str)
2385 : sizeof(DWORD));
2387 case LB_GETCURSEL16:
2388 case LB_GETCURSEL:
2389 if (descr->nb_items==0)
2390 return LB_ERR;
2391 if (!IS_MULTISELECT(descr))
2392 return descr->selected_item;
2393 /* else */
2394 if (descr->selected_item!=-1)
2395 return descr->selected_item;
2396 /* else */
2397 return descr->focus_item;
2398 /* otherwise, if the user tries to move the selection with the */
2399 /* arrow keys, we will give the application something to choke on */
2400 case LB_GETTOPINDEX16:
2401 case LB_GETTOPINDEX:
2402 return descr->top_item;
2404 case LB_GETITEMHEIGHT16:
2405 case LB_GETITEMHEIGHT:
2406 return LISTBOX_GetItemHeight( wnd, descr, wParam );
2408 case LB_SETITEMHEIGHT16:
2409 lParam = LOWORD(lParam);
2410 /* fall through */
2411 case LB_SETITEMHEIGHT:
2412 return LISTBOX_SetItemHeight( wnd, descr, wParam, lParam );
2414 case LB_ITEMFROMPOINT:
2416 POINT pt;
2417 RECT rect;
2419 pt.x = LOWORD(lParam);
2420 pt.y = HIWORD(lParam);
2421 rect.left = 0;
2422 rect.top = 0;
2423 rect.right = descr->width;
2424 rect.bottom = descr->height;
2426 return MAKELONG( LISTBOX_GetItemFromPoint(wnd, descr, pt.x, pt.y),
2427 !PtInRect( &rect, pt ) );
2430 case LB_SETCARETINDEX16:
2431 case LB_SETCARETINDEX:
2432 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2433 if (LISTBOX_SetCaretIndex( wnd, descr, wParam, !lParam ) == LB_ERR)
2434 return LB_ERR;
2435 else if (ISWIN31)
2436 return wParam;
2437 else
2438 return LB_OKAY;
2440 case LB_GETCARETINDEX16:
2441 case LB_GETCARETINDEX:
2442 return descr->focus_item;
2444 case LB_SETTOPINDEX16:
2445 case LB_SETTOPINDEX:
2446 return LISTBOX_SetTopItem( wnd, descr, wParam, TRUE );
2448 case LB_SETCOLUMNWIDTH16:
2449 case LB_SETCOLUMNWIDTH:
2450 return LISTBOX_SetColumnWidth( wnd, descr, wParam );
2452 case LB_GETITEMRECT16:
2454 RECT rect;
2455 ret = LISTBOX_GetItemRect( wnd, descr, (INT16)wParam, &rect );
2456 CONV_RECT32TO16( &rect, (RECT16 *)PTR_SEG_TO_LIN(lParam) );
2458 return ret;
2460 case LB_GETITEMRECT:
2461 return LISTBOX_GetItemRect( wnd, descr, wParam, (RECT *)lParam );
2463 case LB_FINDSTRING16:
2464 wParam = (INT)(INT16)wParam;
2465 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2466 /* fall through */
2467 case LB_FINDSTRING:
2468 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, FALSE );
2470 case LB_FINDSTRINGEXACT16:
2471 wParam = (INT)(INT16)wParam;
2472 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2473 /* fall through */
2474 case LB_FINDSTRINGEXACT:
2475 return LISTBOX_FindString( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2477 case LB_SELECTSTRING16:
2478 wParam = (INT)(INT16)wParam;
2479 if (HAS_STRINGS(descr)) lParam = (LPARAM)PTR_SEG_TO_LIN(lParam);
2480 /* fall through */
2481 case LB_SELECTSTRING:
2483 INT index = LISTBOX_FindString( wnd, descr, wParam,
2484 (LPCSTR)lParam, FALSE );
2485 if (index == LB_ERR)
2486 return LB_ERR;
2487 LISTBOX_SetSelection( wnd, descr, index, TRUE, FALSE );
2488 return index;
2491 case LB_GETSEL16:
2492 wParam = (INT)(INT16)wParam;
2493 /* fall through */
2494 case LB_GETSEL:
2495 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2496 return LB_ERR;
2497 return descr->items[wParam].selected;
2499 case LB_SETSEL16:
2500 lParam = (INT)(INT16)lParam;
2501 /* fall through */
2502 case LB_SETSEL:
2503 return LISTBOX_SetSelection( wnd, descr, lParam, wParam, FALSE );
2505 case LB_SETCURSEL16:
2506 wParam = (INT)(INT16)wParam;
2507 /* fall through */
2508 case LB_SETCURSEL:
2509 if (IS_MULTISELECT(descr)) return LB_ERR;
2510 LISTBOX_SetCaretIndex( wnd, descr, wParam, TRUE );
2511 return LISTBOX_SetSelection( wnd, descr, wParam, TRUE, FALSE );
2513 case LB_GETSELCOUNT16:
2514 case LB_GETSELCOUNT:
2515 return LISTBOX_GetSelCount( wnd, descr );
2517 case LB_GETSELITEMS16:
2518 return LISTBOX_GetSelItems16( wnd, descr, wParam,
2519 (LPINT16)PTR_SEG_TO_LIN(lParam) );
2521 case LB_GETSELITEMS:
2522 return LISTBOX_GetSelItems( wnd, descr, wParam, (LPINT)lParam );
2524 case LB_SELITEMRANGE16:
2525 case LB_SELITEMRANGE:
2526 if (LOWORD(lParam) <= HIWORD(lParam))
2527 return LISTBOX_SelectItemRange( wnd, descr, LOWORD(lParam),
2528 HIWORD(lParam), wParam );
2529 else
2530 return LISTBOX_SelectItemRange( wnd, descr, HIWORD(lParam),
2531 LOWORD(lParam), wParam );
2533 case LB_SELITEMRANGEEX16:
2534 case LB_SELITEMRANGEEX:
2535 if ((INT)lParam >= (INT)wParam)
2536 return LISTBOX_SelectItemRange( wnd, descr, wParam, lParam, TRUE );
2537 else
2538 return LISTBOX_SelectItemRange( wnd, descr, lParam, wParam, FALSE);
2540 case LB_GETHORIZONTALEXTENT16:
2541 case LB_GETHORIZONTALEXTENT:
2542 return descr->horz_extent;
2544 case LB_SETHORIZONTALEXTENT16:
2545 case LB_SETHORIZONTALEXTENT:
2546 return LISTBOX_SetHorizontalExtent( wnd, descr, wParam );
2548 case LB_GETANCHORINDEX16:
2549 case LB_GETANCHORINDEX:
2550 return descr->anchor_item;
2552 case LB_SETANCHORINDEX16:
2553 wParam = (INT)(INT16)wParam;
2554 /* fall through */
2555 case LB_SETANCHORINDEX:
2556 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2557 return LB_ERR;
2558 descr->anchor_item = (INT)wParam;
2559 return LB_OKAY;
2561 case LB_DIR16:
2562 return LISTBOX_Directory( wnd, descr, wParam,
2563 (LPCSTR)PTR_SEG_TO_LIN(lParam), FALSE );
2565 case LB_DIR:
2566 return LISTBOX_Directory( wnd, descr, wParam, (LPCSTR)lParam, TRUE );
2568 case LB_GETLOCALE:
2569 return descr->locale;
2571 case LB_SETLOCALE:
2572 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2573 return LB_OKAY;
2575 case LB_INITSTORAGE:
2576 return LISTBOX_InitStorage( wnd, descr, wParam, (DWORD)lParam );
2578 case LB_SETCOUNT:
2579 return LISTBOX_SetCount( wnd, descr, (INT)wParam );
2581 case LB_SETTABSTOPS16:
2582 return LISTBOX_SetTabStops( wnd, descr, (INT)(INT16)wParam,
2583 (LPINT)PTR_SEG_TO_LIN(lParam), TRUE );
2585 case LB_SETTABSTOPS:
2586 return LISTBOX_SetTabStops( wnd, descr, wParam, (LPINT)lParam, FALSE );
2588 case LB_CARETON16:
2589 case LB_CARETON:
2590 if (descr->caret_on)
2591 return LB_OKAY;
2592 descr->caret_on = TRUE;
2593 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2594 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2595 return LB_OKAY;
2597 case LB_CARETOFF16:
2598 case LB_CARETOFF:
2599 if (!descr->caret_on)
2600 return LB_OKAY;
2601 descr->caret_on = FALSE;
2602 if ((descr->focus_item != -1) && (GetFocus() == wnd->hwndSelf))
2603 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2604 return LB_OKAY;
2606 case WM_DESTROY:
2607 return LISTBOX_Destroy( wnd, descr );
2609 case WM_ENABLE:
2610 InvalidateRect( hwnd, NULL, TRUE );
2611 return 0;
2613 case WM_SETREDRAW:
2614 LISTBOX_SetRedraw( wnd, descr, wParam != 0 );
2615 return 0;
2617 case WM_GETDLGCODE:
2618 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2620 case WM_PAINT:
2622 PAINTSTRUCT ps;
2623 HDC hdc = ( wParam ) ? ((HDC)wParam)
2624 : BeginPaint( hwnd, &ps );
2625 ret = LISTBOX_Paint( wnd, descr, hdc );
2626 if( !wParam ) EndPaint( hwnd, &ps );
2628 return ret;
2629 case WM_SIZE:
2630 LISTBOX_UpdateSize( wnd, descr );
2631 return 0;
2632 case WM_GETFONT:
2633 return descr->font;
2634 case WM_SETFONT:
2635 LISTBOX_SetFont( wnd, descr, (HFONT)wParam );
2636 if (lParam) InvalidateRect( wnd->hwndSelf, 0, TRUE );
2637 return 0;
2638 case WM_SETFOCUS:
2639 descr->caret_on = TRUE;
2640 if (descr->focus_item != -1)
2641 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2642 SEND_NOTIFICATION( wnd, descr, LBN_SETFOCUS );
2643 return 0;
2644 case WM_KILLFOCUS:
2645 if ((descr->focus_item != -1) && descr->caret_on)
2646 LISTBOX_RepaintItem( wnd, descr, descr->focus_item, ODA_FOCUS );
2647 SEND_NOTIFICATION( wnd, descr, LBN_KILLFOCUS );
2648 return 0;
2649 case WM_HSCROLL:
2650 return LISTBOX_HandleHScroll( wnd, descr, wParam, lParam );
2651 case WM_VSCROLL:
2652 return LISTBOX_HandleVScroll( wnd, descr, wParam, lParam );
2653 case WM_LBUTTONDOWN:
2654 return LISTBOX_HandleLButtonDown( wnd, descr, wParam,
2655 (INT16)LOWORD(lParam),
2656 (INT16)HIWORD(lParam) );
2657 case WM_LBUTTONDBLCLK:
2658 if (descr->style & LBS_NOTIFY)
2659 SEND_NOTIFICATION( wnd, descr, LBN_DBLCLK );
2660 return 0;
2661 case WM_MOUSEMOVE:
2662 if (GetCapture() == hwnd)
2663 LISTBOX_HandleMouseMove( wnd, descr, (INT16)LOWORD(lParam),
2664 (INT16)HIWORD(lParam) );
2665 return 0;
2666 case WM_LBUTTONUP:
2667 return LISTBOX_HandleLButtonUp( wnd, descr );
2668 case WM_KEYDOWN:
2669 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2670 case WM_CHAR:
2671 return LISTBOX_HandleChar( wnd, descr, wParam );
2672 case WM_SYSTIMER:
2673 return LISTBOX_HandleSystemTimer( wnd, descr );
2674 case WM_ERASEBKGND:
2675 if (IS_OWNERDRAW(descr))
2677 RECT rect;
2678 HBRUSH hbrush = SendMessageA( descr->owner, WM_CTLCOLORLISTBOX,
2679 wParam, (LPARAM)wnd->hwndSelf );
2680 GetClientRect(hwnd, &rect);
2681 if (hbrush) FillRect( (HDC)wParam, &rect, hbrush );
2683 return 1;
2684 case WM_DROPFILES:
2685 if( !descr->lphc )
2686 return SendMessageA( descr->owner, msg, wParam, lParam );
2687 break;
2689 case WM_DROPOBJECT:
2690 case WM_QUERYDROPOBJECT:
2691 case WM_DRAGSELECT:
2692 case WM_DRAGMOVE:
2693 if( !descr->lphc )
2695 LPDRAGINFO dragInfo = (LPDRAGINFO)PTR_SEG_TO_LIN( (SEGPTR)lParam );
2696 dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2697 dragInfo->pt.y );
2698 return SendMessageA( descr->owner, msg, wParam, lParam );
2700 break;
2702 default:
2703 if ((msg >= WM_USER) && (msg < 0xc000))
2704 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2705 hwnd, msg, wParam, lParam );
2706 return DefWindowProcA( hwnd, msg, wParam, lParam );
2708 return 0;
2711 /***********************************************************************
2712 * ListBoxWndProc
2714 * This is just a wrapper for the real wndproc, it only does window locking
2715 * and unlocking.
2717 LRESULT WINAPI ListBoxWndProc( HWND hwnd, UINT msg,
2718 WPARAM wParam, LPARAM lParam )
2720 WND* wndPtr = WIN_FindWndPtr( hwnd );
2721 LRESULT res = ListBoxWndProc_locked(wndPtr,msg,wParam,lParam);
2723 WIN_ReleaseWndPtr(wndPtr);
2724 return res;
2727 /***********************************************************************
2728 * COMBO_Directory
2730 LRESULT COMBO_Directory( LPHEADCOMBO lphc, UINT attrib, LPSTR dir, BOOL bLong)
2732 WND *wnd = WIN_FindWndPtr( lphc->hWndLBox );
2734 if( wnd )
2736 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2737 if( descr )
2739 LRESULT lRet = LISTBOX_Directory( wnd, descr, attrib, dir, bLong );
2741 RedrawWindow( lphc->self->hwndSelf, NULL, 0,
2742 RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
2743 WIN_ReleaseWndPtr(wnd);
2744 return lRet;
2746 WIN_ReleaseWndPtr(wnd);
2748 return CB_ERR;
2751 /***********************************************************************
2752 * ComboLBWndProc_locked
2754 * The real combo listbox wndproc, but called with locked WND struct.
2756 static inline LRESULT WINAPI ComboLBWndProc_locked( WND* wnd, UINT msg,
2757 WPARAM wParam, LPARAM lParam )
2759 LRESULT lRet = 0;
2760 HWND hwnd = wnd->hwndSelf;
2762 if (wnd)
2764 LB_DESCR *descr = *(LB_DESCR **)wnd->wExtra;
2766 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
2767 wnd->hwndSelf, SPY_GetMsgName(msg), wParam, lParam );
2769 if( descr || msg == WM_CREATE )
2771 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
2773 switch( msg )
2775 case WM_CREATE:
2776 #define lpcs ((LPCREATESTRUCTA)lParam)
2777 TRACE_(combo)("\tpassed parent handle = 0x%08x\n",
2778 (UINT)lpcs->lpCreateParams);
2780 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
2781 #undef lpcs
2782 return LISTBOX_Create( wnd, lphc );
2783 case WM_MOUSEMOVE:
2784 if ( (TWEAK_WineLook > WIN31_LOOK) &&
2785 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
2787 POINT mousePos;
2788 BOOL captured;
2789 RECT clientRect;
2791 mousePos.x = (INT16)LOWORD(lParam);
2792 mousePos.y = (INT16)HIWORD(lParam);
2795 * If we are in a dropdown combobox, we simulate that
2796 * the mouse is captured to show the tracking of the item.
2798 GetClientRect(hwnd, &clientRect);
2800 if (PtInRect( &clientRect, mousePos ))
2802 captured = descr->captured;
2803 descr->captured = TRUE;
2805 LISTBOX_HandleMouseMove( wnd, descr,
2806 mousePos.x, mousePos.y);
2808 descr->captured = captured;
2811 else
2813 LISTBOX_HandleMouseMove( wnd, descr,
2814 mousePos.x, mousePos.y);
2817 return 0;
2820 else
2823 * If we are in Win3.1 look, go with the default behavior.
2825 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2827 case WM_LBUTTONUP:
2828 if (TWEAK_WineLook > WIN31_LOOK)
2830 POINT mousePos;
2831 RECT clientRect;
2834 * If the mouse button "up" is not in the listbox,
2835 * we make sure there is no selection by re-selecting the
2836 * item that was selected when the listbox was made visible.
2838 mousePos.x = (INT16)LOWORD(lParam);
2839 mousePos.y = (INT16)HIWORD(lParam);
2841 GetClientRect(hwnd, &clientRect);
2844 * When the user clicks outside the combobox and the focus
2845 * is lost, the owning combobox will send a fake buttonup with
2846 * 0xFFFFFFF as the mouse location, we must also revert the
2847 * selection to the original selection.
2849 if ( (lParam == 0xFFFFFFFF) ||
2850 (!PtInRect( &clientRect, mousePos )) )
2852 LISTBOX_MoveCaret( wnd,
2853 descr,
2854 lphc->droppedIndex,
2855 FALSE );
2858 return LISTBOX_HandleLButtonUp( wnd, descr );
2859 case WM_LBUTTONDOWN:
2860 return LISTBOX_HandleLButtonDownCombo(wnd, descr, wParam,
2861 (INT16)LOWORD(lParam),
2862 (INT16)HIWORD(lParam) );
2863 case WM_MOUSEACTIVATE:
2864 return MA_NOACTIVATE;
2865 case WM_NCACTIVATE:
2866 return FALSE;
2867 case WM_KEYDOWN:
2868 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2870 /* for some reason(?) Windows makes it possible to
2871 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2873 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
2874 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
2875 && (wParam == VK_DOWN || wParam == VK_UP)) )
2877 COMBO_FlipListbox( lphc, FALSE );
2878 return 0;
2881 return LISTBOX_HandleKeyDown( wnd, descr, wParam );
2883 case LB_SETCURSEL16:
2884 case LB_SETCURSEL:
2885 lRet = ListBoxWndProc( hwnd, msg, wParam, lParam );
2886 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
2887 return lRet;
2888 case WM_NCDESTROY:
2889 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
2890 lphc->hWndLBox = 0;
2891 /* fall through */
2893 default:
2894 return ListBoxWndProc( hwnd, msg, wParam, lParam );
2897 lRet = DefWindowProcA( hwnd, msg, wParam, lParam );
2899 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
2901 return lRet;
2904 /***********************************************************************
2905 * ComboLBWndProc
2907 * NOTE: in Windows, winproc address of the ComboLBox is the same
2908 * as that of the Listbox.
2910 * This is just a wrapper for the real wndproc, it only does window locking
2911 * and unlocking.
2913 LRESULT WINAPI ComboLBWndProc( HWND hwnd, UINT msg,
2914 WPARAM wParam, LPARAM lParam )
2916 WND *wnd = WIN_FindWndPtr( hwnd );
2917 LRESULT res = ComboLBWndProc_locked(wnd,msg,wParam,lParam);
2919 WIN_ReleaseWndPtr(wnd);
2920 return res;