Include wine/test.h before windows headers.
[wine/multimedia.git] / controls / listbox.c
blobe5667cd071a216c27f4eb9c6271cb1bff09a8ae5
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include "windef.h"
25 #include "wingdi.h"
26 #include "wine/winuser16.h"
27 #include "wine/winbase16.h"
28 #include "wine/unicode.h"
29 #include "winuser.h"
30 #include "winerror.h"
31 #include "spy.h"
32 #include "user.h"
33 #include "controls.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
37 WINE_DECLARE_DEBUG_CHANNEL(combo);
39 /* Unimplemented yet:
40 * - LBS_USETABSTOPS
41 * - Locale handling
43 * Probably needs improvement:
44 * - LBS_NOSEL
47 /* Items array granularity */
48 #define LB_ARRAY_GRANULARITY 16
50 /* Scrolling timeout in ms */
51 #define LB_SCROLL_TIMEOUT 50
53 /* Listbox system timer id */
54 #define LB_TIMER_ID 2
56 /* flag listbox changed while setredraw false - internal style */
57 #define LBS_DISPLAYCHANGED 0x80000000
59 /* Item structure */
60 typedef struct
62 LPWSTR str; /* Item text */
63 BOOL selected; /* Is item selected? */
64 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
65 DWORD data; /* User data */
66 } LB_ITEMDATA;
68 /* Listbox structure */
69 typedef struct
71 HWND owner; /* Owner window to send notifications to */
72 UINT style; /* Window style */
73 INT width; /* Window width */
74 INT height; /* Window height */
75 LB_ITEMDATA *items; /* Array of items */
76 INT nb_items; /* Number of items */
77 INT top_item; /* Top visible item */
78 INT selected_item; /* Selected item */
79 INT focus_item; /* Item that has the focus */
80 INT anchor_item; /* Anchor item for extended selection */
81 INT item_height; /* Default item height */
82 INT page_size; /* Items per listbox page */
83 INT column_width; /* Column width for multi-column listboxes */
84 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
85 INT horz_pos; /* Horizontal position */
86 INT nb_tabs; /* Number of tabs in array */
87 INT *tabs; /* Array of tabs */
88 BOOL caret_on; /* Is caret on? */
89 BOOL captured; /* Is mouse captured? */
90 BOOL in_focus;
91 HFONT font; /* Current font */
92 LCID locale; /* Current locale for string comparisons */
93 LPHEADCOMBO lphc; /* ComboLBox */
94 } LB_DESCR;
97 #define IS_OWNERDRAW(descr) \
98 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
100 #define HAS_STRINGS(descr) \
101 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
104 #define IS_MULTISELECT(descr) \
105 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
107 #define SEND_NOTIFICATION(hwnd,descr,code) \
108 (SendMessageW( (descr)->owner, WM_COMMAND, \
109 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
111 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
113 /* Current timer status */
114 typedef enum
116 LB_TIMER_NONE,
117 LB_TIMER_UP,
118 LB_TIMER_LEFT,
119 LB_TIMER_DOWN,
120 LB_TIMER_RIGHT
121 } TIMER_DIRECTION;
123 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
125 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
126 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
127 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
128 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
132 /*********************************************************************
133 * listbox class descriptor
135 const struct builtin_class_descr LISTBOX_builtin_class =
137 "ListBox", /* name */
138 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
139 ListBoxWndProcA, /* procA */
140 ListBoxWndProcW, /* procW */
141 sizeof(LB_DESCR *), /* extra */
142 IDC_ARROWA, /* cursor */
143 0 /* brush */
147 /*********************************************************************
148 * combolbox class descriptor
150 const struct builtin_class_descr COMBOLBOX_builtin_class =
152 "ComboLBox", /* name */
153 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
154 ComboLBWndProcA, /* procA */
155 ComboLBWndProcW, /* procW */
156 sizeof(LB_DESCR *), /* extra */
157 IDC_ARROWA, /* cursor */
158 0 /* brush */
162 /* check whether app is a Win 3.1 app */
163 inline static BOOL is_old_app( HWND hwnd )
165 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
169 /***********************************************************************
170 * LISTBOX_Dump
172 void LISTBOX_Dump( HWND hwnd )
174 INT i;
175 LB_ITEMDATA *item;
176 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
178 TRACE( "Listbox:\n" );
179 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
180 hwnd, (UINT)descr, descr->nb_items,
181 descr->top_item );
182 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
184 TRACE( "%4d: %-40s %d %08lx %3d\n",
185 i, debugstr_w(item->str), item->selected, item->data, item->height );
190 /***********************************************************************
191 * LISTBOX_GetCurrentPageSize
193 * Return the current page size
195 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
197 INT i, height;
198 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
199 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
201 if ((height += descr->items[i].height) > descr->height) break;
203 if (i == descr->top_item) return 1;
204 else return i - descr->top_item;
208 /***********************************************************************
209 * LISTBOX_GetMaxTopIndex
211 * Return the maximum possible index for the top of the listbox.
213 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
215 INT max, page;
217 if (descr->style & LBS_OWNERDRAWVARIABLE)
219 page = descr->height;
220 for (max = descr->nb_items - 1; max >= 0; max--)
221 if ((page -= descr->items[max].height) < 0) break;
222 if (max < descr->nb_items - 1) max++;
224 else if (descr->style & LBS_MULTICOLUMN)
226 if ((page = descr->width / descr->column_width) < 1) page = 1;
227 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
228 max = (max - page) * descr->page_size;
230 else
232 max = descr->nb_items - descr->page_size;
234 if (max < 0) max = 0;
235 return max;
239 /***********************************************************************
240 * LISTBOX_UpdateScroll
242 * Update the scrollbars. Should be called whenever the content
243 * of the listbox changes.
245 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
247 SCROLLINFO info;
249 /* Check the listbox scroll bar flags individually before we call
250 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
251 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
252 scroll bar when we do not need one.
253 if (!(descr->style & WS_VSCROLL)) return;
256 /* It is important that we check descr->style, and not wnd->dwStyle,
257 for WS_VSCROLL, as the former is exactly the one passed in
258 argument to CreateWindow.
259 In Windows (and from now on in Wine :) a listbox created
260 with such a style (no WS_SCROLL) does not update
261 the scrollbar with listbox-related data, thus letting
262 the programmer use it for his/her own purposes. */
264 if (descr->style & LBS_NOREDRAW) return;
265 info.cbSize = sizeof(info);
267 if (descr->style & LBS_MULTICOLUMN)
269 info.nMin = 0;
270 info.nMax = (descr->nb_items - 1) / descr->page_size;
271 info.nPos = descr->top_item / descr->page_size;
272 info.nPage = descr->width / descr->column_width;
273 if (info.nPage < 1) info.nPage = 1;
274 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
275 if (descr->style & LBS_DISABLENOSCROLL)
276 info.fMask |= SIF_DISABLENOSCROLL;
277 if (descr->style & WS_HSCROLL)
278 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
279 info.nMax = 0;
280 info.fMask = SIF_RANGE;
281 if (descr->style & WS_VSCROLL)
282 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
284 else
286 info.nMin = 0;
287 info.nMax = descr->nb_items - 1;
288 info.nPos = descr->top_item;
289 info.nPage = LISTBOX_GetCurrentPageSize( descr );
290 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
291 if (descr->style & LBS_DISABLENOSCROLL)
292 info.fMask |= SIF_DISABLENOSCROLL;
293 if (descr->style & WS_VSCROLL)
294 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
296 if (descr->horz_extent)
298 info.nMin = 0;
299 info.nMax = descr->horz_extent - 1;
300 info.nPos = descr->horz_pos;
301 info.nPage = descr->width;
302 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
303 if (descr->style & LBS_DISABLENOSCROLL)
304 info.fMask |= SIF_DISABLENOSCROLL;
305 if (descr->style & WS_HSCROLL)
306 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
312 /***********************************************************************
313 * LISTBOX_SetTopItem
315 * Set the top item of the listbox, scrolling up or down if necessary.
317 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
318 BOOL scroll )
320 INT max = LISTBOX_GetMaxTopIndex( descr );
321 if (index > max) index = max;
322 if (index < 0) index = 0;
323 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
324 if (descr->top_item == index) return LB_OKAY;
325 if (descr->style & LBS_MULTICOLUMN)
327 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
328 if (scroll && (abs(diff) < descr->width))
329 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
330 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
332 else
333 scroll = FALSE;
335 else if (scroll)
337 INT diff;
338 if (descr->style & LBS_OWNERDRAWVARIABLE)
340 INT i;
341 diff = 0;
342 if (index > descr->top_item)
344 for (i = index - 1; i >= descr->top_item; i--)
345 diff -= descr->items[i].height;
347 else
349 for (i = index; i < descr->top_item; i++)
350 diff += descr->items[i].height;
353 else
354 diff = (descr->top_item - index) * descr->item_height;
356 if (abs(diff) < descr->height)
357 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
358 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
359 else
360 scroll = FALSE;
362 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
363 descr->top_item = index;
364 LISTBOX_UpdateScroll( hwnd, descr );
365 return LB_OKAY;
369 /***********************************************************************
370 * LISTBOX_UpdatePage
372 * Update the page size. Should be called when the size of
373 * the client area or the item height changes.
375 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
377 INT page_size;
379 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
380 page_size = 1;
381 if (page_size == descr->page_size) return;
382 descr->page_size = page_size;
383 if (descr->style & LBS_MULTICOLUMN)
384 InvalidateRect( hwnd, NULL, TRUE );
385 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
389 /***********************************************************************
390 * LISTBOX_UpdateSize
392 * Update the size of the listbox. Should be called when the size of
393 * the client area changes.
395 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
397 RECT rect;
399 GetClientRect( hwnd, &rect );
400 descr->width = rect.right - rect.left;
401 descr->height = rect.bottom - rect.top;
402 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
404 INT remaining;
405 RECT rect;
407 GetWindowRect( hwnd, &rect );
408 if(descr->item_height != 0)
409 remaining = descr->height % descr->item_height;
410 else
411 remaining = 0;
412 if ((descr->height > descr->item_height) && remaining)
414 if (is_old_app(hwnd))
415 { /* give a margin for error to 16 bits programs - if we need
416 less than the height of the nonclient area, round to the
417 *next* number of items */
418 int ncheight = rect.bottom - rect.top - descr->height;
419 if ((descr->item_height - remaining) <= ncheight)
420 remaining = remaining - descr->item_height;
422 TRACE("[%04x]: changing height %d -> %d\n",
423 hwnd, descr->height, descr->height - remaining );
424 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
425 rect.bottom - rect.top - remaining,
426 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
427 return;
430 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
431 LISTBOX_UpdatePage( hwnd, descr );
432 LISTBOX_UpdateScroll( hwnd, descr );
434 /* Invalidate the focused item so it will be repainted correctly */
435 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
437 InvalidateRect( hwnd, &rect, FALSE );
442 /***********************************************************************
443 * LISTBOX_GetItemRect
445 * Get the rectangle enclosing an item, in listbox client coordinates.
446 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
448 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
450 /* Index <= 0 is legal even on empty listboxes */
451 if (index && (index >= descr->nb_items)) return -1;
452 SetRect( rect, 0, 0, descr->width, descr->height );
453 if (descr->style & LBS_MULTICOLUMN)
455 INT col = (index / descr->page_size) -
456 (descr->top_item / descr->page_size);
457 rect->left += col * descr->column_width;
458 rect->right = rect->left + descr->column_width;
459 rect->top += (index % descr->page_size) * descr->item_height;
460 rect->bottom = rect->top + descr->item_height;
462 else if (descr->style & LBS_OWNERDRAWVARIABLE)
464 INT i;
465 rect->right += descr->horz_pos;
466 if ((index >= 0) && (index < descr->nb_items))
468 if (index < descr->top_item)
470 for (i = descr->top_item-1; i >= index; i--)
471 rect->top -= descr->items[i].height;
473 else
475 for (i = descr->top_item; i < index; i++)
476 rect->top += descr->items[i].height;
478 rect->bottom = rect->top + descr->items[index].height;
482 else
484 rect->top += (index - descr->top_item) * descr->item_height;
485 rect->bottom = rect->top + descr->item_height;
486 rect->right += descr->horz_pos;
489 return ((rect->left < descr->width) && (rect->right > 0) &&
490 (rect->top < descr->height) && (rect->bottom > 0));
494 /***********************************************************************
495 * LISTBOX_GetItemFromPoint
497 * Return the item nearest from point (x,y) (in client coordinates).
499 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
501 INT index = descr->top_item;
503 if (!descr->nb_items) return -1; /* No items */
504 if (descr->style & LBS_OWNERDRAWVARIABLE)
506 INT pos = 0;
507 if (y >= 0)
509 while (index < descr->nb_items)
511 if ((pos += descr->items[index].height) > y) break;
512 index++;
515 else
517 while (index > 0)
519 index--;
520 if ((pos -= descr->items[index].height) <= y) break;
524 else if (descr->style & LBS_MULTICOLUMN)
526 if (y >= descr->item_height * descr->page_size) return -1;
527 if (y >= 0) index += y / descr->item_height;
528 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
529 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
531 else
533 index += (y / descr->item_height);
535 if (index < 0) return 0;
536 if (index >= descr->nb_items) return -1;
537 return index;
541 /***********************************************************************
542 * LISTBOX_PaintItem
544 * Paint an item.
546 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
547 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
549 LB_ITEMDATA *item = NULL;
550 if (index < descr->nb_items) item = &descr->items[index];
552 if (IS_OWNERDRAW(descr))
554 DRAWITEMSTRUCT dis;
555 RECT r;
556 HRGN hrgn;
557 UINT id = GetWindowLongA( hwnd, GWL_ID );
559 if (!item)
561 if (action == ODA_FOCUS)
562 DrawFocusRect( hdc, rect );
563 else
564 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
565 return;
568 /* some programs mess with the clipping region when
569 drawing the item, *and* restore the previous region
570 after they are done, so a region has better to exist
571 else everything ends clipped */
572 GetClientRect(hwnd, &r);
573 hrgn = CreateRectRgnIndirect(&r);
574 SelectClipRgn( hdc, hrgn);
575 DeleteObject( hrgn );
577 dis.CtlType = ODT_LISTBOX;
578 dis.CtlID = id;
579 dis.hwndItem = hwnd;
580 dis.itemAction = action;
581 dis.hDC = hdc;
582 dis.itemID = index;
583 dis.itemState = 0;
584 if (item && item->selected) dis.itemState |= ODS_SELECTED;
585 if (!ignoreFocus && (descr->focus_item == index) &&
586 (descr->caret_on) &&
587 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
588 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
589 dis.itemData = item ? item->data : 0;
590 dis.rcItem = *rect;
591 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
592 hwnd, index, item ? debugstr_w(item->str) : "", action,
593 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
594 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
596 else
598 COLORREF oldText = 0, oldBk = 0;
600 if (action == ODA_FOCUS)
602 DrawFocusRect( hdc, rect );
603 return;
605 if (item && item->selected)
607 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
608 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
611 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
612 hwnd, index, item ? debugstr_w(item->str) : "", action,
613 rect->left, rect->top, rect->right, rect->bottom );
614 if (!item)
615 ExtTextOutW( hdc, rect->left + 1, rect->top,
616 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
617 else if (!(descr->style & LBS_USETABSTOPS))
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
620 strlenW(item->str), NULL );
621 else
623 /* Output empty string to paint background in the full width. */
624 ExtTextOutW( hdc, rect->left + 1, rect->top,
625 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
626 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
627 item->str, strlenW(item->str),
628 descr->nb_tabs, descr->tabs, 0);
630 if (item && item->selected)
632 SetBkColor( hdc, oldBk );
633 SetTextColor( hdc, oldText );
635 if (!ignoreFocus && (descr->focus_item == index) &&
636 (descr->caret_on) &&
637 (descr->in_focus)) DrawFocusRect( hdc, rect );
642 /***********************************************************************
643 * LISTBOX_SetRedraw
645 * Change the redraw flag.
647 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
649 if (on)
651 if (!(descr->style & LBS_NOREDRAW)) return;
652 descr->style &= ~LBS_NOREDRAW;
653 if (descr->style & LBS_DISPLAYCHANGED)
654 { /* page was changed while setredraw false, refresh automatically */
655 InvalidateRect(hwnd, NULL, TRUE);
656 if ((descr->top_item + descr->page_size) > descr->nb_items)
657 { /* reset top of page if less than number of items/page */
658 descr->top_item = descr->nb_items - descr->page_size;
659 if (descr->top_item < 0) descr->top_item = 0;
661 descr->style &= ~LBS_DISPLAYCHANGED;
663 LISTBOX_UpdateScroll( hwnd, descr );
665 else descr->style |= LBS_NOREDRAW;
669 /***********************************************************************
670 * LISTBOX_RepaintItem
672 * Repaint a single item synchronously.
674 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
675 UINT action )
677 HDC hdc;
678 RECT rect;
679 HFONT oldFont = 0;
680 HBRUSH hbrush, oldBrush = 0;
682 /* Do not repaint the item if the item is not visible */
683 if (!IsWindowVisible(hwnd)) return;
684 if (descr->style & LBS_NOREDRAW)
686 descr->style |= LBS_DISPLAYCHANGED;
687 return;
689 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
690 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
691 if (descr->font) oldFont = SelectObject( hdc, descr->font );
692 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
693 hdc, (LPARAM)hwnd );
694 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
695 if (!IsWindowEnabled(hwnd))
696 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
697 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
698 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
699 if (oldFont) SelectObject( hdc, oldFont );
700 if (oldBrush) SelectObject( hdc, oldBrush );
701 ReleaseDC( hwnd, hdc );
705 /***********************************************************************
706 * LISTBOX_InitStorage
708 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
710 LB_ITEMDATA *item;
712 nb_items += LB_ARRAY_GRANULARITY - 1;
713 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
714 if (descr->items)
715 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
716 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
717 nb_items * sizeof(LB_ITEMDATA) )))
719 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
720 return LB_ERRSPACE;
722 descr->items = item;
723 return LB_OKAY;
727 /***********************************************************************
728 * LISTBOX_SetTabStops
730 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
731 LPINT tabs, BOOL short_ints )
733 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
734 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
735 if (!(descr->nb_tabs = count))
737 descr->tabs = NULL;
738 return TRUE;
740 /* FIXME: count = 1 */
741 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
742 descr->nb_tabs * sizeof(INT) )))
743 return FALSE;
744 if (short_ints)
746 INT i;
747 LPINT16 p = (LPINT16)tabs;
749 TRACE("[%04x]: settabstops ", hwnd );
750 for (i = 0; i < descr->nb_tabs; i++) {
751 descr->tabs[i] = *p++<<1; /* FIXME */
752 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
754 if (TRACE_ON(listbox)) DPRINTF("\n");
756 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
757 /* FIXME: repaint the window? */
758 return TRUE;
762 /***********************************************************************
763 * LISTBOX_GetText
765 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
767 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
768 if (HAS_STRINGS(descr))
770 if (!lParam)
771 return strlenW(descr->items[index].str);
773 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
775 if(unicode)
777 LPWSTR buffer = (LPWSTR)lParam;
778 strcpyW( buffer, descr->items[index].str );
779 return strlenW(buffer);
781 else
783 LPSTR buffer = (LPSTR)lParam;
784 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
786 } else {
787 if (lParam)
788 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
789 return sizeof(DWORD);
794 /***********************************************************************
795 * LISTBOX_FindStringPos
797 * Find the nearest string located before a given string in sort order.
798 * If 'exact' is TRUE, return an error if we don't get an exact match.
800 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
801 BOOL exact )
803 INT index, min, max, res = -1;
805 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
806 min = 0;
807 max = descr->nb_items;
808 while (min != max)
810 index = (min + max) / 2;
811 if (HAS_STRINGS(descr))
812 res = lstrcmpiW( descr->items[index].str, str );
813 else
815 COMPAREITEMSTRUCT cis;
816 UINT id = GetWindowLongA( hwnd, GWL_ID );
818 cis.CtlType = ODT_LISTBOX;
819 cis.CtlID = id;
820 cis.hwndItem = hwnd;
821 cis.itemID1 = index;
822 cis.itemData1 = descr->items[index].data;
823 cis.itemID2 = -1;
824 cis.itemData2 = (DWORD)str;
825 cis.dwLocaleId = descr->locale;
826 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
828 if (!res) return index;
829 if (res > 0) max = index;
830 else min = index + 1;
832 return exact ? -1 : max;
836 /***********************************************************************
837 * LISTBOX_FindFileStrPos
839 * Find the nearest string located before a given string in directory
840 * sort order (i.e. first files, then directories, then drives).
842 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
844 INT min, max, res = -1;
846 if (!HAS_STRINGS(descr))
847 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
848 min = 0;
849 max = descr->nb_items;
850 while (min != max)
852 INT index = (min + max) / 2;
853 LPCWSTR p = descr->items[index].str;
854 if (*p == '[') /* drive or directory */
856 if (*str != '[') res = -1;
857 else if (p[1] == '-') /* drive */
859 if (str[1] == '-') res = str[2] - p[2];
860 else res = -1;
862 else /* directory */
864 if (str[1] == '-') res = 1;
865 else res = lstrcmpiW( str, p );
868 else /* filename */
870 if (*str == '[') res = 1;
871 else res = lstrcmpiW( str, p );
873 if (!res) return index;
874 if (res < 0) max = index;
875 else min = index + 1;
877 return max;
881 /***********************************************************************
882 * LISTBOX_FindString
884 * Find the item beginning with a given string.
886 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
887 LPCWSTR str, BOOL exact )
889 INT i;
890 LB_ITEMDATA *item;
892 if (start >= descr->nb_items) start = -1;
893 item = descr->items + start + 1;
894 if (HAS_STRINGS(descr))
896 if (!str || ! str[0] ) return LB_ERR;
897 if (exact)
899 for (i = start + 1; i < descr->nb_items; i++, item++)
900 if (!lstrcmpiW( str, item->str )) return i;
901 for (i = 0, item = descr->items; i <= start; i++, item++)
902 if (!lstrcmpiW( str, item->str )) return i;
904 else
906 /* Special case for drives and directories: ignore prefix */
907 #define CHECK_DRIVE(item) \
908 if ((item)->str[0] == '[') \
910 if (!strncmpiW( str, (item)->str+1, len )) return i; \
911 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
912 return i; \
915 INT len = strlenW(str);
916 for (i = start + 1; i < descr->nb_items; i++, item++)
918 if (!strncmpiW( str, item->str, len )) return i;
919 CHECK_DRIVE(item);
921 for (i = 0, item = descr->items; i <= start; i++, item++)
923 if (!strncmpiW( str, item->str, len )) return i;
924 CHECK_DRIVE(item);
926 #undef CHECK_DRIVE
929 else
931 if (exact && (descr->style & LBS_SORT))
932 /* If sorted, use a WM_COMPAREITEM binary search */
933 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
935 /* Otherwise use a linear search */
936 for (i = start + 1; i < descr->nb_items; i++, item++)
937 if (item->data == (DWORD)str) return i;
938 for (i = 0, item = descr->items; i <= start; i++, item++)
939 if (item->data == (DWORD)str) return i;
941 return LB_ERR;
945 /***********************************************************************
946 * LISTBOX_GetSelCount
948 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
950 INT i, count;
951 LB_ITEMDATA *item = descr->items;
953 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
954 for (i = count = 0; i < descr->nb_items; i++, item++)
955 if (item->selected) count++;
956 return count;
960 /***********************************************************************
961 * LISTBOX_GetSelItems16
963 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
965 INT i, count;
966 LB_ITEMDATA *item = descr->items;
968 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
969 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
970 if (item->selected) array[count++] = (INT16)i;
971 return count;
975 /***********************************************************************
976 * LISTBOX_GetSelItems
978 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
980 INT i, count;
981 LB_ITEMDATA *item = descr->items;
983 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
984 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
985 if (item->selected) array[count++] = i;
986 return count;
990 /***********************************************************************
991 * LISTBOX_Paint
993 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
995 INT i, col_pos = descr->page_size - 1;
996 RECT rect;
997 RECT focusRect = {-1, -1, -1, -1};
998 HFONT oldFont = 0;
999 HBRUSH hbrush, oldBrush = 0;
1001 if (descr->style & LBS_NOREDRAW) return 0;
1003 SetRect( &rect, 0, 0, descr->width, descr->height );
1004 if (descr->style & LBS_MULTICOLUMN)
1005 rect.right = rect.left + descr->column_width;
1006 else if (descr->horz_pos)
1008 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1009 rect.right += descr->horz_pos;
1012 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1013 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1014 hdc, (LPARAM)hwnd );
1015 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1016 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1018 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1019 (descr->in_focus))
1021 /* Special case for empty listbox: paint focus rect */
1022 rect.bottom = rect.top + descr->item_height;
1023 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1024 ODA_FOCUS, FALSE );
1025 rect.top = rect.bottom;
1028 /* Paint all the item, regarding the selection
1029 Focus state will be painted after */
1031 for (i = descr->top_item; i < descr->nb_items; i++)
1033 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1034 rect.bottom = rect.top + descr->item_height;
1035 else
1036 rect.bottom = rect.top + descr->items[i].height;
1038 if (i == descr->focus_item)
1040 /* keep the focus rect, to paint the focus item after */
1041 focusRect.left = rect.left;
1042 focusRect.right = rect.right;
1043 focusRect.top = rect.top;
1044 focusRect.bottom = rect.bottom;
1046 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1047 rect.top = rect.bottom;
1049 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1051 if (!IS_OWNERDRAW(descr))
1053 /* Clear the bottom of the column */
1054 if (rect.top < descr->height)
1056 rect.bottom = descr->height;
1057 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1058 &rect, NULL, 0, NULL );
1062 /* Go to the next column */
1063 rect.left += descr->column_width;
1064 rect.right += descr->column_width;
1065 rect.top = 0;
1066 col_pos = descr->page_size - 1;
1068 else
1070 col_pos--;
1071 if (rect.top >= descr->height) break;
1075 /* Paint the focus item now */
1076 if (focusRect.top != focusRect.bottom && descr->caret_on)
1077 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1079 if (!IS_OWNERDRAW(descr))
1081 /* Clear the remainder of the client area */
1082 if (rect.top < descr->height)
1084 rect.bottom = descr->height;
1085 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1086 &rect, NULL, 0, NULL );
1088 if (rect.right < descr->width)
1090 rect.left = rect.right;
1091 rect.right = descr->width;
1092 rect.top = 0;
1093 rect.bottom = descr->height;
1094 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1095 &rect, NULL, 0, NULL );
1098 if (oldFont) SelectObject( hdc, oldFont );
1099 if (oldBrush) SelectObject( hdc, oldBrush );
1100 return 0;
1104 /***********************************************************************
1105 * LISTBOX_InvalidateItems
1107 * Invalidate all items from a given item. If the specified item is not
1108 * visible, nothing happens.
1110 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1112 RECT rect;
1114 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1116 if (descr->style & LBS_NOREDRAW)
1118 descr->style |= LBS_DISPLAYCHANGED;
1119 return;
1121 rect.bottom = descr->height;
1122 InvalidateRect( hwnd, &rect, TRUE );
1123 if (descr->style & LBS_MULTICOLUMN)
1125 /* Repaint the other columns */
1126 rect.left = rect.right;
1127 rect.right = descr->width;
1128 rect.top = 0;
1129 InvalidateRect( hwnd, &rect, TRUE );
1135 /***********************************************************************
1136 * LISTBOX_GetItemHeight
1138 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1140 if (descr->style & LBS_OWNERDRAWVARIABLE)
1142 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1143 return descr->items[index].height;
1145 else return descr->item_height;
1149 /***********************************************************************
1150 * LISTBOX_SetItemHeight
1152 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1153 INT height, BOOL repaint )
1155 if (!height) height = 1;
1157 if (descr->style & LBS_OWNERDRAWVARIABLE)
1159 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1160 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1161 descr->items[index].height = height;
1162 LISTBOX_UpdateScroll( hwnd, descr );
1163 if (repaint)
1164 LISTBOX_InvalidateItems( hwnd, descr, index );
1166 else if (height != descr->item_height)
1168 TRACE("[%04x]: new height = %d\n", hwnd, height );
1169 descr->item_height = height;
1170 LISTBOX_UpdatePage( hwnd, descr );
1171 LISTBOX_UpdateScroll( hwnd, descr );
1172 if (repaint)
1173 InvalidateRect( hwnd, 0, TRUE );
1175 return LB_OKAY;
1179 /***********************************************************************
1180 * LISTBOX_SetHorizontalPos
1182 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1184 INT diff;
1186 if (pos > descr->horz_extent - descr->width)
1187 pos = descr->horz_extent - descr->width;
1188 if (pos < 0) pos = 0;
1189 if (!(diff = descr->horz_pos - pos)) return;
1190 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1191 descr->horz_pos = pos;
1192 LISTBOX_UpdateScroll( hwnd, descr );
1193 if (abs(diff) < descr->width)
1194 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1195 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1196 else
1197 InvalidateRect( hwnd, NULL, TRUE );
1201 /***********************************************************************
1202 * LISTBOX_SetHorizontalExtent
1204 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1205 INT extent )
1207 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1208 return LB_OKAY;
1209 if (extent <= 0) extent = 1;
1210 if (extent == descr->horz_extent) return LB_OKAY;
1211 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1212 descr->horz_extent = extent;
1213 if (descr->horz_pos > extent - descr->width)
1214 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1215 else
1216 LISTBOX_UpdateScroll( hwnd, descr );
1217 return LB_OKAY;
1221 /***********************************************************************
1222 * LISTBOX_SetColumnWidth
1224 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1226 if (width == descr->column_width) return LB_OKAY;
1227 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1228 descr->column_width = width;
1229 LISTBOX_UpdatePage( hwnd, descr );
1230 return LB_OKAY;
1234 /***********************************************************************
1235 * LISTBOX_SetFont
1237 * Returns the item height.
1239 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1241 HDC hdc;
1242 HFONT oldFont = 0;
1243 TEXTMETRICW tm;
1245 descr->font = font;
1247 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1249 ERR("unable to get DC.\n" );
1250 return 16;
1252 if (font) oldFont = SelectObject( hdc, font );
1253 GetTextMetricsW( hdc, &tm );
1254 if (oldFont) SelectObject( hdc, oldFont );
1255 ReleaseDC( hwnd, hdc );
1256 if (!IS_OWNERDRAW(descr))
1257 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1258 return tm.tmHeight ;
1262 /***********************************************************************
1263 * LISTBOX_MakeItemVisible
1265 * Make sure that a given item is partially or fully visible.
1267 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1268 BOOL fully )
1270 INT top;
1272 if (index <= descr->top_item) top = index;
1273 else if (descr->style & LBS_MULTICOLUMN)
1275 INT cols = descr->width;
1276 if (!fully) cols += descr->column_width - 1;
1277 if (cols >= descr->column_width) cols /= descr->column_width;
1278 else cols = 1;
1279 if (index < descr->top_item + (descr->page_size * cols)) return;
1280 top = index - descr->page_size * (cols - 1);
1282 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1284 INT height = fully ? descr->items[index].height : 1;
1285 for (top = index; top > descr->top_item; top--)
1286 if ((height += descr->items[top-1].height) > descr->height) break;
1288 else
1290 if (index < descr->top_item + descr->page_size) return;
1291 if (!fully && (index == descr->top_item + descr->page_size) &&
1292 (descr->height > (descr->page_size * descr->item_height))) return;
1293 top = index - descr->page_size + 1;
1295 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1298 /***********************************************************************
1299 * LISTBOX_SetCaretIndex
1301 * NOTES
1302 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1305 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1306 BOOL fully_visible )
1308 INT oldfocus = descr->focus_item;
1310 if (descr->style & LBS_NOSEL) return LB_ERR;
1311 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1312 if (index == oldfocus) return LB_OKAY;
1313 descr->focus_item = index;
1314 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1315 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1317 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1318 if (descr->caret_on && (descr->in_focus))
1319 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1321 return LB_OKAY;
1325 /***********************************************************************
1326 * LISTBOX_SelectItemRange
1328 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1330 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1331 INT last, BOOL on )
1333 INT i;
1335 /* A few sanity checks */
1337 if (descr->style & LBS_NOSEL) return LB_ERR;
1338 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1339 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1340 if (last == -1) last = descr->nb_items - 1;
1341 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1342 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1343 /* selected_item reflects last selected/unselected item on multiple sel */
1344 descr->selected_item = last;
1346 if (on) /* Turn selection on */
1348 for (i = first; i <= last; i++)
1350 if (descr->items[i].selected) continue;
1351 descr->items[i].selected = TRUE;
1352 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1354 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1356 else /* Turn selection off */
1358 for (i = first; i <= last; i++)
1360 if (!descr->items[i].selected) continue;
1361 descr->items[i].selected = FALSE;
1362 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1365 return LB_OKAY;
1368 /***********************************************************************
1369 * LISTBOX_SetSelection
1371 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1372 BOOL on, BOOL send_notify )
1374 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1376 if (descr->style & LBS_NOSEL) return LB_ERR;
1377 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1378 if (descr->style & LBS_MULTIPLESEL)
1380 if (index == -1) /* Select all items */
1381 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1382 else /* Only one item */
1383 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1385 else
1387 INT oldsel = descr->selected_item;
1388 if (index == oldsel) return LB_OKAY;
1389 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1390 if (index != -1) descr->items[index].selected = TRUE;
1391 descr->selected_item = index;
1392 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1393 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1394 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1395 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1396 else
1397 if( descr->lphc ) /* set selection change flag for parent combo */
1398 descr->lphc->wState |= CBF_SELCHANGE;
1400 return LB_OKAY;
1404 /***********************************************************************
1405 * LISTBOX_MoveCaret
1407 * Change the caret position and extend the selection to the new caret.
1409 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1410 BOOL fully_visible )
1412 INT oldfocus = descr->focus_item;
1414 if ((index < 0) || (index >= descr->nb_items))
1415 return;
1417 /* Important, repaint needs to be done in this order if
1418 you want to mimic Windows behavior:
1419 1. Remove the focus and paint the item
1420 2. Remove the selection and paint the item(s)
1421 3. Set the selection and repaint the item(s)
1422 4. Set the focus to 'index' and repaint the item */
1424 /* 1. remove the focus and repaint the item */
1425 descr->focus_item = -1;
1426 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1427 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1429 /* 2. then turn off the previous selection */
1430 /* 3. repaint the new selected item */
1431 if (descr->style & LBS_EXTENDEDSEL)
1433 if (descr->anchor_item != -1)
1435 INT first = min( index, descr->anchor_item );
1436 INT last = max( index, descr->anchor_item );
1437 if (first > 0)
1438 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1439 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1440 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1443 else if (!(descr->style & LBS_MULTIPLESEL))
1445 /* Set selection to new caret item */
1446 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1449 /* 4. repaint the new item with the focus */
1450 descr->focus_item = index;
1451 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1452 if (descr->caret_on && (descr->in_focus))
1453 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1457 /***********************************************************************
1458 * LISTBOX_InsertItem
1460 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1461 LPWSTR str, DWORD data )
1463 LB_ITEMDATA *item;
1464 INT max_items;
1465 INT oldfocus = descr->focus_item;
1467 if (index == -1) index = descr->nb_items;
1468 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1469 if (!descr->items) max_items = 0;
1470 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1471 if (descr->nb_items == max_items)
1473 /* We need to grow the array */
1474 max_items += LB_ARRAY_GRANULARITY;
1475 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1476 max_items * sizeof(LB_ITEMDATA) )))
1478 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1479 return LB_ERRSPACE;
1481 descr->items = item;
1484 /* Insert the item structure */
1486 item = &descr->items[index];
1487 if (index < descr->nb_items)
1488 RtlMoveMemory( item + 1, item,
1489 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1490 item->str = str;
1491 item->data = data;
1492 item->height = 0;
1493 item->selected = FALSE;
1494 descr->nb_items++;
1496 /* Get item height */
1498 if (descr->style & LBS_OWNERDRAWVARIABLE)
1500 MEASUREITEMSTRUCT mis;
1501 UINT id = GetWindowLongA( hwnd, GWL_ID );
1503 mis.CtlType = ODT_LISTBOX;
1504 mis.CtlID = id;
1505 mis.itemID = index;
1506 mis.itemData = descr->items[index].data;
1507 mis.itemHeight = descr->item_height;
1508 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1509 item->height = mis.itemHeight ? mis.itemHeight : 1;
1510 TRACE("[%04x]: measure item %d (%s) = %d\n",
1511 hwnd, index, str ? debugstr_w(str) : "", item->height );
1514 /* Repaint the items */
1516 LISTBOX_UpdateScroll( hwnd, descr );
1517 LISTBOX_InvalidateItems( hwnd, descr, index );
1519 /* Move selection and focused item */
1520 /* If listbox was empty, set focus to the first item */
1521 if (descr->nb_items == 1)
1522 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1523 /* single select don't change selection index in win31 */
1524 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1526 descr->selected_item++;
1527 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1529 else
1531 if (index <= descr->selected_item)
1533 descr->selected_item++;
1534 descr->focus_item = oldfocus; /* focus not changed */
1537 return LB_OKAY;
1541 /***********************************************************************
1542 * LISTBOX_InsertString
1544 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1545 LPCWSTR str )
1547 LPWSTR new_str = NULL;
1548 DWORD data = 0;
1549 LRESULT ret;
1551 if (HAS_STRINGS(descr))
1553 static const WCHAR empty_stringW[] = { 0 };
1554 if (!str) str = empty_stringW;
1555 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1557 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1558 return LB_ERRSPACE;
1560 strcpyW(new_str, str);
1562 else data = (DWORD)str;
1564 if (index == -1) index = descr->nb_items;
1565 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1567 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1568 return ret;
1571 TRACE("[%04x]: added item %d %s\n",
1572 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1573 return index;
1577 /***********************************************************************
1578 * LISTBOX_DeleteItem
1580 * Delete the content of an item. 'index' must be a valid index.
1582 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1584 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1585 * while Win95 sends it for all items with user data.
1586 * It's probably better to send it too often than not
1587 * often enough, so this is what we do here.
1589 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1591 DELETEITEMSTRUCT dis;
1592 UINT id = GetWindowLongA( hwnd, GWL_ID );
1594 dis.CtlType = ODT_LISTBOX;
1595 dis.CtlID = id;
1596 dis.itemID = index;
1597 dis.hwndItem = hwnd;
1598 dis.itemData = descr->items[index].data;
1599 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1601 if (HAS_STRINGS(descr) && descr->items[index].str)
1602 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1606 /***********************************************************************
1607 * LISTBOX_RemoveItem
1609 * Remove an item from the listbox and delete its content.
1611 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1613 LB_ITEMDATA *item;
1614 INT max_items;
1616 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1617 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1619 /* We need to invalidate the original rect instead of the updated one. */
1620 LISTBOX_InvalidateItems( hwnd, descr, index );
1622 LISTBOX_DeleteItem( hwnd, descr, index );
1624 /* Remove the item */
1626 item = &descr->items[index];
1627 if (index < descr->nb_items-1)
1628 RtlMoveMemory( item, item + 1,
1629 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1630 descr->nb_items--;
1631 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1633 /* Shrink the item array if possible */
1635 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1636 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1638 max_items -= LB_ARRAY_GRANULARITY;
1639 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1640 max_items * sizeof(LB_ITEMDATA) );
1641 if (item) descr->items = item;
1643 /* Repaint the items */
1645 LISTBOX_UpdateScroll( hwnd, descr );
1646 /* if we removed the scrollbar, reset the top of the list
1647 (correct for owner-drawn ???) */
1648 if (descr->nb_items == descr->page_size)
1649 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1651 /* Move selection and focused item */
1652 if (!IS_MULTISELECT(descr))
1654 if (index == descr->selected_item)
1655 descr->selected_item = -1;
1656 else if (index < descr->selected_item)
1658 descr->selected_item--;
1659 if (ISWIN31) /* win 31 do not change the selected item number */
1660 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1664 if (descr->focus_item >= descr->nb_items)
1666 descr->focus_item = descr->nb_items - 1;
1667 if (descr->focus_item < 0) descr->focus_item = 0;
1669 return LB_OKAY;
1673 /***********************************************************************
1674 * LISTBOX_ResetContent
1676 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1678 INT i;
1680 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1681 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1682 descr->nb_items = 0;
1683 descr->top_item = 0;
1684 descr->selected_item = -1;
1685 descr->focus_item = 0;
1686 descr->anchor_item = -1;
1687 descr->items = NULL;
1691 /***********************************************************************
1692 * LISTBOX_SetCount
1694 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1696 LRESULT ret;
1698 if (HAS_STRINGS(descr)) return LB_ERR;
1699 /* FIXME: this is far from optimal... */
1700 if (count > descr->nb_items)
1702 while (count > descr->nb_items)
1703 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1704 return ret;
1706 else if (count < descr->nb_items)
1708 while (count < descr->nb_items)
1709 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1710 return ret;
1712 return LB_OKAY;
1716 /***********************************************************************
1717 * LISTBOX_Directory
1719 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1720 LPCWSTR filespec, BOOL long_names )
1722 HANDLE handle;
1723 LRESULT ret = LB_OKAY;
1724 WIN32_FIND_DATAW entry;
1725 int pos;
1727 /* don't scan directory if we just want drives exclusively */
1728 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1729 /* scan directory */
1730 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1732 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1734 else
1738 WCHAR buffer[270];
1739 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1741 static const WCHAR bracketW[] = { ']',0 };
1742 static const WCHAR dotW[] = { '.',0 };
1743 if (!(attrib & DDL_DIRECTORY) ||
1744 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1745 buffer[0] = '[';
1746 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1747 else strcpyW( buffer + 1, entry.cAlternateFileName );
1748 strcatW(buffer, bracketW);
1750 else /* not a directory */
1752 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1753 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1755 if ((attrib & DDL_EXCLUSIVE) &&
1756 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1757 continue;
1758 #undef ATTRIBS
1759 if (long_names) strcpyW( buffer, entry.cFileName );
1760 else strcpyW( buffer, entry.cAlternateFileName );
1762 if (!long_names) CharLowerW( buffer );
1763 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1764 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1765 break;
1766 } while (FindNextFileW( handle, &entry ));
1767 FindClose( handle );
1771 /* scan drives */
1772 if ((ret >= 0) && (attrib & DDL_DRIVES))
1774 WCHAR buffer[] = {'[','-','a','-',']',0};
1775 WCHAR root[] = {'A',':','\\',0};
1776 int drive;
1777 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1779 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1780 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1781 break;
1784 return ret;
1788 /***********************************************************************
1789 * LISTBOX_HandleVScroll
1791 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1793 SCROLLINFO info;
1795 if (descr->style & LBS_MULTICOLUMN) return 0;
1796 switch(LOWORD(wParam))
1798 case SB_LINEUP:
1799 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1800 break;
1801 case SB_LINEDOWN:
1802 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1803 break;
1804 case SB_PAGEUP:
1805 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1806 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1807 break;
1808 case SB_PAGEDOWN:
1809 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1810 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1811 break;
1812 case SB_THUMBPOSITION:
1813 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1814 break;
1815 case SB_THUMBTRACK:
1816 info.cbSize = sizeof(info);
1817 info.fMask = SIF_TRACKPOS;
1818 GetScrollInfo( hwnd, SB_VERT, &info );
1819 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1820 break;
1821 case SB_TOP:
1822 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1823 break;
1824 case SB_BOTTOM:
1825 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1826 break;
1828 return 0;
1832 /***********************************************************************
1833 * LISTBOX_HandleHScroll
1835 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1837 SCROLLINFO info;
1838 INT page;
1840 if (descr->style & LBS_MULTICOLUMN)
1842 switch(LOWORD(wParam))
1844 case SB_LINELEFT:
1845 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1846 TRUE );
1847 break;
1848 case SB_LINERIGHT:
1849 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1850 TRUE );
1851 break;
1852 case SB_PAGELEFT:
1853 page = descr->width / descr->column_width;
1854 if (page < 1) page = 1;
1855 LISTBOX_SetTopItem( hwnd, descr,
1856 descr->top_item - page * descr->page_size, TRUE );
1857 break;
1858 case SB_PAGERIGHT:
1859 page = descr->width / descr->column_width;
1860 if (page < 1) page = 1;
1861 LISTBOX_SetTopItem( hwnd, descr,
1862 descr->top_item + page * descr->page_size, TRUE );
1863 break;
1864 case SB_THUMBPOSITION:
1865 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1866 TRUE );
1867 break;
1868 case SB_THUMBTRACK:
1869 info.cbSize = sizeof(info);
1870 info.fMask = SIF_TRACKPOS;
1871 GetScrollInfo( hwnd, SB_VERT, &info );
1872 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1873 TRUE );
1874 break;
1875 case SB_LEFT:
1876 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1877 break;
1878 case SB_RIGHT:
1879 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1880 break;
1883 else if (descr->horz_extent)
1885 switch(LOWORD(wParam))
1887 case SB_LINELEFT:
1888 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1889 break;
1890 case SB_LINERIGHT:
1891 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1892 break;
1893 case SB_PAGELEFT:
1894 LISTBOX_SetHorizontalPos( hwnd, descr,
1895 descr->horz_pos - descr->width );
1896 break;
1897 case SB_PAGERIGHT:
1898 LISTBOX_SetHorizontalPos( hwnd, descr,
1899 descr->horz_pos + descr->width );
1900 break;
1901 case SB_THUMBPOSITION:
1902 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1903 break;
1904 case SB_THUMBTRACK:
1905 info.cbSize = sizeof(info);
1906 info.fMask = SIF_TRACKPOS;
1907 GetScrollInfo( hwnd, SB_HORZ, &info );
1908 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1909 break;
1910 case SB_LEFT:
1911 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1912 break;
1913 case SB_RIGHT:
1914 LISTBOX_SetHorizontalPos( hwnd, descr,
1915 descr->horz_extent - descr->width );
1916 break;
1919 return 0;
1922 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1924 short gcWheelDelta = 0;
1925 UINT pulScrollLines = 3;
1927 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1929 gcWheelDelta -= (short) HIWORD(wParam);
1931 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1933 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1934 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1935 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1937 return 0;
1940 /***********************************************************************
1941 * LISTBOX_HandleLButtonDown
1943 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1944 WPARAM wParam, INT x, INT y )
1946 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1947 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1948 if (!descr->caret_on && (descr->in_focus)) return 0;
1950 if (!descr->in_focus)
1952 if( !descr->lphc ) SetFocus( hwnd );
1953 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1956 if (index == -1) return 0;
1958 if (descr->style & LBS_EXTENDEDSEL)
1960 /* we should perhaps make sure that all items are deselected
1961 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1962 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1963 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1966 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1967 if (wParam & MK_CONTROL)
1969 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1970 LISTBOX_SetSelection( hwnd, descr, index,
1971 !descr->items[index].selected,
1972 (descr->style & LBS_NOTIFY) != 0);
1974 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1976 else
1978 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1979 LISTBOX_SetSelection( hwnd, descr, index,
1980 (!(descr->style & LBS_MULTIPLESEL) ||
1981 !descr->items[index].selected),
1982 (descr->style & LBS_NOTIFY) != 0 );
1985 descr->captured = TRUE;
1986 SetCapture( hwnd );
1988 if (!descr->lphc)
1990 if (descr->style & LBS_NOTIFY )
1991 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1992 MAKELPARAM( x, y ) );
1993 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1995 POINT pt;
1997 pt.x = x;
1998 pt.y = y;
2000 if (DragDetect( hwnd, pt ))
2001 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2004 return 0;
2008 /*************************************************************************
2009 * LISTBOX_HandleLButtonDownCombo [Internal]
2011 * Process LButtonDown message for the ComboListBox
2013 nn * PARAMS
2014 * pWnd [I] The windows internal structure
2015 * pDescr [I] The ListBox internal structure
2016 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2017 * x [I] X Mouse Coordinate
2018 * y [I] Y Mouse Coordinate
2020 * RETURNS
2021 * 0 since we are processing the WM_LBUTTONDOWN Message
2023 * NOTES
2024 * This function is only to be used when a ListBox is a ComboListBox
2027 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2028 UINT msg, WPARAM wParam, INT x, INT y)
2030 RECT clientRect, screenRect;
2031 POINT mousePos;
2033 mousePos.x = x;
2034 mousePos.y = y;
2036 GetClientRect(hwnd, &clientRect);
2038 if(PtInRect(&clientRect, mousePos))
2040 /* MousePos is in client, resume normal processing */
2041 if (msg == WM_LBUTTONDOWN)
2043 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2044 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2046 else if (pDescr->style & LBS_NOTIFY)
2047 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2048 return 0;
2050 else
2052 POINT screenMousePos;
2053 HWND hWndOldCapture;
2055 /* Check the Non-Client Area */
2056 screenMousePos = mousePos;
2057 hWndOldCapture = GetCapture();
2058 ReleaseCapture();
2059 GetWindowRect(hwnd, &screenRect);
2060 ClientToScreen(hwnd, &screenMousePos);
2062 if(!PtInRect(&screenRect, screenMousePos))
2064 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2065 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2066 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2067 return 0;
2069 else
2071 /* Check to see the NC is a scrollbar */
2072 INT nHitTestType=0;
2073 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2074 /* Check Vertical scroll bar */
2075 if (style & WS_VSCROLL)
2077 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2078 if (PtInRect( &clientRect, mousePos ))
2080 nHitTestType = HTVSCROLL;
2083 /* Check horizontal scroll bar */
2084 if (style & WS_HSCROLL)
2086 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2087 if (PtInRect( &clientRect, mousePos ))
2089 nHitTestType = HTHSCROLL;
2092 /* Windows sends this message when a scrollbar is clicked
2095 if(nHitTestType != 0)
2097 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2098 MAKELONG(screenMousePos.x, screenMousePos.y));
2100 /* Resume the Capture after scrolling is complete
2102 if(hWndOldCapture != 0)
2104 SetCapture(hWndOldCapture);
2108 return 0;
2111 /***********************************************************************
2112 * LISTBOX_HandleLButtonUp
2114 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2116 if (LISTBOX_Timer != LB_TIMER_NONE)
2117 KillSystemTimer( hwnd, LB_TIMER_ID );
2118 LISTBOX_Timer = LB_TIMER_NONE;
2119 if (descr->captured)
2121 descr->captured = FALSE;
2122 if (GetCapture() == hwnd) ReleaseCapture();
2123 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2124 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2126 return 0;
2130 /***********************************************************************
2131 * LISTBOX_HandleTimer
2133 * Handle scrolling upon a timer event.
2134 * Return TRUE if scrolling should continue.
2136 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2137 INT index, TIMER_DIRECTION dir )
2139 switch(dir)
2141 case LB_TIMER_UP:
2142 if (descr->top_item) index = descr->top_item - 1;
2143 else index = 0;
2144 break;
2145 case LB_TIMER_LEFT:
2146 if (descr->top_item) index -= descr->page_size;
2147 break;
2148 case LB_TIMER_DOWN:
2149 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2150 if (index == descr->focus_item) index++;
2151 if (index >= descr->nb_items) index = descr->nb_items - 1;
2152 break;
2153 case LB_TIMER_RIGHT:
2154 if (index + descr->page_size < descr->nb_items)
2155 index += descr->page_size;
2156 break;
2157 case LB_TIMER_NONE:
2158 break;
2160 if (index == descr->focus_item) return FALSE;
2161 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2162 return TRUE;
2166 /***********************************************************************
2167 * LISTBOX_HandleSystemTimer
2169 * WM_SYSTIMER handler.
2171 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2173 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2175 KillSystemTimer( hwnd, LB_TIMER_ID );
2176 LISTBOX_Timer = LB_TIMER_NONE;
2178 return 0;
2182 /***********************************************************************
2183 * LISTBOX_HandleMouseMove
2185 * WM_MOUSEMOVE handler.
2187 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2188 INT x, INT y )
2190 INT index;
2191 TIMER_DIRECTION dir = LB_TIMER_NONE;
2193 if (!descr->captured) return;
2195 if (descr->style & LBS_MULTICOLUMN)
2197 if (y < 0) y = 0;
2198 else if (y >= descr->item_height * descr->page_size)
2199 y = descr->item_height * descr->page_size - 1;
2201 if (x < 0)
2203 dir = LB_TIMER_LEFT;
2204 x = 0;
2206 else if (x >= descr->width)
2208 dir = LB_TIMER_RIGHT;
2209 x = descr->width - 1;
2212 else
2214 if (y < 0) dir = LB_TIMER_UP; /* above */
2215 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2218 index = LISTBOX_GetItemFromPoint( descr, x, y );
2219 if (index == -1) index = descr->focus_item;
2220 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2222 /* Start/stop the system timer */
2224 if (dir != LB_TIMER_NONE)
2225 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2226 else if (LISTBOX_Timer != LB_TIMER_NONE)
2227 KillSystemTimer( hwnd, LB_TIMER_ID );
2228 LISTBOX_Timer = dir;
2232 /***********************************************************************
2233 * LISTBOX_HandleKeyDown
2235 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2237 INT caret = -1;
2238 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2239 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2240 bForceSelection = FALSE; /* only for single select list */
2242 if (descr->style & LBS_WANTKEYBOARDINPUT)
2244 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2245 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2246 hwnd );
2247 if (caret == -2) return 0;
2249 if (caret == -1) switch(wParam)
2251 case VK_LEFT:
2252 if (descr->style & LBS_MULTICOLUMN)
2254 bForceSelection = FALSE;
2255 if (descr->focus_item >= descr->page_size)
2256 caret = descr->focus_item - descr->page_size;
2257 break;
2259 /* fall through */
2260 case VK_UP:
2261 caret = descr->focus_item - 1;
2262 if (caret < 0) caret = 0;
2263 break;
2264 case VK_RIGHT:
2265 if (descr->style & LBS_MULTICOLUMN)
2267 bForceSelection = FALSE;
2268 if (descr->focus_item + descr->page_size < descr->nb_items)
2269 caret = descr->focus_item + descr->page_size;
2270 break;
2272 /* fall through */
2273 case VK_DOWN:
2274 caret = descr->focus_item + 1;
2275 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2276 break;
2278 case VK_PRIOR:
2279 if (descr->style & LBS_MULTICOLUMN)
2281 INT page = descr->width / descr->column_width;
2282 if (page < 1) page = 1;
2283 caret = descr->focus_item - (page * descr->page_size) + 1;
2285 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2286 if (caret < 0) caret = 0;
2287 break;
2288 case VK_NEXT:
2289 if (descr->style & LBS_MULTICOLUMN)
2291 INT page = descr->width / descr->column_width;
2292 if (page < 1) page = 1;
2293 caret = descr->focus_item + (page * descr->page_size) - 1;
2295 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2296 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2297 break;
2298 case VK_HOME:
2299 caret = 0;
2300 break;
2301 case VK_END:
2302 caret = descr->nb_items - 1;
2303 break;
2304 case VK_SPACE:
2305 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2306 else if (descr->style & LBS_MULTIPLESEL)
2308 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2309 !descr->items[descr->focus_item].selected,
2310 (descr->style & LBS_NOTIFY) != 0 );
2312 break;
2313 default:
2314 bForceSelection = FALSE;
2316 if (bForceSelection) /* focused item is used instead of key */
2317 caret = descr->focus_item;
2318 if (caret >= 0)
2320 if ((descr->style & LBS_EXTENDEDSEL) &&
2321 !(GetKeyState( VK_SHIFT ) & 0x8000))
2322 descr->anchor_item = caret;
2323 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2324 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2325 if (descr->style & LBS_NOTIFY)
2327 if( descr->lphc )
2329 /* make sure that combo parent doesn't hide us */
2330 descr->lphc->wState |= CBF_NOROLLUP;
2332 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2335 return 0;
2339 /***********************************************************************
2340 * LISTBOX_HandleChar
2342 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2344 INT caret = -1;
2345 WCHAR str[2];
2347 str[0] = charW;
2348 str[1] = '\0';
2350 if (descr->style & LBS_WANTKEYBOARDINPUT)
2352 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2353 MAKEWPARAM(charW, descr->focus_item),
2354 hwnd );
2355 if (caret == -2) return 0;
2357 if (caret == -1)
2358 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2359 if (caret != -1)
2361 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2362 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2363 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2364 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2365 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2367 return 0;
2371 /***********************************************************************
2372 * LISTBOX_Create
2374 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2376 LB_DESCR *descr;
2377 MEASUREITEMSTRUCT mis;
2378 RECT rect;
2380 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2381 return FALSE;
2383 GetClientRect( hwnd, &rect );
2384 descr->owner = GetParent( hwnd );
2385 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2386 descr->width = rect.right - rect.left;
2387 descr->height = rect.bottom - rect.top;
2388 descr->items = NULL;
2389 descr->nb_items = 0;
2390 descr->top_item = 0;
2391 descr->selected_item = -1;
2392 descr->focus_item = 0;
2393 descr->anchor_item = -1;
2394 descr->item_height = 1;
2395 descr->page_size = 1;
2396 descr->column_width = 150;
2397 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2398 descr->horz_pos = 0;
2399 descr->nb_tabs = 0;
2400 descr->tabs = NULL;
2401 descr->caret_on = lphc ? FALSE : TRUE;
2402 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2403 descr->in_focus = FALSE;
2404 descr->captured = FALSE;
2405 descr->font = 0;
2406 descr->locale = 0; /* FIXME */
2407 descr->lphc = lphc;
2409 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2411 /* Win95 document "List Box Differences" from MSDN:
2412 If a list box in a version 3.x application has either the
2413 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2414 horizontal and vertical scroll bars.
2416 descr->style |= WS_VSCROLL | WS_HSCROLL;
2419 if( lphc )
2421 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2422 hwnd, descr->owner, lphc->self );
2423 descr->owner = lphc->self;
2426 SetWindowLongA( hwnd, 0, (LONG)descr );
2428 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2430 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2431 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2432 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2433 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2435 if (descr->style & LBS_OWNERDRAWFIXED)
2437 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2439 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2440 descr->item_height = lphc->fixedOwnerDrawHeight;
2442 else
2444 UINT id = GetWindowLongA( hwnd, GWL_ID );
2445 mis.CtlType = ODT_LISTBOX;
2446 mis.CtlID = id;
2447 mis.itemID = -1;
2448 mis.itemWidth = 0;
2449 mis.itemData = 0;
2450 mis.itemHeight = descr->item_height;
2451 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2452 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2456 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2457 return TRUE;
2461 /***********************************************************************
2462 * LISTBOX_Destroy
2464 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2466 LISTBOX_ResetContent( hwnd, descr );
2467 SetWindowLongA( hwnd, 0, 0 );
2468 HeapFree( GetProcessHeap(), 0, descr );
2469 return TRUE;
2473 /***********************************************************************
2474 * ListBoxWndProc_common
2476 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2477 WPARAM wParam, LPARAM lParam, BOOL unicode )
2479 LRESULT ret;
2480 LB_DESCR *descr;
2482 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2484 if (msg == WM_CREATE)
2486 if (!LISTBOX_Create( hwnd, NULL ))
2487 return -1;
2488 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2489 return 0;
2491 /* Ignore all other messages before we get a WM_CREATE */
2492 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2493 DefWindowProcA( hwnd, msg, wParam, lParam );
2496 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2497 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2498 switch(msg)
2500 case LB_RESETCONTENT16:
2501 case LB_RESETCONTENT:
2502 LISTBOX_ResetContent( hwnd, descr );
2503 LISTBOX_UpdateScroll( hwnd, descr );
2504 InvalidateRect( hwnd, NULL, TRUE );
2505 return 0;
2507 case LB_ADDSTRING16:
2508 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2509 /* fall through */
2510 case LB_ADDSTRING:
2512 INT ret;
2513 LPWSTR textW;
2514 if(unicode || !HAS_STRINGS(descr))
2515 textW = (LPWSTR)lParam;
2516 else
2518 LPSTR textA = (LPSTR)lParam;
2519 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2520 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2521 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2523 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2524 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2525 if (!unicode && HAS_STRINGS(descr))
2526 HeapFree(GetProcessHeap(), 0, textW);
2527 return ret;
2530 case LB_INSERTSTRING16:
2531 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2532 wParam = (INT)(INT16)wParam;
2533 /* fall through */
2534 case LB_INSERTSTRING:
2536 INT ret;
2537 LPWSTR textW;
2538 if(unicode || !HAS_STRINGS(descr))
2539 textW = (LPWSTR)lParam;
2540 else
2542 LPSTR textA = (LPSTR)lParam;
2543 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2544 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2545 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2547 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2548 if(!unicode && HAS_STRINGS(descr))
2549 HeapFree(GetProcessHeap(), 0, textW);
2550 return ret;
2553 case LB_ADDFILE16:
2554 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2555 /* fall through */
2556 case LB_ADDFILE:
2558 INT ret;
2559 LPWSTR textW;
2560 if(unicode || !HAS_STRINGS(descr))
2561 textW = (LPWSTR)lParam;
2562 else
2564 LPSTR textA = (LPSTR)lParam;
2565 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2566 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2567 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2569 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2570 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2571 if(!unicode && HAS_STRINGS(descr))
2572 HeapFree(GetProcessHeap(), 0, textW);
2573 return ret;
2576 case LB_DELETESTRING16:
2577 case LB_DELETESTRING:
2578 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2579 return descr->nb_items;
2580 else
2581 return LB_ERR;
2583 case LB_GETITEMDATA16:
2584 case LB_GETITEMDATA:
2585 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2586 return LB_ERR;
2587 return descr->items[wParam].data;
2589 case LB_SETITEMDATA16:
2590 case LB_SETITEMDATA:
2591 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2592 return LB_ERR;
2593 descr->items[wParam].data = (DWORD)lParam;
2594 return LB_OKAY;
2596 case LB_GETCOUNT16:
2597 case LB_GETCOUNT:
2598 return descr->nb_items;
2600 case LB_GETTEXT16:
2601 lParam = (LPARAM)MapSL(lParam);
2602 /* fall through */
2603 case LB_GETTEXT:
2604 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2606 case LB_GETTEXTLEN16:
2607 /* fall through */
2608 case LB_GETTEXTLEN:
2609 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2610 return LB_ERR;
2611 return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2612 : sizeof(DWORD));
2614 case LB_GETCURSEL16:
2615 case LB_GETCURSEL:
2616 if (descr->nb_items==0)
2617 return LB_ERR;
2618 if (!IS_MULTISELECT(descr))
2619 return descr->selected_item;
2620 /* else */
2621 if (descr->selected_item!=-1)
2622 return descr->selected_item;
2623 /* else */
2624 return descr->focus_item;
2625 /* otherwise, if the user tries to move the selection with the */
2626 /* arrow keys, we will give the application something to choke on */
2627 case LB_GETTOPINDEX16:
2628 case LB_GETTOPINDEX:
2629 return descr->top_item;
2631 case LB_GETITEMHEIGHT16:
2632 case LB_GETITEMHEIGHT:
2633 return LISTBOX_GetItemHeight( descr, wParam );
2635 case LB_SETITEMHEIGHT16:
2636 lParam = LOWORD(lParam);
2637 /* fall through */
2638 case LB_SETITEMHEIGHT:
2639 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2641 case LB_ITEMFROMPOINT:
2643 POINT pt;
2644 RECT rect;
2646 pt.x = LOWORD(lParam);
2647 pt.y = HIWORD(lParam);
2648 rect.left = 0;
2649 rect.top = 0;
2650 rect.right = descr->width;
2651 rect.bottom = descr->height;
2653 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2654 !PtInRect( &rect, pt ) );
2657 case LB_SETCARETINDEX16:
2658 case LB_SETCARETINDEX:
2659 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2660 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2661 return LB_ERR;
2662 else if (ISWIN31)
2663 return wParam;
2664 else
2665 return LB_OKAY;
2667 case LB_GETCARETINDEX16:
2668 case LB_GETCARETINDEX:
2669 return descr->focus_item;
2671 case LB_SETTOPINDEX16:
2672 case LB_SETTOPINDEX:
2673 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2675 case LB_SETCOLUMNWIDTH16:
2676 case LB_SETCOLUMNWIDTH:
2677 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2679 case LB_GETITEMRECT16:
2681 RECT rect;
2682 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2683 CONV_RECT32TO16( &rect, MapSL(lParam) );
2685 return ret;
2687 case LB_GETITEMRECT:
2688 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2690 case LB_FINDSTRING16:
2691 wParam = (INT)(INT16)wParam;
2692 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2693 /* fall through */
2694 case LB_FINDSTRING:
2696 INT ret;
2697 LPWSTR textW;
2698 if(unicode || !HAS_STRINGS(descr))
2699 textW = (LPWSTR)lParam;
2700 else
2702 LPSTR textA = (LPSTR)lParam;
2703 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2704 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2705 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2707 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2708 if(!unicode && HAS_STRINGS(descr))
2709 HeapFree(GetProcessHeap(), 0, textW);
2710 return ret;
2713 case LB_FINDSTRINGEXACT16:
2714 wParam = (INT)(INT16)wParam;
2715 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2716 /* fall through */
2717 case LB_FINDSTRINGEXACT:
2719 INT ret;
2720 LPWSTR textW;
2721 if(unicode || !HAS_STRINGS(descr))
2722 textW = (LPWSTR)lParam;
2723 else
2725 LPSTR textA = (LPSTR)lParam;
2726 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2727 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2728 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2730 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2731 if(!unicode && HAS_STRINGS(descr))
2732 HeapFree(GetProcessHeap(), 0, textW);
2733 return ret;
2736 case LB_SELECTSTRING16:
2737 wParam = (INT)(INT16)wParam;
2738 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2739 /* fall through */
2740 case LB_SELECTSTRING:
2742 INT index;
2743 LPWSTR textW;
2745 if(HAS_STRINGS(descr))
2746 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2747 debugstr_a((LPSTR)lParam));
2748 if(unicode || !HAS_STRINGS(descr))
2749 textW = (LPWSTR)lParam;
2750 else
2752 LPSTR textA = (LPSTR)lParam;
2753 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2754 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2755 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2757 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2758 if(!unicode && HAS_STRINGS(descr))
2759 HeapFree(GetProcessHeap(), 0, textW);
2760 if (index != LB_ERR)
2762 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2763 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2765 return index;
2768 case LB_GETSEL16:
2769 wParam = (INT)(INT16)wParam;
2770 /* fall through */
2771 case LB_GETSEL:
2772 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2773 return LB_ERR;
2774 return descr->items[wParam].selected;
2776 case LB_SETSEL16:
2777 lParam = (INT)(INT16)lParam;
2778 /* fall through */
2779 case LB_SETSEL:
2780 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2782 case LB_SETCURSEL16:
2783 wParam = (INT)(INT16)wParam;
2784 /* fall through */
2785 case LB_SETCURSEL:
2786 if (IS_MULTISELECT(descr)) return LB_ERR;
2787 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2788 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2790 case LB_GETSELCOUNT16:
2791 case LB_GETSELCOUNT:
2792 return LISTBOX_GetSelCount( descr );
2794 case LB_GETSELITEMS16:
2795 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2797 case LB_GETSELITEMS:
2798 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2800 case LB_SELITEMRANGE16:
2801 case LB_SELITEMRANGE:
2802 if (LOWORD(lParam) <= HIWORD(lParam))
2803 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2804 HIWORD(lParam), wParam );
2805 else
2806 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2807 LOWORD(lParam), wParam );
2809 case LB_SELITEMRANGEEX16:
2810 case LB_SELITEMRANGEEX:
2811 if ((INT)lParam >= (INT)wParam)
2812 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2813 else
2814 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2816 case LB_GETHORIZONTALEXTENT16:
2817 case LB_GETHORIZONTALEXTENT:
2818 return descr->horz_extent;
2820 case LB_SETHORIZONTALEXTENT16:
2821 case LB_SETHORIZONTALEXTENT:
2822 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2824 case LB_GETANCHORINDEX16:
2825 case LB_GETANCHORINDEX:
2826 return descr->anchor_item;
2828 case LB_SETANCHORINDEX16:
2829 wParam = (INT)(INT16)wParam;
2830 /* fall through */
2831 case LB_SETANCHORINDEX:
2832 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2833 return LB_ERR;
2834 descr->anchor_item = (INT)wParam;
2835 return LB_OKAY;
2837 case LB_DIR16:
2838 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2839 * be set automatically (this is different in Win32) */
2840 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2841 lParam = (LPARAM)MapSL(lParam);
2842 /* fall through */
2843 case LB_DIR:
2845 INT ret;
2846 LPWSTR textW;
2847 if(unicode)
2848 textW = (LPWSTR)lParam;
2849 else
2851 LPSTR textA = (LPSTR)lParam;
2852 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2853 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2854 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2856 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2857 if(!unicode)
2858 HeapFree(GetProcessHeap(), 0, textW);
2859 return ret;
2862 case LB_GETLOCALE:
2863 return descr->locale;
2865 case LB_SETLOCALE:
2866 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2867 return LB_OKAY;
2869 case LB_INITSTORAGE:
2870 return LISTBOX_InitStorage( hwnd, descr, wParam );
2872 case LB_SETCOUNT:
2873 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2875 case LB_SETTABSTOPS16:
2876 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2878 case LB_SETTABSTOPS:
2879 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2881 case LB_CARETON16:
2882 case LB_CARETON:
2883 if (descr->caret_on)
2884 return LB_OKAY;
2885 descr->caret_on = TRUE;
2886 if ((descr->focus_item != -1) && (descr->in_focus))
2887 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2888 return LB_OKAY;
2890 case LB_CARETOFF16:
2891 case LB_CARETOFF:
2892 if (!descr->caret_on)
2893 return LB_OKAY;
2894 descr->caret_on = FALSE;
2895 if ((descr->focus_item != -1) && (descr->in_focus))
2896 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2897 return LB_OKAY;
2899 case WM_DESTROY:
2900 return LISTBOX_Destroy( hwnd, descr );
2902 case WM_ENABLE:
2903 InvalidateRect( hwnd, NULL, TRUE );
2904 return 0;
2906 case WM_SETREDRAW:
2907 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2908 return 0;
2910 case WM_GETDLGCODE:
2911 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2913 case WM_PAINT:
2915 PAINTSTRUCT ps;
2916 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2917 ret = LISTBOX_Paint( hwnd, descr, hdc );
2918 if( !wParam ) EndPaint( hwnd, &ps );
2920 return ret;
2921 case WM_SIZE:
2922 LISTBOX_UpdateSize( hwnd, descr );
2923 return 0;
2924 case WM_GETFONT:
2925 return descr->font;
2926 case WM_SETFONT:
2927 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2928 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2929 return 0;
2930 case WM_SETFOCUS:
2931 descr->in_focus = TRUE;
2932 descr->caret_on = TRUE;
2933 if (descr->focus_item != -1)
2934 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2935 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2936 return 0;
2937 case WM_KILLFOCUS:
2938 descr->in_focus = FALSE;
2939 if ((descr->focus_item != -1) && descr->caret_on)
2940 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2941 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2942 return 0;
2943 case WM_HSCROLL:
2944 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2945 case WM_VSCROLL:
2946 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2947 case WM_MOUSEWHEEL:
2948 if (wParam & (MK_SHIFT | MK_CONTROL))
2949 return DefWindowProcW( hwnd, msg, wParam, lParam );
2950 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2951 case WM_LBUTTONDOWN:
2952 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2953 (INT16)LOWORD(lParam),
2954 (INT16)HIWORD(lParam) );
2955 case WM_LBUTTONDBLCLK:
2956 if (descr->style & LBS_NOTIFY)
2957 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2958 return 0;
2959 case WM_MOUSEMOVE:
2960 if (GetCapture() == hwnd)
2961 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2962 (INT16)HIWORD(lParam) );
2963 return 0;
2964 case WM_LBUTTONUP:
2965 return LISTBOX_HandleLButtonUp( hwnd, descr );
2966 case WM_KEYDOWN:
2967 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2968 case WM_CHAR:
2970 WCHAR charW;
2971 if(unicode)
2972 charW = (WCHAR)wParam;
2973 else
2975 CHAR charA = (CHAR)wParam;
2976 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2978 return LISTBOX_HandleChar( hwnd, descr, charW );
2980 case WM_SYSTIMER:
2981 return LISTBOX_HandleSystemTimer( hwnd, descr );
2982 case WM_ERASEBKGND:
2983 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2985 RECT rect;
2986 HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2987 wParam, (LPARAM)hwnd );
2988 TRACE("hbrush = %04x\n", hbrush);
2989 if(!hbrush)
2990 hbrush = GetSysColorBrush(COLOR_WINDOW);
2991 if(hbrush)
2993 GetClientRect(hwnd, &rect);
2994 FillRect((HDC)wParam, &rect, hbrush);
2997 return 1;
2998 case WM_DROPFILES:
2999 if( !descr->lphc )
3000 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3001 SendMessageA( descr->owner, msg, wParam, lParam );
3002 break;
3004 case WM_DROPOBJECT:
3005 case WM_QUERYDROPOBJECT:
3006 case WM_DRAGSELECT:
3007 case WM_DRAGMOVE:
3008 if( !descr->lphc )
3010 LPDRAGINFO16 dragInfo = MapSL( lParam );
3011 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3012 dragInfo->pt.y );
3013 return SendMessage16( descr->owner, msg, wParam, lParam );
3015 break;
3017 default:
3018 if ((msg >= WM_USER) && (msg < 0xc000))
3019 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3020 hwnd, msg, wParam, lParam );
3021 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3022 DefWindowProcA( hwnd, msg, wParam, lParam );
3024 return 0;
3027 /***********************************************************************
3028 * ListBoxWndProcA
3030 * This is just a wrapper for the real wndproc, it only does window locking
3031 * and unlocking.
3033 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3035 if (!IsWindow(hwnd)) return 0;
3036 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3039 /***********************************************************************
3040 * ListBoxWndProcW
3042 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3044 if (!IsWindow(hwnd)) return 0;
3045 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3048 /***********************************************************************
3049 * ComboLBWndProc_common
3051 * The real combo listbox wndproc
3053 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3054 WPARAM wParam, LPARAM lParam, BOOL unicode )
3056 LRESULT lRet = 0;
3057 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
3059 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3060 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3062 if( descr || msg == WM_CREATE )
3064 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3066 switch( msg )
3068 case WM_CREATE:
3070 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3071 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3072 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3073 return LISTBOX_Create( hwnd, lphc );
3075 case WM_MOUSEMOVE:
3076 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3077 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3079 POINT mousePos;
3080 BOOL captured;
3081 RECT clientRect;
3083 mousePos.x = (INT16)LOWORD(lParam);
3084 mousePos.y = (INT16)HIWORD(lParam);
3087 * If we are in a dropdown combobox, we simulate that
3088 * the mouse is captured to show the tracking of the item.
3090 GetClientRect(hwnd, &clientRect);
3092 if (PtInRect( &clientRect, mousePos ))
3094 captured = descr->captured;
3095 descr->captured = TRUE;
3097 LISTBOX_HandleMouseMove( hwnd, descr,
3098 mousePos.x, mousePos.y);
3100 descr->captured = captured;
3103 else
3105 LISTBOX_HandleMouseMove( hwnd, descr,
3106 mousePos.x, mousePos.y);
3109 return 0;
3112 else
3115 * If we are in Win3.1 look, go with the default behavior.
3117 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3118 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3120 case WM_LBUTTONUP:
3121 if (TWEAK_WineLook > WIN31_LOOK)
3123 POINT mousePos;
3124 RECT clientRect;
3127 * If the mouse button "up" is not in the listbox,
3128 * we make sure there is no selection by re-selecting the
3129 * item that was selected when the listbox was made visible.
3131 mousePos.x = (INT16)LOWORD(lParam);
3132 mousePos.y = (INT16)HIWORD(lParam);
3134 GetClientRect(hwnd, &clientRect);
3137 * When the user clicks outside the combobox and the focus
3138 * is lost, the owning combobox will send a fake buttonup with
3139 * 0xFFFFFFF as the mouse location, we must also revert the
3140 * selection to the original selection.
3142 if ( (lParam == (LPARAM)-1) ||
3143 (!PtInRect( &clientRect, mousePos )) )
3145 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3148 return LISTBOX_HandleLButtonUp( hwnd, descr );
3149 case WM_LBUTTONDBLCLK:
3150 case WM_LBUTTONDOWN:
3151 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3152 (INT16)LOWORD(lParam),
3153 (INT16)HIWORD(lParam) );
3154 case WM_NCACTIVATE:
3155 return FALSE;
3156 case WM_KEYDOWN:
3157 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3159 /* for some reason(?) Windows makes it possible to
3160 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3162 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3163 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3164 && (wParam == VK_DOWN || wParam == VK_UP)) )
3166 COMBO_FlipListbox( lphc, FALSE, FALSE );
3167 return 0;
3170 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3172 case LB_SETCURSEL16:
3173 case LB_SETCURSEL:
3174 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3175 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3176 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3177 return lRet;
3178 case WM_NCDESTROY:
3179 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3180 lphc->hWndLBox = 0;
3181 /* fall through */
3183 default:
3184 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3185 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3188 lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3189 DefWindowProcA( hwnd, msg, wParam, lParam );
3191 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3193 return lRet;
3196 /***********************************************************************
3197 * ComboLBWndProcA
3199 * NOTE: in Windows, winproc address of the ComboLBox is the same
3200 * as that of the Listbox.
3202 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3204 if (!IsWindow(hwnd)) return 0;
3205 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3208 /***********************************************************************
3209 * ComboLBWndProcW
3211 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3213 if (!IsWindow(hwnd)) return 0;
3214 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );