Add missing </para>.
[wine/hacks.git] / controls / listbox.c
blob042f131168aca51589b61b0eca2238352f850fe3
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 "wine/unicode.h"
15 #include "winuser.h"
16 #include "winerror.h"
17 #include "spy.h"
18 #include "user.h"
19 #include "controls.h"
20 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(listbox);
23 DECLARE_DEBUG_CHANNEL(combo);
25 /* Unimplemented yet:
26 * - LBS_USETABSTOPS
27 * - Locale handling
29 * Probably needs improvement:
30 * - LBS_NOSEL
33 /* Items array granularity */
34 #define LB_ARRAY_GRANULARITY 16
36 /* Scrolling timeout in ms */
37 #define LB_SCROLL_TIMEOUT 50
39 /* Listbox system timer id */
40 #define LB_TIMER_ID 2
42 /* flag listbox changed while setredraw false - internal style */
43 #define LBS_DISPLAYCHANGED 0x80000000
45 /* Item structure */
46 typedef struct
48 LPWSTR str; /* Item text */
49 BOOL selected; /* Is item selected? */
50 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
51 DWORD data; /* User data */
52 } LB_ITEMDATA;
54 /* Listbox structure */
55 typedef struct
57 HWND owner; /* Owner window to send notifications to */
58 UINT style; /* Window style */
59 INT width; /* Window width */
60 INT height; /* Window height */
61 LB_ITEMDATA *items; /* Array of items */
62 INT nb_items; /* Number of items */
63 INT top_item; /* Top visible item */
64 INT selected_item; /* Selected item */
65 INT focus_item; /* Item that has the focus */
66 INT anchor_item; /* Anchor item for extended selection */
67 INT item_height; /* Default item height */
68 INT page_size; /* Items per listbox page */
69 INT column_width; /* Column width for multi-column listboxes */
70 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
71 INT horz_pos; /* Horizontal position */
72 INT nb_tabs; /* Number of tabs in array */
73 INT *tabs; /* Array of tabs */
74 BOOL caret_on; /* Is caret on? */
75 BOOL captured; /* Is mouse captured? */
76 BOOL in_focus;
77 HFONT font; /* Current font */
78 LCID locale; /* Current locale for string comparisons */
79 LPHEADCOMBO lphc; /* ComboLBox */
80 } LB_DESCR;
83 #define IS_OWNERDRAW(descr) \
84 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
86 #define HAS_STRINGS(descr) \
87 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
90 #define IS_MULTISELECT(descr) \
91 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
93 #define SEND_NOTIFICATION(hwnd,descr,code) \
94 (SendMessageW( (descr)->owner, WM_COMMAND, \
95 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
97 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
99 /* Current timer status */
100 typedef enum
102 LB_TIMER_NONE,
103 LB_TIMER_UP,
104 LB_TIMER_LEFT,
105 LB_TIMER_DOWN,
106 LB_TIMER_RIGHT
107 } TIMER_DIRECTION;
109 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
111 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
112 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
113 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
114 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
116 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
118 /*********************************************************************
119 * listbox class descriptor
121 const struct builtin_class_descr LISTBOX_builtin_class =
123 "ListBox", /* name */
124 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
125 ListBoxWndProcA, /* procA */
126 ListBoxWndProcW, /* procW */
127 sizeof(LB_DESCR *), /* extra */
128 IDC_ARROWA, /* cursor */
129 0 /* brush */
133 /*********************************************************************
134 * combolbox class descriptor
136 const struct builtin_class_descr COMBOLBOX_builtin_class =
138 "ComboLBox", /* name */
139 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
140 ComboLBWndProcA, /* procA */
141 ComboLBWndProcW, /* procW */
142 sizeof(LB_DESCR *), /* extra */
143 IDC_ARROWA, /* cursor */
144 0 /* brush */
148 /* check whether app is a Win 3.1 app */
149 inline static BOOL is_old_app( HWND hwnd )
151 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
155 /***********************************************************************
156 * LISTBOX_Dump
158 void LISTBOX_Dump( HWND hwnd )
160 INT i;
161 LB_ITEMDATA *item;
162 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
164 TRACE( "Listbox:\n" );
165 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
166 hwnd, (UINT)descr, descr->nb_items,
167 descr->top_item );
168 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
170 TRACE( "%4d: %-40s %d %08lx %3d\n",
171 i, debugstr_w(item->str), item->selected, item->data, item->height );
176 /***********************************************************************
177 * LISTBOX_GetCurrentPageSize
179 * Return the current page size
181 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
183 INT i, height;
184 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
185 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
187 if ((height += descr->items[i].height) > descr->height) break;
189 if (i == descr->top_item) return 1;
190 else return i - descr->top_item;
194 /***********************************************************************
195 * LISTBOX_GetMaxTopIndex
197 * Return the maximum possible index for the top of the listbox.
199 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
201 INT max, page;
203 if (descr->style & LBS_OWNERDRAWVARIABLE)
205 page = descr->height;
206 for (max = descr->nb_items - 1; max >= 0; max--)
207 if ((page -= descr->items[max].height) < 0) break;
208 if (max < descr->nb_items - 1) max++;
210 else if (descr->style & LBS_MULTICOLUMN)
212 if ((page = descr->width / descr->column_width) < 1) page = 1;
213 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
214 max = (max - page) * descr->page_size;
216 else
218 max = descr->nb_items - descr->page_size;
220 if (max < 0) max = 0;
221 return max;
225 /***********************************************************************
226 * LISTBOX_UpdateScroll
228 * Update the scrollbars. Should be called whenever the content
229 * of the listbox changes.
231 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
233 SCROLLINFO info;
235 /* Check the listbox scroll bar flags individually before we call
236 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
237 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
238 scroll bar when we do not need one.
239 if (!(descr->style & WS_VSCROLL)) return;
242 /* It is important that we check descr->style, and not wnd->dwStyle,
243 for WS_VSCROLL, as the former is exactly the one passed in
244 argument to CreateWindow.
245 In Windows (and from now on in Wine :) a listbox created
246 with such a style (no WS_SCROLL) does not update
247 the scrollbar with listbox-related data, thus letting
248 the programmer use it for his/her own purposes. */
250 if (descr->style & LBS_NOREDRAW) return;
251 info.cbSize = sizeof(info);
253 if (descr->style & LBS_MULTICOLUMN)
255 info.nMin = 0;
256 info.nMax = (descr->nb_items - 1) / descr->page_size;
257 info.nPos = descr->top_item / descr->page_size;
258 info.nPage = descr->width / descr->column_width;
259 if (info.nPage < 1) info.nPage = 1;
260 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
261 if (descr->style & LBS_DISABLENOSCROLL)
262 info.fMask |= SIF_DISABLENOSCROLL;
263 if (descr->style & WS_HSCROLL)
264 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
265 info.nMax = 0;
266 info.fMask = SIF_RANGE;
267 if (descr->style & WS_VSCROLL)
268 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
270 else
272 info.nMin = 0;
273 info.nMax = descr->nb_items - 1;
274 info.nPos = descr->top_item;
275 info.nPage = LISTBOX_GetCurrentPageSize( descr );
276 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
277 if (descr->style & LBS_DISABLENOSCROLL)
278 info.fMask |= SIF_DISABLENOSCROLL;
279 if (descr->style & WS_VSCROLL)
280 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
282 if (descr->horz_extent)
284 info.nMin = 0;
285 info.nMax = descr->horz_extent - 1;
286 info.nPos = descr->horz_pos;
287 info.nPage = descr->width;
288 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
289 if (descr->style & LBS_DISABLENOSCROLL)
290 info.fMask |= SIF_DISABLENOSCROLL;
291 if (descr->style & WS_HSCROLL)
292 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
298 /***********************************************************************
299 * LISTBOX_SetTopItem
301 * Set the top item of the listbox, scrolling up or down if necessary.
303 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
304 BOOL scroll )
306 INT max = LISTBOX_GetMaxTopIndex( descr );
307 if (index > max) index = max;
308 if (index < 0) index = 0;
309 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
310 if (descr->top_item == index) return LB_OKAY;
311 if (descr->style & LBS_MULTICOLUMN)
313 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
314 if (scroll && (abs(diff) < descr->width))
315 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
316 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
318 else
319 scroll = FALSE;
321 else if (scroll)
323 INT diff;
324 if (descr->style & LBS_OWNERDRAWVARIABLE)
326 INT i;
327 diff = 0;
328 if (index > descr->top_item)
330 for (i = index - 1; i >= descr->top_item; i--)
331 diff -= descr->items[i].height;
333 else
335 for (i = index; i < descr->top_item; i++)
336 diff += descr->items[i].height;
339 else
340 diff = (descr->top_item - index) * descr->item_height;
342 if (abs(diff) < descr->height)
343 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
344 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
345 else
346 scroll = FALSE;
348 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
349 descr->top_item = index;
350 LISTBOX_UpdateScroll( hwnd, descr );
351 return LB_OKAY;
355 /***********************************************************************
356 * LISTBOX_UpdatePage
358 * Update the page size. Should be called when the size of
359 * the client area or the item height changes.
361 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
363 INT page_size;
365 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
366 page_size = 1;
367 if (page_size == descr->page_size) return;
368 descr->page_size = page_size;
369 if (descr->style & LBS_MULTICOLUMN)
370 InvalidateRect( hwnd, NULL, TRUE );
371 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
375 /***********************************************************************
376 * LISTBOX_UpdateSize
378 * Update the size of the listbox. Should be called when the size of
379 * the client area changes.
381 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
383 RECT rect;
385 GetClientRect( hwnd, &rect );
386 descr->width = rect.right - rect.left;
387 descr->height = rect.bottom - rect.top;
388 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
390 INT remaining;
391 RECT rect;
393 GetWindowRect( hwnd, &rect );
394 if(descr->item_height != 0)
395 remaining = descr->height % descr->item_height;
396 else
397 remaining = 0;
398 if ((descr->height > descr->item_height) && remaining)
400 if (is_old_app(hwnd))
401 { /* give a margin for error to 16 bits programs - if we need
402 less than the height of the nonclient area, round to the
403 *next* number of items */
404 int ncheight = rect.bottom - rect.top - descr->height;
405 if ((descr->item_height - remaining) <= ncheight)
406 remaining = remaining - descr->item_height;
408 TRACE("[%04x]: changing height %d -> %d\n",
409 hwnd, descr->height, descr->height - remaining );
410 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
411 rect.bottom - rect.top - remaining,
412 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
413 return;
416 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
417 LISTBOX_UpdatePage( hwnd, descr );
418 LISTBOX_UpdateScroll( hwnd, descr );
420 /* Invalidate the focused item so it will be repainted correctly */
421 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
423 InvalidateRect( hwnd, &rect, FALSE );
428 /***********************************************************************
429 * LISTBOX_GetItemRect
431 * Get the rectangle enclosing an item, in listbox client coordinates.
432 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
434 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
436 /* Index <= 0 is legal even on empty listboxes */
437 if (index && (index >= descr->nb_items)) return -1;
438 SetRect( rect, 0, 0, descr->width, descr->height );
439 if (descr->style & LBS_MULTICOLUMN)
441 INT col = (index / descr->page_size) -
442 (descr->top_item / descr->page_size);
443 rect->left += col * descr->column_width;
444 rect->right = rect->left + descr->column_width;
445 rect->top += (index % descr->page_size) * descr->item_height;
446 rect->bottom = rect->top + descr->item_height;
448 else if (descr->style & LBS_OWNERDRAWVARIABLE)
450 INT i;
451 rect->right += descr->horz_pos;
452 if ((index >= 0) && (index < descr->nb_items))
454 if (index < descr->top_item)
456 for (i = descr->top_item-1; i >= index; i--)
457 rect->top -= descr->items[i].height;
459 else
461 for (i = descr->top_item; i < index; i++)
462 rect->top += descr->items[i].height;
464 rect->bottom = rect->top + descr->items[index].height;
468 else
470 rect->top += (index - descr->top_item) * descr->item_height;
471 rect->bottom = rect->top + descr->item_height;
472 rect->right += descr->horz_pos;
475 return ((rect->left < descr->width) && (rect->right > 0) &&
476 (rect->top < descr->height) && (rect->bottom > 0));
480 /***********************************************************************
481 * LISTBOX_GetItemFromPoint
483 * Return the item nearest from point (x,y) (in client coordinates).
485 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
487 INT index = descr->top_item;
489 if (!descr->nb_items) return -1; /* No items */
490 if (descr->style & LBS_OWNERDRAWVARIABLE)
492 INT pos = 0;
493 if (y >= 0)
495 while (index < descr->nb_items)
497 if ((pos += descr->items[index].height) > y) break;
498 index++;
501 else
503 while (index > 0)
505 index--;
506 if ((pos -= descr->items[index].height) <= y) break;
510 else if (descr->style & LBS_MULTICOLUMN)
512 if (y >= descr->item_height * descr->page_size) return -1;
513 if (y >= 0) index += y / descr->item_height;
514 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
515 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
517 else
519 index += (y / descr->item_height);
521 if (index < 0) return 0;
522 if (index >= descr->nb_items) return -1;
523 return index;
527 /***********************************************************************
528 * LISTBOX_PaintItem
530 * Paint an item.
532 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
533 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
535 LB_ITEMDATA *item = NULL;
536 if (index < descr->nb_items) item = &descr->items[index];
538 if (IS_OWNERDRAW(descr))
540 DRAWITEMSTRUCT dis;
541 RECT r;
542 HRGN hrgn;
543 UINT id = GetWindowLongA( hwnd, GWL_ID );
545 if (!item)
547 if (action == ODA_FOCUS)
548 DrawFocusRect( hdc, rect );
549 else
550 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
551 return;
554 /* some programs mess with the clipping region when
555 drawing the item, *and* restore the previous region
556 after they are done, so a region has better to exist
557 else everything ends clipped */
558 GetClientRect(hwnd, &r);
559 hrgn = CreateRectRgnIndirect(&r);
560 SelectClipRgn( hdc, hrgn);
561 DeleteObject( hrgn );
563 dis.CtlType = ODT_LISTBOX;
564 dis.CtlID = id;
565 dis.hwndItem = hwnd;
566 dis.itemAction = action;
567 dis.hDC = hdc;
568 dis.itemID = index;
569 dis.itemState = 0;
570 if (item && item->selected) dis.itemState |= ODS_SELECTED;
571 if (!ignoreFocus && (descr->focus_item == index) &&
572 (descr->caret_on) &&
573 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
574 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
575 dis.itemData = item ? item->data : 0;
576 dis.rcItem = *rect;
577 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
578 hwnd, index, item ? debugstr_w(item->str) : "", action,
579 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
580 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
582 else
584 COLORREF oldText = 0, oldBk = 0;
586 if (action == ODA_FOCUS)
588 DrawFocusRect( hdc, rect );
589 return;
591 if (item && item->selected)
593 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
594 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
597 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
598 hwnd, index, item ? debugstr_w(item->str) : "", action,
599 rect->left, rect->top, rect->right, rect->bottom );
600 if (!item)
601 ExtTextOutW( hdc, rect->left + 1, rect->top,
602 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
603 else if (!(descr->style & LBS_USETABSTOPS))
604 ExtTextOutW( hdc, rect->left + 1, rect->top,
605 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
606 strlenW(item->str), NULL );
607 else
609 /* Output empty string to paint background in the full width. */
610 ExtTextOutW( hdc, rect->left + 1, rect->top,
611 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
612 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
613 item->str, strlenW(item->str),
614 descr->nb_tabs, descr->tabs, 0);
616 if (item && item->selected)
618 SetBkColor( hdc, oldBk );
619 SetTextColor( hdc, oldText );
621 if (!ignoreFocus && (descr->focus_item == index) &&
622 (descr->caret_on) &&
623 (descr->in_focus)) DrawFocusRect( hdc, rect );
628 /***********************************************************************
629 * LISTBOX_SetRedraw
631 * Change the redraw flag.
633 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
635 if (on)
637 if (!(descr->style & LBS_NOREDRAW)) return;
638 descr->style &= ~LBS_NOREDRAW;
639 if (descr->style & LBS_DISPLAYCHANGED)
640 { /* page was changed while setredraw false, refresh automatically */
641 InvalidateRect(hwnd, NULL, TRUE);
642 if ((descr->top_item + descr->page_size) > descr->nb_items)
643 { /* reset top of page if less than number of items/page */
644 descr->top_item = descr->nb_items - descr->page_size;
645 if (descr->top_item < 0) descr->top_item = 0;
647 descr->style &= ~LBS_DISPLAYCHANGED;
649 LISTBOX_UpdateScroll( hwnd, descr );
651 else descr->style |= LBS_NOREDRAW;
655 /***********************************************************************
656 * LISTBOX_RepaintItem
658 * Repaint a single item synchronously.
660 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
661 UINT action )
663 HDC hdc;
664 RECT rect;
665 HFONT oldFont = 0;
666 HBRUSH hbrush, oldBrush = 0;
668 /* Do not repaint the item if the item is not visible */
669 if (!IsWindowVisible(hwnd)) return;
670 if (descr->style & LBS_NOREDRAW)
672 descr->style |= LBS_DISPLAYCHANGED;
673 return;
675 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
676 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
677 if (descr->font) oldFont = SelectObject( hdc, descr->font );
678 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
679 hdc, (LPARAM)hwnd );
680 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
681 if (!IsWindowEnabled(hwnd))
682 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
683 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
684 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
685 if (oldFont) SelectObject( hdc, oldFont );
686 if (oldBrush) SelectObject( hdc, oldBrush );
687 ReleaseDC( hwnd, hdc );
691 /***********************************************************************
692 * LISTBOX_InitStorage
694 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
696 LB_ITEMDATA *item;
698 nb_items += LB_ARRAY_GRANULARITY - 1;
699 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
700 if (descr->items)
701 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
702 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
703 nb_items * sizeof(LB_ITEMDATA) )))
705 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
706 return LB_ERRSPACE;
708 descr->items = item;
709 return LB_OKAY;
713 /***********************************************************************
714 * LISTBOX_SetTabStops
716 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
717 LPINT tabs, BOOL short_ints )
719 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
720 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
721 if (!(descr->nb_tabs = count))
723 descr->tabs = NULL;
724 return TRUE;
726 /* FIXME: count = 1 */
727 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
728 descr->nb_tabs * sizeof(INT) )))
729 return FALSE;
730 if (short_ints)
732 INT i;
733 LPINT16 p = (LPINT16)tabs;
735 TRACE("[%04x]: settabstops ", hwnd );
736 for (i = 0; i < descr->nb_tabs; i++) {
737 descr->tabs[i] = *p++<<1; /* FIXME */
738 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
740 if (TRACE_ON(listbox)) DPRINTF("\n");
742 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
743 /* FIXME: repaint the window? */
744 return TRUE;
748 /***********************************************************************
749 * LISTBOX_GetText
751 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
753 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
754 if (HAS_STRINGS(descr))
756 if (!lParam)
757 return strlenW(descr->items[index].str);
759 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
761 if(unicode)
763 LPWSTR buffer = (LPWSTR)lParam;
764 strcpyW( buffer, descr->items[index].str );
765 return strlenW(buffer);
767 else
769 LPSTR buffer = (LPSTR)lParam;
770 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
772 } else {
773 if (lParam)
774 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
775 return sizeof(DWORD);
780 /***********************************************************************
781 * LISTBOX_FindStringPos
783 * Find the nearest string located before a given string in sort order.
784 * If 'exact' is TRUE, return an error if we don't get an exact match.
786 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
787 BOOL exact )
789 INT index, min, max, res = -1;
791 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
792 min = 0;
793 max = descr->nb_items;
794 while (min != max)
796 index = (min + max) / 2;
797 if (HAS_STRINGS(descr))
798 res = lstrcmpiW( descr->items[index].str, str );
799 else
801 COMPAREITEMSTRUCT cis;
802 UINT id = GetWindowLongA( hwnd, GWL_ID );
804 cis.CtlType = ODT_LISTBOX;
805 cis.CtlID = id;
806 cis.hwndItem = hwnd;
807 cis.itemID1 = index;
808 cis.itemData1 = descr->items[index].data;
809 cis.itemID2 = -1;
810 cis.itemData2 = (DWORD)str;
811 cis.dwLocaleId = descr->locale;
812 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
814 if (!res) return index;
815 if (res > 0) max = index;
816 else min = index + 1;
818 return exact ? -1 : max;
822 /***********************************************************************
823 * LISTBOX_FindFileStrPos
825 * Find the nearest string located before a given string in directory
826 * sort order (i.e. first files, then directories, then drives).
828 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
830 INT min, max, res = -1;
832 if (!HAS_STRINGS(descr))
833 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
834 min = 0;
835 max = descr->nb_items;
836 while (min != max)
838 INT index = (min + max) / 2;
839 LPCWSTR p = descr->items[index].str;
840 if (*p == '[') /* drive or directory */
842 if (*str != '[') res = -1;
843 else if (p[1] == '-') /* drive */
845 if (str[1] == '-') res = str[2] - p[2];
846 else res = -1;
848 else /* directory */
850 if (str[1] == '-') res = 1;
851 else res = lstrcmpiW( str, p );
854 else /* filename */
856 if (*str == '[') res = 1;
857 else res = lstrcmpiW( str, p );
859 if (!res) return index;
860 if (res < 0) max = index;
861 else min = index + 1;
863 return max;
867 /***********************************************************************
868 * LISTBOX_FindString
870 * Find the item beginning with a given string.
872 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
873 LPCWSTR str, BOOL exact )
875 INT i;
876 LB_ITEMDATA *item;
878 if (start >= descr->nb_items) start = -1;
879 item = descr->items + start + 1;
880 if (HAS_STRINGS(descr))
882 if (!str || ! str[0] ) return LB_ERR;
883 if (exact)
885 for (i = start + 1; i < descr->nb_items; i++, item++)
886 if (!lstrcmpiW( str, item->str )) return i;
887 for (i = 0, item = descr->items; i <= start; i++, item++)
888 if (!lstrcmpiW( str, item->str )) return i;
890 else
892 /* Special case for drives and directories: ignore prefix */
893 #define CHECK_DRIVE(item) \
894 if ((item)->str[0] == '[') \
896 if (!strncmpiW( str, (item)->str+1, len )) return i; \
897 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
898 return i; \
901 INT len = strlenW(str);
902 for (i = start + 1; i < descr->nb_items; i++, item++)
904 if (!strncmpiW( str, item->str, len )) return i;
905 CHECK_DRIVE(item);
907 for (i = 0, item = descr->items; i <= start; i++, item++)
909 if (!strncmpiW( str, item->str, len )) return i;
910 CHECK_DRIVE(item);
912 #undef CHECK_DRIVE
915 else
917 if (exact && (descr->style & LBS_SORT))
918 /* If sorted, use a WM_COMPAREITEM binary search */
919 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
921 /* Otherwise use a linear search */
922 for (i = start + 1; i < descr->nb_items; i++, item++)
923 if (item->data == (DWORD)str) return i;
924 for (i = 0, item = descr->items; i <= start; i++, item++)
925 if (item->data == (DWORD)str) return i;
927 return LB_ERR;
931 /***********************************************************************
932 * LISTBOX_GetSelCount
934 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
936 INT i, count;
937 LB_ITEMDATA *item = descr->items;
939 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
940 for (i = count = 0; i < descr->nb_items; i++, item++)
941 if (item->selected) count++;
942 return count;
946 /***********************************************************************
947 * LISTBOX_GetSelItems16
949 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
951 INT i, count;
952 LB_ITEMDATA *item = descr->items;
954 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
955 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
956 if (item->selected) array[count++] = (INT16)i;
957 return count;
961 /***********************************************************************
962 * LISTBOX_GetSelItems
964 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
966 INT i, count;
967 LB_ITEMDATA *item = descr->items;
969 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
970 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
971 if (item->selected) array[count++] = i;
972 return count;
976 /***********************************************************************
977 * LISTBOX_Paint
979 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
981 INT i, col_pos = descr->page_size - 1;
982 RECT rect;
983 RECT focusRect = {-1, -1, -1, -1};
984 HFONT oldFont = 0;
985 HBRUSH hbrush, oldBrush = 0;
987 if (descr->style & LBS_NOREDRAW) return 0;
989 SetRect( &rect, 0, 0, descr->width, descr->height );
990 if (descr->style & LBS_MULTICOLUMN)
991 rect.right = rect.left + descr->column_width;
992 else if (descr->horz_pos)
994 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
995 rect.right += descr->horz_pos;
998 if (descr->font) oldFont = SelectObject( hdc, descr->font );
999 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1000 hdc, (LPARAM)hwnd );
1001 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1002 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1004 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1005 (descr->in_focus))
1007 /* Special case for empty listbox: paint focus rect */
1008 rect.bottom = rect.top + descr->item_height;
1009 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1010 ODA_FOCUS, FALSE );
1011 rect.top = rect.bottom;
1014 /* Paint all the item, regarding the selection
1015 Focus state will be painted after */
1017 for (i = descr->top_item; i < descr->nb_items; i++)
1019 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1020 rect.bottom = rect.top + descr->item_height;
1021 else
1022 rect.bottom = rect.top + descr->items[i].height;
1024 if (i == descr->focus_item)
1026 /* keep the focus rect, to paint the focus item after */
1027 focusRect.left = rect.left;
1028 focusRect.right = rect.right;
1029 focusRect.top = rect.top;
1030 focusRect.bottom = rect.bottom;
1032 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1033 rect.top = rect.bottom;
1035 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1037 if (!IS_OWNERDRAW(descr))
1039 /* Clear the bottom of the column */
1040 if (rect.top < descr->height)
1042 rect.bottom = descr->height;
1043 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1044 &rect, NULL, 0, NULL );
1048 /* Go to the next column */
1049 rect.left += descr->column_width;
1050 rect.right += descr->column_width;
1051 rect.top = 0;
1052 col_pos = descr->page_size - 1;
1054 else
1056 col_pos--;
1057 if (rect.top >= descr->height) break;
1061 /* Paint the focus item now */
1062 if (focusRect.top != focusRect.bottom && descr->caret_on)
1063 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1065 if (!IS_OWNERDRAW(descr))
1067 /* Clear the remainder of the client area */
1068 if (rect.top < descr->height)
1070 rect.bottom = descr->height;
1071 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1072 &rect, NULL, 0, NULL );
1074 if (rect.right < descr->width)
1076 rect.left = rect.right;
1077 rect.right = descr->width;
1078 rect.top = 0;
1079 rect.bottom = descr->height;
1080 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1081 &rect, NULL, 0, NULL );
1084 if (oldFont) SelectObject( hdc, oldFont );
1085 if (oldBrush) SelectObject( hdc, oldBrush );
1086 return 0;
1090 /***********************************************************************
1091 * LISTBOX_InvalidateItems
1093 * Invalidate all items from a given item. If the specified item is not
1094 * visible, nothing happens.
1096 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1098 RECT rect;
1100 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1102 if (descr->style & LBS_NOREDRAW)
1104 descr->style |= LBS_DISPLAYCHANGED;
1105 return;
1107 rect.bottom = descr->height;
1108 InvalidateRect( hwnd, &rect, TRUE );
1109 if (descr->style & LBS_MULTICOLUMN)
1111 /* Repaint the other columns */
1112 rect.left = rect.right;
1113 rect.right = descr->width;
1114 rect.top = 0;
1115 InvalidateRect( hwnd, &rect, TRUE );
1121 /***********************************************************************
1122 * LISTBOX_GetItemHeight
1124 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1126 if (descr->style & LBS_OWNERDRAWVARIABLE)
1128 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1129 return descr->items[index].height;
1131 else return descr->item_height;
1135 /***********************************************************************
1136 * LISTBOX_SetItemHeight
1138 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1139 INT height )
1141 if (!height) height = 1;
1143 if (descr->style & LBS_OWNERDRAWVARIABLE)
1145 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1146 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1147 descr->items[index].height = height;
1148 LISTBOX_UpdateScroll( hwnd, descr );
1149 LISTBOX_InvalidateItems( hwnd, descr, index );
1151 else if (height != descr->item_height)
1153 TRACE("[%04x]: new height = %d\n", hwnd, height );
1154 descr->item_height = height;
1155 LISTBOX_UpdatePage( hwnd, descr );
1156 LISTBOX_UpdateScroll( hwnd, descr );
1157 InvalidateRect( hwnd, 0, TRUE );
1159 return LB_OKAY;
1163 /***********************************************************************
1164 * LISTBOX_SetHorizontalPos
1166 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1168 INT diff;
1170 if (pos > descr->horz_extent - descr->width)
1171 pos = descr->horz_extent - descr->width;
1172 if (pos < 0) pos = 0;
1173 if (!(diff = descr->horz_pos - pos)) return;
1174 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1175 descr->horz_pos = pos;
1176 LISTBOX_UpdateScroll( hwnd, descr );
1177 if (abs(diff) < descr->width)
1178 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1179 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1180 else
1181 InvalidateRect( hwnd, NULL, TRUE );
1185 /***********************************************************************
1186 * LISTBOX_SetHorizontalExtent
1188 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1189 INT extent )
1191 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1192 return LB_OKAY;
1193 if (extent <= 0) extent = 1;
1194 if (extent == descr->horz_extent) return LB_OKAY;
1195 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1196 descr->horz_extent = extent;
1197 if (descr->horz_pos > extent - descr->width)
1198 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1199 else
1200 LISTBOX_UpdateScroll( hwnd, descr );
1201 return LB_OKAY;
1205 /***********************************************************************
1206 * LISTBOX_SetColumnWidth
1208 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1210 if (width == descr->column_width) return LB_OKAY;
1211 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1212 descr->column_width = width;
1213 LISTBOX_UpdatePage( hwnd, descr );
1214 return LB_OKAY;
1218 /***********************************************************************
1219 * LISTBOX_SetFont
1221 * Returns the item height.
1223 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1225 HDC hdc;
1226 HFONT oldFont = 0;
1227 TEXTMETRICW tm;
1229 descr->font = font;
1231 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1233 ERR("unable to get DC.\n" );
1234 return 16;
1236 if (font) oldFont = SelectObject( hdc, font );
1237 GetTextMetricsW( hdc, &tm );
1238 if (oldFont) SelectObject( hdc, oldFont );
1239 ReleaseDC( hwnd, hdc );
1240 if (!IS_OWNERDRAW(descr))
1241 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight );
1242 return tm.tmHeight ;
1246 /***********************************************************************
1247 * LISTBOX_MakeItemVisible
1249 * Make sure that a given item is partially or fully visible.
1251 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1252 BOOL fully )
1254 INT top;
1256 if (index <= descr->top_item) top = index;
1257 else if (descr->style & LBS_MULTICOLUMN)
1259 INT cols = descr->width;
1260 if (!fully) cols += descr->column_width - 1;
1261 if (cols >= descr->column_width) cols /= descr->column_width;
1262 else cols = 1;
1263 if (index < descr->top_item + (descr->page_size * cols)) return;
1264 top = index - descr->page_size * (cols - 1);
1266 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1268 INT height = fully ? descr->items[index].height : 1;
1269 for (top = index; top > descr->top_item; top--)
1270 if ((height += descr->items[top-1].height) > descr->height) break;
1272 else
1274 if (index < descr->top_item + descr->page_size) return;
1275 if (!fully && (index == descr->top_item + descr->page_size) &&
1276 (descr->height > (descr->page_size * descr->item_height))) return;
1277 top = index - descr->page_size + 1;
1279 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1282 /***********************************************************************
1283 * LISTBOX_SetCaretIndex
1285 * NOTES
1286 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1289 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1290 BOOL fully_visible )
1292 INT oldfocus = descr->focus_item;
1294 if (descr->style & LBS_NOSEL) return LB_ERR;
1295 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1296 if (index == oldfocus) return LB_OKAY;
1297 descr->focus_item = index;
1298 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1299 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1301 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1302 if (descr->caret_on && (descr->in_focus))
1303 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1305 return LB_OKAY;
1309 /***********************************************************************
1310 * LISTBOX_SelectItemRange
1312 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1314 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1315 INT last, BOOL on )
1317 INT i;
1319 /* A few sanity checks */
1321 if (descr->style & LBS_NOSEL) return LB_ERR;
1322 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1323 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1324 if (last == -1) last = descr->nb_items - 1;
1325 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1326 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1327 /* selected_item reflects last selected/unselected item on multiple sel */
1328 descr->selected_item = last;
1330 if (on) /* Turn selection on */
1332 for (i = first; i <= last; i++)
1334 if (descr->items[i].selected) continue;
1335 descr->items[i].selected = TRUE;
1336 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1338 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1340 else /* Turn selection off */
1342 for (i = first; i <= last; i++)
1344 if (!descr->items[i].selected) continue;
1345 descr->items[i].selected = FALSE;
1346 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1349 return LB_OKAY;
1352 /***********************************************************************
1353 * LISTBOX_SetSelection
1355 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1356 BOOL on, BOOL send_notify )
1358 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1360 if (descr->style & LBS_NOSEL) return LB_ERR;
1361 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1362 if (descr->style & LBS_MULTIPLESEL)
1364 if (index == -1) /* Select all items */
1365 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1366 else /* Only one item */
1367 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1369 else
1371 INT oldsel = descr->selected_item;
1372 if (index == oldsel) return LB_OKAY;
1373 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1374 if (index != -1) descr->items[index].selected = TRUE;
1375 descr->selected_item = index;
1376 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1377 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1378 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1379 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1380 else
1381 if( descr->lphc ) /* set selection change flag for parent combo */
1382 descr->lphc->wState |= CBF_SELCHANGE;
1384 return LB_OKAY;
1388 /***********************************************************************
1389 * LISTBOX_MoveCaret
1391 * Change the caret position and extend the selection to the new caret.
1393 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1394 BOOL fully_visible )
1396 INT oldfocus = descr->focus_item;
1398 if ((index < 0) || (index >= descr->nb_items))
1399 return;
1401 /* Important, repaint needs to be done in this order if
1402 you want to mimic Windows behavior:
1403 1. Remove the focus and paint the item
1404 2. Remove the selection and paint the item(s)
1405 3. Set the selection and repaint the item(s)
1406 4. Set the focus to 'index' and repaint the item */
1408 /* 1. remove the focus and repaint the item */
1409 descr->focus_item = -1;
1410 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1411 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1413 /* 2. then turn off the previous selection */
1414 /* 3. repaint the new selected item */
1415 if (descr->style & LBS_EXTENDEDSEL)
1417 if (descr->anchor_item != -1)
1419 INT first = min( index, descr->anchor_item );
1420 INT last = max( index, descr->anchor_item );
1421 if (first > 0)
1422 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1423 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1424 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1427 else if (!(descr->style & LBS_MULTIPLESEL))
1429 /* Set selection to new caret item */
1430 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1433 /* 4. repaint the new item with the focus */
1434 descr->focus_item = index;
1435 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1436 if (descr->caret_on && (descr->in_focus))
1437 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1441 /***********************************************************************
1442 * LISTBOX_InsertItem
1444 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1445 LPWSTR str, DWORD data )
1447 LB_ITEMDATA *item;
1448 INT max_items;
1449 INT oldfocus = descr->focus_item;
1451 if (index == -1) index = descr->nb_items;
1452 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1453 if (!descr->items) max_items = 0;
1454 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1455 if (descr->nb_items == max_items)
1457 /* We need to grow the array */
1458 max_items += LB_ARRAY_GRANULARITY;
1459 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1460 max_items * sizeof(LB_ITEMDATA) )))
1462 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1463 return LB_ERRSPACE;
1465 descr->items = item;
1468 /* Insert the item structure */
1470 item = &descr->items[index];
1471 if (index < descr->nb_items)
1472 RtlMoveMemory( item + 1, item,
1473 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1474 item->str = str;
1475 item->data = data;
1476 item->height = 0;
1477 item->selected = FALSE;
1478 descr->nb_items++;
1480 /* Get item height */
1482 if (descr->style & LBS_OWNERDRAWVARIABLE)
1484 MEASUREITEMSTRUCT mis;
1485 UINT id = GetWindowLongA( hwnd, GWL_ID );
1487 mis.CtlType = ODT_LISTBOX;
1488 mis.CtlID = id;
1489 mis.itemID = index;
1490 mis.itemData = descr->items[index].data;
1491 mis.itemHeight = descr->item_height;
1492 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1493 item->height = mis.itemHeight ? mis.itemHeight : 1;
1494 TRACE("[%04x]: measure item %d (%s) = %d\n",
1495 hwnd, index, str ? debugstr_w(str) : "", item->height );
1498 /* Repaint the items */
1500 LISTBOX_UpdateScroll( hwnd, descr );
1501 LISTBOX_InvalidateItems( hwnd, descr, index );
1503 /* Move selection and focused item */
1504 /* If listbox was empty, set focus to the first item */
1505 if (descr->nb_items == 1)
1506 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1507 /* single select don't change selection index in win31 */
1508 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1510 descr->selected_item++;
1511 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1513 else
1515 if (index <= descr->selected_item)
1517 descr->selected_item++;
1518 descr->focus_item = oldfocus; /* focus not changed */
1521 return LB_OKAY;
1525 /***********************************************************************
1526 * LISTBOX_InsertString
1528 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1529 LPCWSTR str )
1531 LPWSTR new_str = NULL;
1532 DWORD data = 0;
1533 LRESULT ret;
1535 if (HAS_STRINGS(descr))
1537 static const WCHAR empty_stringW[] = { 0 };
1538 if (!str) str = empty_stringW;
1539 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1541 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1542 return LB_ERRSPACE;
1544 strcpyW(new_str, str);
1546 else data = (DWORD)str;
1548 if (index == -1) index = descr->nb_items;
1549 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1551 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1552 return ret;
1555 TRACE("[%04x]: added item %d %s\n",
1556 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1557 return index;
1561 /***********************************************************************
1562 * LISTBOX_DeleteItem
1564 * Delete the content of an item. 'index' must be a valid index.
1566 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1568 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1569 * while Win95 sends it for all items with user data.
1570 * It's probably better to send it too often than not
1571 * often enough, so this is what we do here.
1573 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1575 DELETEITEMSTRUCT dis;
1576 UINT id = GetWindowLongA( hwnd, GWL_ID );
1578 dis.CtlType = ODT_LISTBOX;
1579 dis.CtlID = id;
1580 dis.itemID = index;
1581 dis.hwndItem = hwnd;
1582 dis.itemData = descr->items[index].data;
1583 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1585 if (HAS_STRINGS(descr) && descr->items[index].str)
1586 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1590 /***********************************************************************
1591 * LISTBOX_RemoveItem
1593 * Remove an item from the listbox and delete its content.
1595 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1597 LB_ITEMDATA *item;
1598 INT max_items;
1600 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1601 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1603 /* We need to invalidate the original rect instead of the updated one. */
1604 LISTBOX_InvalidateItems( hwnd, descr, index );
1606 LISTBOX_DeleteItem( hwnd, descr, index );
1608 /* Remove the item */
1610 item = &descr->items[index];
1611 if (index < descr->nb_items-1)
1612 RtlMoveMemory( item, item + 1,
1613 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1614 descr->nb_items--;
1615 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1617 /* Shrink the item array if possible */
1619 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1620 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1622 max_items -= LB_ARRAY_GRANULARITY;
1623 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1624 max_items * sizeof(LB_ITEMDATA) );
1625 if (item) descr->items = item;
1627 /* Repaint the items */
1629 LISTBOX_UpdateScroll( hwnd, descr );
1630 /* if we removed the scrollbar, reset the top of the list
1631 (correct for owner-drawn ???) */
1632 if (descr->nb_items == descr->page_size)
1633 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1635 /* Move selection and focused item */
1636 if (!IS_MULTISELECT(descr))
1638 if (index == descr->selected_item)
1639 descr->selected_item = -1;
1640 else if (index < descr->selected_item)
1642 descr->selected_item--;
1643 if (ISWIN31) /* win 31 do not change the selected item number */
1644 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1648 if (descr->focus_item >= descr->nb_items)
1650 descr->focus_item = descr->nb_items - 1;
1651 if (descr->focus_item < 0) descr->focus_item = 0;
1653 return LB_OKAY;
1657 /***********************************************************************
1658 * LISTBOX_ResetContent
1660 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1662 INT i;
1664 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1665 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1666 descr->nb_items = 0;
1667 descr->top_item = 0;
1668 descr->selected_item = -1;
1669 descr->focus_item = 0;
1670 descr->anchor_item = -1;
1671 descr->items = NULL;
1675 /***********************************************************************
1676 * LISTBOX_SetCount
1678 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1680 LRESULT ret;
1682 if (HAS_STRINGS(descr)) return LB_ERR;
1683 /* FIXME: this is far from optimal... */
1684 if (count > descr->nb_items)
1686 while (count > descr->nb_items)
1687 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1688 return ret;
1690 else if (count < descr->nb_items)
1692 while (count < descr->nb_items)
1693 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1694 return ret;
1696 return LB_OKAY;
1700 /***********************************************************************
1701 * LISTBOX_Directory
1703 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1704 LPCWSTR filespec, BOOL long_names )
1706 HANDLE handle;
1707 LRESULT ret = LB_OKAY;
1708 WIN32_FIND_DATAW entry;
1709 int pos;
1711 /* don't scan directory if we just want drives exclusively */
1712 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1713 /* scan directory */
1714 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1716 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1718 else
1722 WCHAR buffer[270];
1723 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1725 static const WCHAR bracketW[] = { ']',0 };
1726 static const WCHAR dotW[] = { '.',0 };
1727 if (!(attrib & DDL_DIRECTORY) ||
1728 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1729 buffer[0] = '[';
1730 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1731 else strcpyW( buffer + 1, entry.cAlternateFileName );
1732 strcatW(buffer, bracketW);
1734 else /* not a directory */
1736 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1737 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1739 if ((attrib & DDL_EXCLUSIVE) &&
1740 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1741 continue;
1742 #undef ATTRIBS
1743 if (long_names) strcpyW( buffer, entry.cFileName );
1744 else strcpyW( buffer, entry.cAlternateFileName );
1746 if (!long_names) CharLowerW( buffer );
1747 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1748 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1749 break;
1750 } while (FindNextFileW( handle, &entry ));
1751 FindClose( handle );
1755 /* scan drives */
1756 if ((ret >= 0) && (attrib & DDL_DRIVES))
1758 WCHAR buffer[] = {'[','-','a','-',']',0};
1759 WCHAR root[] = {'A',':','\\',0};
1760 int drive;
1761 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1763 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1764 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1765 break;
1768 return ret;
1772 /***********************************************************************
1773 * LISTBOX_HandleVScroll
1775 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1777 SCROLLINFO info;
1779 if (descr->style & LBS_MULTICOLUMN) return 0;
1780 switch(LOWORD(wParam))
1782 case SB_LINEUP:
1783 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1784 break;
1785 case SB_LINEDOWN:
1786 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1787 break;
1788 case SB_PAGEUP:
1789 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1790 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1791 break;
1792 case SB_PAGEDOWN:
1793 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1794 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1795 break;
1796 case SB_THUMBPOSITION:
1797 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1798 break;
1799 case SB_THUMBTRACK:
1800 info.cbSize = sizeof(info);
1801 info.fMask = SIF_TRACKPOS;
1802 GetScrollInfo( hwnd, SB_VERT, &info );
1803 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1804 break;
1805 case SB_TOP:
1806 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1807 break;
1808 case SB_BOTTOM:
1809 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1810 break;
1812 return 0;
1816 /***********************************************************************
1817 * LISTBOX_HandleHScroll
1819 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1821 SCROLLINFO info;
1822 INT page;
1824 if (descr->style & LBS_MULTICOLUMN)
1826 switch(LOWORD(wParam))
1828 case SB_LINELEFT:
1829 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1830 TRUE );
1831 break;
1832 case SB_LINERIGHT:
1833 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1834 TRUE );
1835 break;
1836 case SB_PAGELEFT:
1837 page = descr->width / descr->column_width;
1838 if (page < 1) page = 1;
1839 LISTBOX_SetTopItem( hwnd, descr,
1840 descr->top_item - page * descr->page_size, TRUE );
1841 break;
1842 case SB_PAGERIGHT:
1843 page = descr->width / descr->column_width;
1844 if (page < 1) page = 1;
1845 LISTBOX_SetTopItem( hwnd, descr,
1846 descr->top_item + page * descr->page_size, TRUE );
1847 break;
1848 case SB_THUMBPOSITION:
1849 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1850 TRUE );
1851 break;
1852 case SB_THUMBTRACK:
1853 info.cbSize = sizeof(info);
1854 info.fMask = SIF_TRACKPOS;
1855 GetScrollInfo( hwnd, SB_VERT, &info );
1856 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1857 TRUE );
1858 break;
1859 case SB_LEFT:
1860 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1861 break;
1862 case SB_RIGHT:
1863 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1864 break;
1867 else if (descr->horz_extent)
1869 switch(LOWORD(wParam))
1871 case SB_LINELEFT:
1872 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1873 break;
1874 case SB_LINERIGHT:
1875 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1876 break;
1877 case SB_PAGELEFT:
1878 LISTBOX_SetHorizontalPos( hwnd, descr,
1879 descr->horz_pos - descr->width );
1880 break;
1881 case SB_PAGERIGHT:
1882 LISTBOX_SetHorizontalPos( hwnd, descr,
1883 descr->horz_pos + descr->width );
1884 break;
1885 case SB_THUMBPOSITION:
1886 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1887 break;
1888 case SB_THUMBTRACK:
1889 info.cbSize = sizeof(info);
1890 info.fMask = SIF_TRACKPOS;
1891 GetScrollInfo( hwnd, SB_HORZ, &info );
1892 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1893 break;
1894 case SB_LEFT:
1895 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1896 break;
1897 case SB_RIGHT:
1898 LISTBOX_SetHorizontalPos( hwnd, descr,
1899 descr->horz_extent - descr->width );
1900 break;
1903 return 0;
1906 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1908 short gcWheelDelta = 0;
1909 UINT pulScrollLines = 3;
1911 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1913 gcWheelDelta -= (short) HIWORD(wParam);
1915 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1917 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1918 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1919 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1921 return 0;
1924 /***********************************************************************
1925 * LISTBOX_HandleLButtonDown
1927 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1928 WPARAM wParam, INT x, INT y )
1930 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1931 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1932 if (!descr->caret_on && (descr->in_focus)) return 0;
1934 if (!descr->in_focus)
1936 if( !descr->lphc ) SetFocus( hwnd );
1937 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1940 if (index == -1) return 0;
1942 if (descr->style & LBS_EXTENDEDSEL)
1944 /* we should perhaps make sure that all items are deselected
1945 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1946 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1947 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1950 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1951 if (wParam & MK_CONTROL)
1953 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1954 LISTBOX_SetSelection( hwnd, descr, index,
1955 !descr->items[index].selected,
1956 (descr->style & LBS_NOTIFY) != 0);
1958 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1960 else
1962 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1963 LISTBOX_SetSelection( hwnd, descr, index,
1964 (!(descr->style & LBS_MULTIPLESEL) ||
1965 !descr->items[index].selected),
1966 (descr->style & LBS_NOTIFY) != 0 );
1969 descr->captured = TRUE;
1970 SetCapture( hwnd );
1972 if (!descr->lphc)
1974 if (descr->style & LBS_NOTIFY )
1975 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1976 MAKELPARAM( x, y ) );
1977 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1979 POINT pt;
1981 pt.x = x;
1982 pt.y = y;
1984 if (DragDetect( hwnd, pt ))
1985 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
1988 return 0;
1992 /*************************************************************************
1993 * LISTBOX_HandleLButtonDownCombo [Internal]
1995 * Process LButtonDown message for the ComboListBox
1997 nn * PARAMS
1998 * pWnd [I] The windows internal structure
1999 * pDescr [I] The ListBox internal structure
2000 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2001 * x [I] X Mouse Coordinate
2002 * y [I] Y Mouse Coordinate
2004 * RETURNS
2005 * 0 since we are processing the WM_LBUTTONDOWN Message
2007 * NOTES
2008 * This function is only to be used when a ListBox is a ComboListBox
2011 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2012 UINT msg, WPARAM wParam, INT x, INT y)
2014 RECT clientRect, screenRect;
2015 POINT mousePos;
2017 mousePos.x = x;
2018 mousePos.y = y;
2020 GetClientRect(hwnd, &clientRect);
2022 if(PtInRect(&clientRect, mousePos))
2024 /* MousePos is in client, resume normal processing */
2025 if (msg == WM_LBUTTONDOWN)
2027 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2028 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2030 else if (pDescr->style & LBS_NOTIFY)
2031 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2032 return 0;
2034 else
2036 POINT screenMousePos;
2037 HWND hWndOldCapture;
2039 /* Check the Non-Client Area */
2040 screenMousePos = mousePos;
2041 hWndOldCapture = GetCapture();
2042 ReleaseCapture();
2043 GetWindowRect(hwnd, &screenRect);
2044 ClientToScreen(hwnd, &screenMousePos);
2046 if(!PtInRect(&screenRect, screenMousePos))
2048 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2049 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2050 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2051 return 0;
2053 else
2055 /* Check to see the NC is a scrollbar */
2056 INT nHitTestType=0;
2057 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2058 /* Check Vertical scroll bar */
2059 if (style & WS_VSCROLL)
2061 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2062 if (PtInRect( &clientRect, mousePos ))
2064 nHitTestType = HTVSCROLL;
2067 /* Check horizontal scroll bar */
2068 if (style & WS_HSCROLL)
2070 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2071 if (PtInRect( &clientRect, mousePos ))
2073 nHitTestType = HTHSCROLL;
2076 /* Windows sends this message when a scrollbar is clicked
2079 if(nHitTestType != 0)
2081 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2082 MAKELONG(screenMousePos.x, screenMousePos.y));
2084 /* Resume the Capture after scrolling is complete
2086 if(hWndOldCapture != 0)
2088 SetCapture(hWndOldCapture);
2092 return 0;
2095 /***********************************************************************
2096 * LISTBOX_HandleLButtonUp
2098 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2100 if (LISTBOX_Timer != LB_TIMER_NONE)
2101 KillSystemTimer( hwnd, LB_TIMER_ID );
2102 LISTBOX_Timer = LB_TIMER_NONE;
2103 if (descr->captured)
2105 descr->captured = FALSE;
2106 if (GetCapture() == hwnd) ReleaseCapture();
2107 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2108 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2110 return 0;
2114 /***********************************************************************
2115 * LISTBOX_HandleTimer
2117 * Handle scrolling upon a timer event.
2118 * Return TRUE if scrolling should continue.
2120 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2121 INT index, TIMER_DIRECTION dir )
2123 switch(dir)
2125 case LB_TIMER_UP:
2126 if (descr->top_item) index = descr->top_item - 1;
2127 else index = 0;
2128 break;
2129 case LB_TIMER_LEFT:
2130 if (descr->top_item) index -= descr->page_size;
2131 break;
2132 case LB_TIMER_DOWN:
2133 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2134 if (index == descr->focus_item) index++;
2135 if (index >= descr->nb_items) index = descr->nb_items - 1;
2136 break;
2137 case LB_TIMER_RIGHT:
2138 if (index + descr->page_size < descr->nb_items)
2139 index += descr->page_size;
2140 break;
2141 case LB_TIMER_NONE:
2142 break;
2144 if (index == descr->focus_item) return FALSE;
2145 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2146 return TRUE;
2150 /***********************************************************************
2151 * LISTBOX_HandleSystemTimer
2153 * WM_SYSTIMER handler.
2155 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2157 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2159 KillSystemTimer( hwnd, LB_TIMER_ID );
2160 LISTBOX_Timer = LB_TIMER_NONE;
2162 return 0;
2166 /***********************************************************************
2167 * LISTBOX_HandleMouseMove
2169 * WM_MOUSEMOVE handler.
2171 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2172 INT x, INT y )
2174 INT index;
2175 TIMER_DIRECTION dir = LB_TIMER_NONE;
2177 if (!descr->captured) return;
2179 if (descr->style & LBS_MULTICOLUMN)
2181 if (y < 0) y = 0;
2182 else if (y >= descr->item_height * descr->page_size)
2183 y = descr->item_height * descr->page_size - 1;
2185 if (x < 0)
2187 dir = LB_TIMER_LEFT;
2188 x = 0;
2190 else if (x >= descr->width)
2192 dir = LB_TIMER_RIGHT;
2193 x = descr->width - 1;
2196 else
2198 if (y < 0) dir = LB_TIMER_UP; /* above */
2199 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2202 index = LISTBOX_GetItemFromPoint( descr, x, y );
2203 if (index == -1) index = descr->focus_item;
2204 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2206 /* Start/stop the system timer */
2208 if (dir != LB_TIMER_NONE)
2209 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2210 else if (LISTBOX_Timer != LB_TIMER_NONE)
2211 KillSystemTimer( hwnd, LB_TIMER_ID );
2212 LISTBOX_Timer = dir;
2216 /***********************************************************************
2217 * LISTBOX_HandleKeyDown
2219 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2221 INT caret = -1;
2222 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2223 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2224 bForceSelection = FALSE; /* only for single select list */
2226 if (descr->style & LBS_WANTKEYBOARDINPUT)
2228 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2229 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2230 hwnd );
2231 if (caret == -2) return 0;
2233 if (caret == -1) switch(wParam)
2235 case VK_LEFT:
2236 if (descr->style & LBS_MULTICOLUMN)
2238 bForceSelection = FALSE;
2239 if (descr->focus_item >= descr->page_size)
2240 caret = descr->focus_item - descr->page_size;
2241 break;
2243 /* fall through */
2244 case VK_UP:
2245 caret = descr->focus_item - 1;
2246 if (caret < 0) caret = 0;
2247 break;
2248 case VK_RIGHT:
2249 if (descr->style & LBS_MULTICOLUMN)
2251 bForceSelection = FALSE;
2252 if (descr->focus_item + descr->page_size < descr->nb_items)
2253 caret = descr->focus_item + descr->page_size;
2254 break;
2256 /* fall through */
2257 case VK_DOWN:
2258 caret = descr->focus_item + 1;
2259 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2260 break;
2262 case VK_PRIOR:
2263 if (descr->style & LBS_MULTICOLUMN)
2265 INT page = descr->width / descr->column_width;
2266 if (page < 1) page = 1;
2267 caret = descr->focus_item - (page * descr->page_size) + 1;
2269 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2270 if (caret < 0) caret = 0;
2271 break;
2272 case VK_NEXT:
2273 if (descr->style & LBS_MULTICOLUMN)
2275 INT page = descr->width / descr->column_width;
2276 if (page < 1) page = 1;
2277 caret = descr->focus_item + (page * descr->page_size) - 1;
2279 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2280 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2281 break;
2282 case VK_HOME:
2283 caret = 0;
2284 break;
2285 case VK_END:
2286 caret = descr->nb_items - 1;
2287 break;
2288 case VK_SPACE:
2289 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2290 else if (descr->style & LBS_MULTIPLESEL)
2292 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2293 !descr->items[descr->focus_item].selected,
2294 (descr->style & LBS_NOTIFY) != 0 );
2296 break;
2297 default:
2298 bForceSelection = FALSE;
2300 if (bForceSelection) /* focused item is used instead of key */
2301 caret = descr->focus_item;
2302 if (caret >= 0)
2304 if ((descr->style & LBS_EXTENDEDSEL) &&
2305 !(GetKeyState( VK_SHIFT ) & 0x8000))
2306 descr->anchor_item = caret;
2307 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2308 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2309 if (descr->style & LBS_NOTIFY)
2311 if( descr->lphc )
2313 /* make sure that combo parent doesn't hide us */
2314 descr->lphc->wState |= CBF_NOROLLUP;
2316 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2319 return 0;
2323 /***********************************************************************
2324 * LISTBOX_HandleChar
2326 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2328 INT caret = -1;
2329 WCHAR str[2];
2331 str[0] = charW;
2332 str[1] = '\0';
2334 if (descr->style & LBS_WANTKEYBOARDINPUT)
2336 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2337 MAKEWPARAM(charW, descr->focus_item),
2338 hwnd );
2339 if (caret == -2) return 0;
2341 if (caret == -1)
2342 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2343 if (caret != -1)
2345 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2346 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2347 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2348 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2349 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2351 return 0;
2355 /***********************************************************************
2356 * LISTBOX_Create
2358 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2360 LB_DESCR *descr;
2361 MEASUREITEMSTRUCT mis;
2362 RECT rect;
2364 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2365 return FALSE;
2367 GetClientRect( hwnd, &rect );
2368 descr->owner = GetParent( hwnd );
2369 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2370 descr->width = rect.right - rect.left;
2371 descr->height = rect.bottom - rect.top;
2372 descr->items = NULL;
2373 descr->nb_items = 0;
2374 descr->top_item = 0;
2375 descr->selected_item = -1;
2376 descr->focus_item = 0;
2377 descr->anchor_item = -1;
2378 descr->item_height = 1;
2379 descr->page_size = 1;
2380 descr->column_width = 150;
2381 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2382 descr->horz_pos = 0;
2383 descr->nb_tabs = 0;
2384 descr->tabs = NULL;
2385 descr->caret_on = lphc ? FALSE : TRUE;
2386 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2387 descr->in_focus = FALSE;
2388 descr->captured = FALSE;
2389 descr->font = 0;
2390 descr->locale = 0; /* FIXME */
2391 descr->lphc = lphc;
2393 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2395 /* Win95 document "List Box Differences" from MSDN:
2396 If a list box in a version 3.x application has either the
2397 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2398 horizontal and vertical scroll bars.
2400 descr->style |= WS_VSCROLL | WS_HSCROLL;
2403 if( lphc )
2405 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2406 hwnd, descr->owner, lphc->self );
2407 descr->owner = lphc->self;
2410 SetWindowLongA( hwnd, 0, (LONG)descr );
2412 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2414 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2415 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2416 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2417 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2419 if (descr->style & LBS_OWNERDRAWFIXED)
2421 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2423 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2424 descr->item_height = lphc->fixedOwnerDrawHeight;
2426 else
2428 UINT id = GetWindowLongA( hwnd, GWL_ID );
2429 mis.CtlType = ODT_LISTBOX;
2430 mis.CtlID = id;
2431 mis.itemID = -1;
2432 mis.itemWidth = 0;
2433 mis.itemData = 0;
2434 mis.itemHeight = descr->item_height;
2435 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2436 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2440 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2441 return TRUE;
2445 /***********************************************************************
2446 * LISTBOX_Destroy
2448 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2450 LISTBOX_ResetContent( hwnd, descr );
2451 SetWindowLongA( hwnd, 0, 0 );
2452 HeapFree( GetProcessHeap(), 0, descr );
2453 return TRUE;
2457 /***********************************************************************
2458 * ListBoxWndProc_common
2460 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2461 WPARAM wParam, LPARAM lParam, BOOL unicode )
2463 LRESULT ret;
2464 LB_DESCR *descr;
2466 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2468 if (msg == WM_CREATE)
2470 if (!LISTBOX_Create( hwnd, NULL ))
2471 return -1;
2472 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2473 return 0;
2475 /* Ignore all other messages before we get a WM_CREATE */
2476 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2477 DefWindowProcA( hwnd, msg, wParam, lParam );
2480 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2481 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2482 switch(msg)
2484 case LB_RESETCONTENT16:
2485 case LB_RESETCONTENT:
2486 LISTBOX_ResetContent( hwnd, descr );
2487 LISTBOX_UpdateScroll( hwnd, descr );
2488 InvalidateRect( hwnd, NULL, TRUE );
2489 return 0;
2491 case LB_ADDSTRING16:
2492 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2493 /* fall through */
2494 case LB_ADDSTRING:
2496 INT ret;
2497 LPWSTR textW;
2498 if(unicode || !HAS_STRINGS(descr))
2499 textW = (LPWSTR)lParam;
2500 else
2502 LPSTR textA = (LPSTR)lParam;
2503 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2504 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2505 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2507 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2508 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2509 if (!unicode && HAS_STRINGS(descr))
2510 HeapFree(GetProcessHeap(), 0, textW);
2511 return ret;
2514 case LB_INSERTSTRING16:
2515 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2516 wParam = (INT)(INT16)wParam;
2517 /* fall through */
2518 case LB_INSERTSTRING:
2520 INT ret;
2521 LPWSTR textW;
2522 if(unicode || !HAS_STRINGS(descr))
2523 textW = (LPWSTR)lParam;
2524 else
2526 LPSTR textA = (LPSTR)lParam;
2527 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2528 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2529 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2531 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2532 if(!unicode && HAS_STRINGS(descr))
2533 HeapFree(GetProcessHeap(), 0, textW);
2534 return ret;
2537 case LB_ADDFILE16:
2538 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2539 /* fall through */
2540 case LB_ADDFILE:
2542 INT ret;
2543 LPWSTR textW;
2544 if(unicode || !HAS_STRINGS(descr))
2545 textW = (LPWSTR)lParam;
2546 else
2548 LPSTR textA = (LPSTR)lParam;
2549 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2550 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2551 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2553 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2554 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2555 if(!unicode && HAS_STRINGS(descr))
2556 HeapFree(GetProcessHeap(), 0, textW);
2557 return ret;
2560 case LB_DELETESTRING16:
2561 case LB_DELETESTRING:
2562 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2563 return descr->nb_items;
2564 else
2565 return LB_ERR;
2567 case LB_GETITEMDATA16:
2568 case LB_GETITEMDATA:
2569 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2570 return LB_ERR;
2571 return descr->items[wParam].data;
2573 case LB_SETITEMDATA16:
2574 case LB_SETITEMDATA:
2575 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2576 return LB_ERR;
2577 descr->items[wParam].data = (DWORD)lParam;
2578 return LB_OKAY;
2580 case LB_GETCOUNT16:
2581 case LB_GETCOUNT:
2582 return descr->nb_items;
2584 case LB_GETTEXT16:
2585 lParam = (LPARAM)MapSL(lParam);
2586 /* fall through */
2587 case LB_GETTEXT:
2588 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2590 case LB_GETTEXTLEN16:
2591 /* fall through */
2592 case LB_GETTEXTLEN:
2593 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2594 return LB_ERR;
2595 return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2596 : sizeof(DWORD));
2598 case LB_GETCURSEL16:
2599 case LB_GETCURSEL:
2600 if (descr->nb_items==0)
2601 return LB_ERR;
2602 if (!IS_MULTISELECT(descr))
2603 return descr->selected_item;
2604 /* else */
2605 if (descr->selected_item!=-1)
2606 return descr->selected_item;
2607 /* else */
2608 return descr->focus_item;
2609 /* otherwise, if the user tries to move the selection with the */
2610 /* arrow keys, we will give the application something to choke on */
2611 case LB_GETTOPINDEX16:
2612 case LB_GETTOPINDEX:
2613 return descr->top_item;
2615 case LB_GETITEMHEIGHT16:
2616 case LB_GETITEMHEIGHT:
2617 return LISTBOX_GetItemHeight( descr, wParam );
2619 case LB_SETITEMHEIGHT16:
2620 lParam = LOWORD(lParam);
2621 /* fall through */
2622 case LB_SETITEMHEIGHT:
2623 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam );
2625 case LB_ITEMFROMPOINT:
2627 POINT pt;
2628 RECT rect;
2630 pt.x = LOWORD(lParam);
2631 pt.y = HIWORD(lParam);
2632 rect.left = 0;
2633 rect.top = 0;
2634 rect.right = descr->width;
2635 rect.bottom = descr->height;
2637 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2638 !PtInRect( &rect, pt ) );
2641 case LB_SETCARETINDEX16:
2642 case LB_SETCARETINDEX:
2643 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2644 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2645 return LB_ERR;
2646 else if (ISWIN31)
2647 return wParam;
2648 else
2649 return LB_OKAY;
2651 case LB_GETCARETINDEX16:
2652 case LB_GETCARETINDEX:
2653 return descr->focus_item;
2655 case LB_SETTOPINDEX16:
2656 case LB_SETTOPINDEX:
2657 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2659 case LB_SETCOLUMNWIDTH16:
2660 case LB_SETCOLUMNWIDTH:
2661 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2663 case LB_GETITEMRECT16:
2665 RECT rect;
2666 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2667 CONV_RECT32TO16( &rect, MapSL(lParam) );
2669 return ret;
2671 case LB_GETITEMRECT:
2672 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2674 case LB_FINDSTRING16:
2675 wParam = (INT)(INT16)wParam;
2676 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2677 /* fall through */
2678 case LB_FINDSTRING:
2680 INT ret;
2681 LPWSTR textW;
2682 if(unicode || !HAS_STRINGS(descr))
2683 textW = (LPWSTR)lParam;
2684 else
2686 LPSTR textA = (LPSTR)lParam;
2687 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2688 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2689 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2691 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2692 if(!unicode && HAS_STRINGS(descr))
2693 HeapFree(GetProcessHeap(), 0, textW);
2694 return ret;
2697 case LB_FINDSTRINGEXACT16:
2698 wParam = (INT)(INT16)wParam;
2699 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2700 /* fall through */
2701 case LB_FINDSTRINGEXACT:
2703 INT ret;
2704 LPWSTR textW;
2705 if(unicode || !HAS_STRINGS(descr))
2706 textW = (LPWSTR)lParam;
2707 else
2709 LPSTR textA = (LPSTR)lParam;
2710 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2711 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2712 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2714 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2715 if(!unicode && HAS_STRINGS(descr))
2716 HeapFree(GetProcessHeap(), 0, textW);
2717 return ret;
2720 case LB_SELECTSTRING16:
2721 wParam = (INT)(INT16)wParam;
2722 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2723 /* fall through */
2724 case LB_SELECTSTRING:
2726 INT index;
2727 LPWSTR textW;
2729 if(HAS_STRINGS(descr))
2730 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2731 debugstr_a((LPSTR)lParam));
2732 if(unicode || !HAS_STRINGS(descr))
2733 textW = (LPWSTR)lParam;
2734 else
2736 LPSTR textA = (LPSTR)lParam;
2737 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2738 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2739 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2741 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2742 if(!unicode && HAS_STRINGS(descr))
2743 HeapFree(GetProcessHeap(), 0, textW);
2744 if (index != LB_ERR)
2746 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2747 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2749 return index;
2752 case LB_GETSEL16:
2753 wParam = (INT)(INT16)wParam;
2754 /* fall through */
2755 case LB_GETSEL:
2756 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2757 return LB_ERR;
2758 return descr->items[wParam].selected;
2760 case LB_SETSEL16:
2761 lParam = (INT)(INT16)lParam;
2762 /* fall through */
2763 case LB_SETSEL:
2764 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2766 case LB_SETCURSEL16:
2767 wParam = (INT)(INT16)wParam;
2768 /* fall through */
2769 case LB_SETCURSEL:
2770 if (IS_MULTISELECT(descr)) return LB_ERR;
2771 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2772 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2774 case LB_GETSELCOUNT16:
2775 case LB_GETSELCOUNT:
2776 return LISTBOX_GetSelCount( descr );
2778 case LB_GETSELITEMS16:
2779 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2781 case LB_GETSELITEMS:
2782 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2784 case LB_SELITEMRANGE16:
2785 case LB_SELITEMRANGE:
2786 if (LOWORD(lParam) <= HIWORD(lParam))
2787 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2788 HIWORD(lParam), wParam );
2789 else
2790 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2791 LOWORD(lParam), wParam );
2793 case LB_SELITEMRANGEEX16:
2794 case LB_SELITEMRANGEEX:
2795 if ((INT)lParam >= (INT)wParam)
2796 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2797 else
2798 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2800 case LB_GETHORIZONTALEXTENT16:
2801 case LB_GETHORIZONTALEXTENT:
2802 return descr->horz_extent;
2804 case LB_SETHORIZONTALEXTENT16:
2805 case LB_SETHORIZONTALEXTENT:
2806 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2808 case LB_GETANCHORINDEX16:
2809 case LB_GETANCHORINDEX:
2810 return descr->anchor_item;
2812 case LB_SETANCHORINDEX16:
2813 wParam = (INT)(INT16)wParam;
2814 /* fall through */
2815 case LB_SETANCHORINDEX:
2816 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2817 return LB_ERR;
2818 descr->anchor_item = (INT)wParam;
2819 return LB_OKAY;
2821 case LB_DIR16:
2822 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2823 * be set automatically (this is different in Win32) */
2824 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2825 lParam = (LPARAM)MapSL(lParam);
2826 /* fall through */
2827 case LB_DIR:
2829 INT ret;
2830 LPWSTR textW;
2831 if(unicode)
2832 textW = (LPWSTR)lParam;
2833 else
2835 LPSTR textA = (LPSTR)lParam;
2836 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2837 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2838 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2840 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2841 if(!unicode)
2842 HeapFree(GetProcessHeap(), 0, textW);
2843 return ret;
2846 case LB_GETLOCALE:
2847 return descr->locale;
2849 case LB_SETLOCALE:
2850 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2851 return LB_OKAY;
2853 case LB_INITSTORAGE:
2854 return LISTBOX_InitStorage( hwnd, descr, wParam );
2856 case LB_SETCOUNT:
2857 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2859 case LB_SETTABSTOPS16:
2860 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2862 case LB_SETTABSTOPS:
2863 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2865 case LB_CARETON16:
2866 case LB_CARETON:
2867 if (descr->caret_on)
2868 return LB_OKAY;
2869 descr->caret_on = TRUE;
2870 if ((descr->focus_item != -1) && (descr->in_focus))
2871 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2872 return LB_OKAY;
2874 case LB_CARETOFF16:
2875 case LB_CARETOFF:
2876 if (!descr->caret_on)
2877 return LB_OKAY;
2878 descr->caret_on = FALSE;
2879 if ((descr->focus_item != -1) && (descr->in_focus))
2880 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2881 return LB_OKAY;
2883 case WM_DESTROY:
2884 return LISTBOX_Destroy( hwnd, descr );
2886 case WM_ENABLE:
2887 InvalidateRect( hwnd, NULL, TRUE );
2888 return 0;
2890 case WM_SETREDRAW:
2891 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2892 return 0;
2894 case WM_GETDLGCODE:
2895 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2897 case WM_PAINT:
2899 PAINTSTRUCT ps;
2900 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2901 ret = LISTBOX_Paint( hwnd, descr, hdc );
2902 if( !wParam ) EndPaint( hwnd, &ps );
2904 return ret;
2905 case WM_SIZE:
2906 LISTBOX_UpdateSize( hwnd, descr );
2907 return 0;
2908 case WM_GETFONT:
2909 return descr->font;
2910 case WM_SETFONT:
2911 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2912 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2913 return 0;
2914 case WM_SETFOCUS:
2915 descr->in_focus = TRUE;
2916 descr->caret_on = TRUE;
2917 if (descr->focus_item != -1)
2918 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2919 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2920 return 0;
2921 case WM_KILLFOCUS:
2922 descr->in_focus = FALSE;
2923 if ((descr->focus_item != -1) && descr->caret_on)
2924 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2925 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2926 return 0;
2927 case WM_HSCROLL:
2928 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2929 case WM_VSCROLL:
2930 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2931 case WM_MOUSEWHEEL:
2932 if (wParam & (MK_SHIFT | MK_CONTROL))
2933 return DefWindowProcW( hwnd, msg, wParam, lParam );
2934 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2935 case WM_LBUTTONDOWN:
2936 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2937 (INT16)LOWORD(lParam),
2938 (INT16)HIWORD(lParam) );
2939 case WM_LBUTTONDBLCLK:
2940 if (descr->style & LBS_NOTIFY)
2941 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2942 return 0;
2943 case WM_MOUSEMOVE:
2944 if (GetCapture() == hwnd)
2945 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2946 (INT16)HIWORD(lParam) );
2947 return 0;
2948 case WM_LBUTTONUP:
2949 return LISTBOX_HandleLButtonUp( hwnd, descr );
2950 case WM_KEYDOWN:
2951 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2952 case WM_CHAR:
2954 WCHAR charW;
2955 if(unicode)
2956 charW = (WCHAR)wParam;
2957 else
2959 CHAR charA = (CHAR)wParam;
2960 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2962 return LISTBOX_HandleChar( hwnd, descr, charW );
2964 case WM_SYSTIMER:
2965 return LISTBOX_HandleSystemTimer( hwnd, descr );
2966 case WM_ERASEBKGND:
2967 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2969 RECT rect;
2970 HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2971 wParam, (LPARAM)hwnd );
2972 TRACE("hbrush = %04x\n", hbrush);
2973 if(!hbrush)
2974 hbrush = GetSysColorBrush(COLOR_WINDOW);
2975 if(hbrush)
2977 GetClientRect(hwnd, &rect);
2978 FillRect((HDC)wParam, &rect, hbrush);
2981 return 1;
2982 case WM_DROPFILES:
2983 if( !descr->lphc )
2984 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
2985 SendMessageA( descr->owner, msg, wParam, lParam );
2986 break;
2988 case WM_DROPOBJECT:
2989 case WM_QUERYDROPOBJECT:
2990 case WM_DRAGSELECT:
2991 case WM_DRAGMOVE:
2992 if( !descr->lphc )
2994 LPDRAGINFO16 dragInfo = MapSL( lParam );
2995 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
2996 dragInfo->pt.y );
2997 return SendMessage16( descr->owner, msg, wParam, lParam );
2999 break;
3001 default:
3002 if ((msg >= WM_USER) && (msg < 0xc000))
3003 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3004 hwnd, msg, wParam, lParam );
3005 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3006 DefWindowProcA( hwnd, msg, wParam, lParam );
3008 return 0;
3011 /***********************************************************************
3012 * ListBoxWndProcA
3014 * This is just a wrapper for the real wndproc, it only does window locking
3015 * and unlocking.
3017 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3019 if (!IsWindow(hwnd)) return 0;
3020 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3023 /***********************************************************************
3024 * ListBoxWndProcW
3026 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3028 if (!IsWindow(hwnd)) return 0;
3029 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3032 /***********************************************************************
3033 * ComboLBWndProc_common
3035 * The real combo listbox wndproc
3037 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3038 WPARAM wParam, LPARAM lParam, BOOL unicode )
3040 LRESULT lRet = 0;
3041 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
3043 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3044 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3046 if( descr || msg == WM_CREATE )
3048 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3050 switch( msg )
3052 case WM_CREATE:
3054 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3055 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3056 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3057 return LISTBOX_Create( hwnd, lphc );
3059 case WM_MOUSEMOVE:
3060 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3061 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3063 POINT mousePos;
3064 BOOL captured;
3065 RECT clientRect;
3067 mousePos.x = (INT16)LOWORD(lParam);
3068 mousePos.y = (INT16)HIWORD(lParam);
3071 * If we are in a dropdown combobox, we simulate that
3072 * the mouse is captured to show the tracking of the item.
3074 GetClientRect(hwnd, &clientRect);
3076 if (PtInRect( &clientRect, mousePos ))
3078 captured = descr->captured;
3079 descr->captured = TRUE;
3081 LISTBOX_HandleMouseMove( hwnd, descr,
3082 mousePos.x, mousePos.y);
3084 descr->captured = captured;
3087 else
3089 LISTBOX_HandleMouseMove( hwnd, descr,
3090 mousePos.x, mousePos.y);
3093 return 0;
3096 else
3099 * If we are in Win3.1 look, go with the default behavior.
3101 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3102 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3104 case WM_LBUTTONUP:
3105 if (TWEAK_WineLook > WIN31_LOOK)
3107 POINT mousePos;
3108 RECT clientRect;
3111 * If the mouse button "up" is not in the listbox,
3112 * we make sure there is no selection by re-selecting the
3113 * item that was selected when the listbox was made visible.
3115 mousePos.x = (INT16)LOWORD(lParam);
3116 mousePos.y = (INT16)HIWORD(lParam);
3118 GetClientRect(hwnd, &clientRect);
3121 * When the user clicks outside the combobox and the focus
3122 * is lost, the owning combobox will send a fake buttonup with
3123 * 0xFFFFFFF as the mouse location, we must also revert the
3124 * selection to the original selection.
3126 if ( (lParam == (LPARAM)-1) ||
3127 (!PtInRect( &clientRect, mousePos )) )
3129 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3132 return LISTBOX_HandleLButtonUp( hwnd, descr );
3133 case WM_LBUTTONDBLCLK:
3134 case WM_LBUTTONDOWN:
3135 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3136 (INT16)LOWORD(lParam),
3137 (INT16)HIWORD(lParam) );
3138 case WM_NCACTIVATE:
3139 return FALSE;
3140 case WM_KEYDOWN:
3141 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3143 /* for some reason(?) Windows makes it possible to
3144 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3146 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3147 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3148 && (wParam == VK_DOWN || wParam == VK_UP)) )
3150 COMBO_FlipListbox( lphc, FALSE, FALSE );
3151 return 0;
3154 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3156 case LB_SETCURSEL16:
3157 case LB_SETCURSEL:
3158 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3159 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3160 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3161 return lRet;
3162 case WM_NCDESTROY:
3163 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3164 lphc->hWndLBox = 0;
3165 /* fall through */
3167 default:
3168 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3169 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3172 lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3173 DefWindowProcA( hwnd, msg, wParam, lParam );
3175 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3177 return lRet;
3180 /***********************************************************************
3181 * ComboLBWndProcA
3183 * NOTE: in Windows, winproc address of the ComboLBox is the same
3184 * as that of the Listbox.
3186 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3188 if (!IsWindow(hwnd)) return 0;
3189 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3192 /***********************************************************************
3193 * ComboLBWndProcW
3195 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3197 if (!IsWindow(hwnd)) return 0;
3198 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );