Added definition for PeekNamedPipe
[wine/multimedia.git] / controls / listbox.c
blob961b1742b2c4186033765665b5d353218ebdf5db
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 "wownt32.h"
29 #include "wine/unicode.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "spy.h"
33 #include "user.h"
34 #include "controls.h"
35 #include "wine/debug.h"
36 #include "win.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
39 WINE_DECLARE_DEBUG_CHANNEL(combo);
41 /* Unimplemented yet:
42 * - LBS_USETABSTOPS
43 * - Locale handling
45 * Probably needs improvement:
46 * - LBS_NOSEL
49 /* Items array granularity */
50 #define LB_ARRAY_GRANULARITY 16
52 /* Scrolling timeout in ms */
53 #define LB_SCROLL_TIMEOUT 50
55 /* Listbox system timer id */
56 #define LB_TIMER_ID 2
58 /* flag listbox changed while setredraw false - internal style */
59 #define LBS_DISPLAYCHANGED 0x80000000
61 /* Item structure */
62 typedef struct
64 LPWSTR str; /* Item text */
65 BOOL selected; /* Is item selected? */
66 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
67 DWORD data; /* User data */
68 } LB_ITEMDATA;
70 /* Listbox structure */
71 typedef struct
73 HWND owner; /* Owner window to send notifications to */
74 UINT style; /* Window style */
75 INT width; /* Window width */
76 INT height; /* Window height */
77 LB_ITEMDATA *items; /* Array of items */
78 INT nb_items; /* Number of items */
79 INT top_item; /* Top visible item */
80 INT selected_item; /* Selected item */
81 INT focus_item; /* Item that has the focus */
82 INT anchor_item; /* Anchor item for extended selection */
83 INT item_height; /* Default item height */
84 INT page_size; /* Items per listbox page */
85 INT column_width; /* Column width for multi-column listboxes */
86 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
87 INT horz_pos; /* Horizontal position */
88 INT nb_tabs; /* Number of tabs in array */
89 INT *tabs; /* Array of tabs */
90 BOOL caret_on; /* Is caret on? */
91 BOOL captured; /* Is mouse captured? */
92 BOOL in_focus;
93 HFONT font; /* Current font */
94 LCID locale; /* Current locale for string comparisons */
95 LPHEADCOMBO lphc; /* ComboLBox */
96 } LB_DESCR;
99 #define IS_OWNERDRAW(descr) \
100 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
102 #define HAS_STRINGS(descr) \
103 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
106 #define IS_MULTISELECT(descr) \
107 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
109 #define SEND_NOTIFICATION(hwnd,descr,code) \
110 (SendMessageW( (descr)->owner, WM_COMMAND, \
111 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
113 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
115 /* Current timer status */
116 typedef enum
118 LB_TIMER_NONE,
119 LB_TIMER_UP,
120 LB_TIMER_LEFT,
121 LB_TIMER_DOWN,
122 LB_TIMER_RIGHT
123 } TIMER_DIRECTION;
125 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
127 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
128 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
129 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
134 /*********************************************************************
135 * listbox class descriptor
137 const struct builtin_class_descr LISTBOX_builtin_class =
139 "ListBox", /* name */
140 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
141 ListBoxWndProcA, /* procA */
142 ListBoxWndProcW, /* procW */
143 sizeof(LB_DESCR *), /* extra */
144 IDC_ARROWA, /* cursor */
145 0 /* brush */
149 /*********************************************************************
150 * combolbox class descriptor
152 const struct builtin_class_descr COMBOLBOX_builtin_class =
154 "ComboLBox", /* name */
155 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
156 ComboLBWndProcA, /* procA */
157 ComboLBWndProcW, /* procW */
158 sizeof(LB_DESCR *), /* extra */
159 IDC_ARROWA, /* cursor */
160 0 /* brush */
164 /* check whether app is a Win 3.1 app */
165 inline static BOOL is_old_app( HWND hwnd )
167 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
171 /***********************************************************************
172 * LISTBOX_Dump
174 void LISTBOX_Dump( HWND hwnd )
176 INT i;
177 LB_ITEMDATA *item;
178 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
180 TRACE( "Listbox:\n" );
181 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
182 hwnd, (UINT)descr, descr->nb_items,
183 descr->top_item );
184 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
186 TRACE( "%4d: %-40s %d %08lx %3d\n",
187 i, debugstr_w(item->str), item->selected, item->data, item->height );
192 /***********************************************************************
193 * LISTBOX_GetCurrentPageSize
195 * Return the current page size
197 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
199 INT i, height;
200 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
201 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
203 if ((height += descr->items[i].height) > descr->height) break;
205 if (i == descr->top_item) return 1;
206 else return i - descr->top_item;
210 /***********************************************************************
211 * LISTBOX_GetMaxTopIndex
213 * Return the maximum possible index for the top of the listbox.
215 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
217 INT max, page;
219 if (descr->style & LBS_OWNERDRAWVARIABLE)
221 page = descr->height;
222 for (max = descr->nb_items - 1; max >= 0; max--)
223 if ((page -= descr->items[max].height) < 0) break;
224 if (max < descr->nb_items - 1) max++;
226 else if (descr->style & LBS_MULTICOLUMN)
228 if ((page = descr->width / descr->column_width) < 1) page = 1;
229 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
230 max = (max - page) * descr->page_size;
232 else
234 max = descr->nb_items - descr->page_size;
236 if (max < 0) max = 0;
237 return max;
241 /***********************************************************************
242 * LISTBOX_UpdateScroll
244 * Update the scrollbars. Should be called whenever the content
245 * of the listbox changes.
247 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
249 SCROLLINFO info;
251 /* Check the listbox scroll bar flags individually before we call
252 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
253 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
254 scroll bar when we do not need one.
255 if (!(descr->style & WS_VSCROLL)) return;
258 /* It is important that we check descr->style, and not wnd->dwStyle,
259 for WS_VSCROLL, as the former is exactly the one passed in
260 argument to CreateWindow.
261 In Windows (and from now on in Wine :) a listbox created
262 with such a style (no WS_SCROLL) does not update
263 the scrollbar with listbox-related data, thus letting
264 the programmer use it for his/her own purposes. */
266 if (descr->style & LBS_NOREDRAW) return;
267 info.cbSize = sizeof(info);
269 if (descr->style & LBS_MULTICOLUMN)
271 info.nMin = 0;
272 info.nMax = (descr->nb_items - 1) / descr->page_size;
273 info.nPos = descr->top_item / descr->page_size;
274 info.nPage = descr->width / descr->column_width;
275 if (info.nPage < 1) info.nPage = 1;
276 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
277 if (descr->style & LBS_DISABLENOSCROLL)
278 info.fMask |= SIF_DISABLENOSCROLL;
279 if (descr->style & WS_HSCROLL)
280 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
281 info.nMax = 0;
282 info.fMask = SIF_RANGE;
283 if (descr->style & WS_VSCROLL)
284 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
286 else
288 info.nMin = 0;
289 info.nMax = descr->nb_items - 1;
290 info.nPos = descr->top_item;
291 info.nPage = LISTBOX_GetCurrentPageSize( descr );
292 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
293 if (descr->style & LBS_DISABLENOSCROLL)
294 info.fMask |= SIF_DISABLENOSCROLL;
295 if (descr->style & WS_VSCROLL)
296 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
298 if (descr->horz_extent)
300 info.nMin = 0;
301 info.nMax = descr->horz_extent - 1;
302 info.nPos = descr->horz_pos;
303 info.nPage = descr->width;
304 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
305 if (descr->style & LBS_DISABLENOSCROLL)
306 info.fMask |= SIF_DISABLENOSCROLL;
307 if (descr->style & WS_HSCROLL)
308 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
314 /***********************************************************************
315 * LISTBOX_SetTopItem
317 * Set the top item of the listbox, scrolling up or down if necessary.
319 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
320 BOOL scroll )
322 INT max = LISTBOX_GetMaxTopIndex( descr );
323 if (index > max) index = max;
324 if (index < 0) index = 0;
325 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
326 if (descr->top_item == index) return LB_OKAY;
327 if (descr->style & LBS_MULTICOLUMN)
329 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
330 if (scroll && (abs(diff) < descr->width))
331 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
332 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
334 else
335 scroll = FALSE;
337 else if (scroll)
339 INT diff;
340 if (descr->style & LBS_OWNERDRAWVARIABLE)
342 INT i;
343 diff = 0;
344 if (index > descr->top_item)
346 for (i = index - 1; i >= descr->top_item; i--)
347 diff -= descr->items[i].height;
349 else
351 for (i = index; i < descr->top_item; i++)
352 diff += descr->items[i].height;
355 else
356 diff = (descr->top_item - index) * descr->item_height;
358 if (abs(diff) < descr->height)
359 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
360 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
361 else
362 scroll = FALSE;
364 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
365 descr->top_item = index;
366 LISTBOX_UpdateScroll( hwnd, descr );
367 return LB_OKAY;
371 /***********************************************************************
372 * LISTBOX_UpdatePage
374 * Update the page size. Should be called when the size of
375 * the client area or the item height changes.
377 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
379 INT page_size;
381 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
382 page_size = 1;
383 if (page_size == descr->page_size) return;
384 descr->page_size = page_size;
385 if (descr->style & LBS_MULTICOLUMN)
386 InvalidateRect( hwnd, NULL, TRUE );
387 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
391 /***********************************************************************
392 * LISTBOX_UpdateSize
394 * Update the size of the listbox. Should be called when the size of
395 * the client area changes.
397 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
399 RECT rect;
401 GetClientRect( hwnd, &rect );
402 descr->width = rect.right - rect.left;
403 descr->height = rect.bottom - rect.top;
404 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
406 INT remaining;
407 RECT rect;
409 GetWindowRect( hwnd, &rect );
410 if(descr->item_height != 0)
411 remaining = descr->height % descr->item_height;
412 else
413 remaining = 0;
414 if ((descr->height > descr->item_height) && remaining)
416 if (is_old_app(hwnd))
417 { /* give a margin for error to 16 bits programs - if we need
418 less than the height of the nonclient area, round to the
419 *next* number of items */
420 int ncheight = rect.bottom - rect.top - descr->height;
421 if ((descr->item_height - remaining) <= ncheight)
422 remaining = remaining - descr->item_height;
424 TRACE("[%04x]: changing height %d -> %d\n",
425 hwnd, descr->height, descr->height - remaining );
426 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
427 rect.bottom - rect.top - remaining,
428 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
429 return;
432 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
433 LISTBOX_UpdatePage( hwnd, descr );
434 LISTBOX_UpdateScroll( hwnd, descr );
436 /* Invalidate the focused item so it will be repainted correctly */
437 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
439 InvalidateRect( hwnd, &rect, FALSE );
444 /***********************************************************************
445 * LISTBOX_GetItemRect
447 * Get the rectangle enclosing an item, in listbox client coordinates.
448 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
450 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
452 /* Index <= 0 is legal even on empty listboxes */
453 if (index && (index >= descr->nb_items)) return -1;
454 SetRect( rect, 0, 0, descr->width, descr->height );
455 if (descr->style & LBS_MULTICOLUMN)
457 INT col = (index / descr->page_size) -
458 (descr->top_item / descr->page_size);
459 rect->left += col * descr->column_width;
460 rect->right = rect->left + descr->column_width;
461 rect->top += (index % descr->page_size) * descr->item_height;
462 rect->bottom = rect->top + descr->item_height;
464 else if (descr->style & LBS_OWNERDRAWVARIABLE)
466 INT i;
467 rect->right += descr->horz_pos;
468 if ((index >= 0) && (index < descr->nb_items))
470 if (index < descr->top_item)
472 for (i = descr->top_item-1; i >= index; i--)
473 rect->top -= descr->items[i].height;
475 else
477 for (i = descr->top_item; i < index; i++)
478 rect->top += descr->items[i].height;
480 rect->bottom = rect->top + descr->items[index].height;
484 else
486 rect->top += (index - descr->top_item) * descr->item_height;
487 rect->bottom = rect->top + descr->item_height;
488 rect->right += descr->horz_pos;
491 return ((rect->left < descr->width) && (rect->right > 0) &&
492 (rect->top < descr->height) && (rect->bottom > 0));
496 /***********************************************************************
497 * LISTBOX_GetItemFromPoint
499 * Return the item nearest from point (x,y) (in client coordinates).
501 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
503 INT index = descr->top_item;
505 if (!descr->nb_items) return -1; /* No items */
506 if (descr->style & LBS_OWNERDRAWVARIABLE)
508 INT pos = 0;
509 if (y >= 0)
511 while (index < descr->nb_items)
513 if ((pos += descr->items[index].height) > y) break;
514 index++;
517 else
519 while (index > 0)
521 index--;
522 if ((pos -= descr->items[index].height) <= y) break;
526 else if (descr->style & LBS_MULTICOLUMN)
528 if (y >= descr->item_height * descr->page_size) return -1;
529 if (y >= 0) index += y / descr->item_height;
530 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
531 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
533 else
535 index += (y / descr->item_height);
537 if (index < 0) return 0;
538 if (index >= descr->nb_items) return -1;
539 return index;
543 /***********************************************************************
544 * LISTBOX_PaintItem
546 * Paint an item.
548 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
549 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
551 LB_ITEMDATA *item = NULL;
552 if (index < descr->nb_items) item = &descr->items[index];
554 if (IS_OWNERDRAW(descr))
556 DRAWITEMSTRUCT dis;
557 RECT r;
558 HRGN hrgn;
559 UINT id = GetWindowLongA( hwnd, GWL_ID );
561 if (!item)
563 if (action == ODA_FOCUS)
564 DrawFocusRect( hdc, rect );
565 else
566 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
567 return;
570 /* some programs mess with the clipping region when
571 drawing the item, *and* restore the previous region
572 after they are done, so a region has better to exist
573 else everything ends clipped */
574 GetClientRect(hwnd, &r);
575 hrgn = CreateRectRgnIndirect(&r);
576 SelectClipRgn( hdc, hrgn);
577 DeleteObject( hrgn );
579 dis.CtlType = ODT_LISTBOX;
580 dis.CtlID = id;
581 dis.hwndItem = hwnd;
582 dis.itemAction = action;
583 dis.hDC = hdc;
584 dis.itemID = index;
585 dis.itemState = 0;
586 if (item && item->selected) dis.itemState |= ODS_SELECTED;
587 if (!ignoreFocus && (descr->focus_item == index) &&
588 (descr->caret_on) &&
589 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
590 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
591 dis.itemData = item ? item->data : 0;
592 dis.rcItem = *rect;
593 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
594 hwnd, index, item ? debugstr_w(item->str) : "", action,
595 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
596 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
598 else
600 COLORREF oldText = 0, oldBk = 0;
602 if (action == ODA_FOCUS)
604 DrawFocusRect( hdc, rect );
605 return;
607 if (item && item->selected)
609 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
610 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
613 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
614 hwnd, index, item ? debugstr_w(item->str) : "", action,
615 rect->left, rect->top, rect->right, rect->bottom );
616 if (!item)
617 ExtTextOutW( hdc, rect->left + 1, rect->top,
618 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
619 else if (!(descr->style & LBS_USETABSTOPS))
620 ExtTextOutW( hdc, rect->left + 1, rect->top,
621 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
622 strlenW(item->str), NULL );
623 else
625 /* Output empty string to paint background in the full width. */
626 ExtTextOutW( hdc, rect->left + 1, rect->top,
627 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
628 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
629 item->str, strlenW(item->str),
630 descr->nb_tabs, descr->tabs, 0);
632 if (item && item->selected)
634 SetBkColor( hdc, oldBk );
635 SetTextColor( hdc, oldText );
637 if (!ignoreFocus && (descr->focus_item == index) &&
638 (descr->caret_on) &&
639 (descr->in_focus)) DrawFocusRect( hdc, rect );
644 /***********************************************************************
645 * LISTBOX_SetRedraw
647 * Change the redraw flag.
649 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
651 if (on)
653 if (!(descr->style & LBS_NOREDRAW)) return;
654 descr->style &= ~LBS_NOREDRAW;
655 if (descr->style & LBS_DISPLAYCHANGED)
656 { /* page was changed while setredraw false, refresh automatically */
657 InvalidateRect(hwnd, NULL, TRUE);
658 if ((descr->top_item + descr->page_size) > descr->nb_items)
659 { /* reset top of page if less than number of items/page */
660 descr->top_item = descr->nb_items - descr->page_size;
661 if (descr->top_item < 0) descr->top_item = 0;
663 descr->style &= ~LBS_DISPLAYCHANGED;
665 LISTBOX_UpdateScroll( hwnd, descr );
667 else descr->style |= LBS_NOREDRAW;
671 /***********************************************************************
672 * LISTBOX_RepaintItem
674 * Repaint a single item synchronously.
676 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
677 UINT action )
679 HDC hdc;
680 RECT rect;
681 HFONT oldFont = 0;
682 HBRUSH hbrush, oldBrush = 0;
684 /* Do not repaint the item if the item is not visible */
685 if (!IsWindowVisible(hwnd)) return;
686 if (descr->style & LBS_NOREDRAW)
688 descr->style |= LBS_DISPLAYCHANGED;
689 return;
691 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
692 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
693 if (descr->font) oldFont = SelectObject( hdc, descr->font );
694 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
695 (WPARAM)hdc, (LPARAM)hwnd );
696 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
697 if (!IsWindowEnabled(hwnd))
698 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
699 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
700 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
701 if (oldFont) SelectObject( hdc, oldFont );
702 if (oldBrush) SelectObject( hdc, oldBrush );
703 ReleaseDC( hwnd, hdc );
707 /***********************************************************************
708 * LISTBOX_InitStorage
710 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
712 LB_ITEMDATA *item;
714 nb_items += LB_ARRAY_GRANULARITY - 1;
715 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
716 if (descr->items)
717 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
718 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
719 nb_items * sizeof(LB_ITEMDATA) )))
721 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
722 return LB_ERRSPACE;
724 descr->items = item;
725 return LB_OKAY;
729 /***********************************************************************
730 * LISTBOX_SetTabStops
732 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
733 LPINT tabs, BOOL short_ints )
735 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
736 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
737 if (!(descr->nb_tabs = count))
739 descr->tabs = NULL;
740 return TRUE;
742 /* FIXME: count = 1 */
743 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
744 descr->nb_tabs * sizeof(INT) )))
745 return FALSE;
746 if (short_ints)
748 INT i;
749 LPINT16 p = (LPINT16)tabs;
751 TRACE("[%04x]: settabstops ", hwnd );
752 for (i = 0; i < descr->nb_tabs; i++) {
753 descr->tabs[i] = *p++<<1; /* FIXME */
754 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
756 if (TRACE_ON(listbox)) DPRINTF("\n");
758 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
759 /* FIXME: repaint the window? */
760 return TRUE;
764 /***********************************************************************
765 * LISTBOX_GetText
767 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
769 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
770 if (HAS_STRINGS(descr))
772 if (!lParam)
773 return strlenW(descr->items[index].str);
775 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
777 if(unicode)
779 LPWSTR buffer = (LPWSTR)lParam;
780 strcpyW( buffer, descr->items[index].str );
781 return strlenW(buffer);
783 else
785 LPSTR buffer = (LPSTR)lParam;
786 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
788 } else {
789 if (lParam)
790 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
791 return sizeof(DWORD);
796 /***********************************************************************
797 * LISTBOX_FindStringPos
799 * Find the nearest string located before a given string in sort order.
800 * If 'exact' is TRUE, return an error if we don't get an exact match.
802 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
803 BOOL exact )
805 INT index, min, max, res = -1;
807 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
808 min = 0;
809 max = descr->nb_items;
810 while (min != max)
812 index = (min + max) / 2;
813 if (HAS_STRINGS(descr))
814 res = lstrcmpiW( descr->items[index].str, str );
815 else
817 COMPAREITEMSTRUCT cis;
818 UINT id = GetWindowLongA( hwnd, GWL_ID );
820 cis.CtlType = ODT_LISTBOX;
821 cis.CtlID = id;
822 cis.hwndItem = hwnd;
823 cis.itemID1 = index;
824 cis.itemData1 = descr->items[index].data;
825 cis.itemID2 = -1;
826 cis.itemData2 = (DWORD)str;
827 cis.dwLocaleId = descr->locale;
828 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
830 if (!res) return index;
831 if (res > 0) max = index;
832 else min = index + 1;
834 return exact ? -1 : max;
838 /***********************************************************************
839 * LISTBOX_FindFileStrPos
841 * Find the nearest string located before a given string in directory
842 * sort order (i.e. first files, then directories, then drives).
844 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
846 INT min, max, res = -1;
848 if (!HAS_STRINGS(descr))
849 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
850 min = 0;
851 max = descr->nb_items;
852 while (min != max)
854 INT index = (min + max) / 2;
855 LPCWSTR p = descr->items[index].str;
856 if (*p == '[') /* drive or directory */
858 if (*str != '[') res = -1;
859 else if (p[1] == '-') /* drive */
861 if (str[1] == '-') res = str[2] - p[2];
862 else res = -1;
864 else /* directory */
866 if (str[1] == '-') res = 1;
867 else res = lstrcmpiW( str, p );
870 else /* filename */
872 if (*str == '[') res = 1;
873 else res = lstrcmpiW( str, p );
875 if (!res) return index;
876 if (res < 0) max = index;
877 else min = index + 1;
879 return max;
883 /***********************************************************************
884 * LISTBOX_FindString
886 * Find the item beginning with a given string.
888 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
889 LPCWSTR str, BOOL exact )
891 INT i;
892 LB_ITEMDATA *item;
894 if (start >= descr->nb_items) start = -1;
895 item = descr->items + start + 1;
896 if (HAS_STRINGS(descr))
898 if (!str || ! str[0] ) return LB_ERR;
899 if (exact)
901 for (i = start + 1; i < descr->nb_items; i++, item++)
902 if (!lstrcmpiW( str, item->str )) return i;
903 for (i = 0, item = descr->items; i <= start; i++, item++)
904 if (!lstrcmpiW( str, item->str )) return i;
906 else
908 /* Special case for drives and directories: ignore prefix */
909 #define CHECK_DRIVE(item) \
910 if ((item)->str[0] == '[') \
912 if (!strncmpiW( str, (item)->str+1, len )) return i; \
913 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
914 return i; \
917 INT len = strlenW(str);
918 for (i = start + 1; i < descr->nb_items; i++, item++)
920 if (!strncmpiW( str, item->str, len )) return i;
921 CHECK_DRIVE(item);
923 for (i = 0, item = descr->items; i <= start; i++, item++)
925 if (!strncmpiW( str, item->str, len )) return i;
926 CHECK_DRIVE(item);
928 #undef CHECK_DRIVE
931 else
933 if (exact && (descr->style & LBS_SORT))
934 /* If sorted, use a WM_COMPAREITEM binary search */
935 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
937 /* Otherwise use a linear search */
938 for (i = start + 1; i < descr->nb_items; i++, item++)
939 if (item->data == (DWORD)str) return i;
940 for (i = 0, item = descr->items; i <= start; i++, item++)
941 if (item->data == (DWORD)str) return i;
943 return LB_ERR;
947 /***********************************************************************
948 * LISTBOX_GetSelCount
950 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
952 INT i, count;
953 LB_ITEMDATA *item = descr->items;
955 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
956 for (i = count = 0; i < descr->nb_items; i++, item++)
957 if (item->selected) count++;
958 return count;
962 /***********************************************************************
963 * LISTBOX_GetSelItems16
965 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
967 INT i, count;
968 LB_ITEMDATA *item = descr->items;
970 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
971 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
972 if (item->selected) array[count++] = (INT16)i;
973 return count;
977 /***********************************************************************
978 * LISTBOX_GetSelItems
980 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
982 INT i, count;
983 LB_ITEMDATA *item = descr->items;
985 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
986 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
987 if (item->selected) array[count++] = i;
988 return count;
992 /***********************************************************************
993 * LISTBOX_Paint
995 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
997 INT i, col_pos = descr->page_size - 1;
998 RECT rect;
999 RECT focusRect = {-1, -1, -1, -1};
1000 HFONT oldFont = 0;
1001 HBRUSH hbrush, oldBrush = 0;
1003 if (descr->style & LBS_NOREDRAW) return 0;
1005 SetRect( &rect, 0, 0, descr->width, descr->height );
1006 if (descr->style & LBS_MULTICOLUMN)
1007 rect.right = rect.left + descr->column_width;
1008 else if (descr->horz_pos)
1010 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1011 rect.right += descr->horz_pos;
1014 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1015 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1016 (WPARAM)hdc, (LPARAM)hwnd );
1017 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1018 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1020 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1021 (descr->in_focus))
1023 /* Special case for empty listbox: paint focus rect */
1024 rect.bottom = rect.top + descr->item_height;
1025 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1026 ODA_FOCUS, FALSE );
1027 rect.top = rect.bottom;
1030 /* Paint all the item, regarding the selection
1031 Focus state will be painted after */
1033 for (i = descr->top_item; i < descr->nb_items; i++)
1035 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1036 rect.bottom = rect.top + descr->item_height;
1037 else
1038 rect.bottom = rect.top + descr->items[i].height;
1040 if (i == descr->focus_item)
1042 /* keep the focus rect, to paint the focus item after */
1043 focusRect.left = rect.left;
1044 focusRect.right = rect.right;
1045 focusRect.top = rect.top;
1046 focusRect.bottom = rect.bottom;
1048 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1049 rect.top = rect.bottom;
1051 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1053 if (!IS_OWNERDRAW(descr))
1055 /* Clear the bottom of the column */
1056 if (rect.top < descr->height)
1058 rect.bottom = descr->height;
1059 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1060 &rect, NULL, 0, NULL );
1064 /* Go to the next column */
1065 rect.left += descr->column_width;
1066 rect.right += descr->column_width;
1067 rect.top = 0;
1068 col_pos = descr->page_size - 1;
1070 else
1072 col_pos--;
1073 if (rect.top >= descr->height) break;
1077 /* Paint the focus item now */
1078 if (focusRect.top != focusRect.bottom && descr->caret_on)
1079 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1081 if (!IS_OWNERDRAW(descr))
1083 /* Clear the remainder of the client area */
1084 if (rect.top < descr->height)
1086 rect.bottom = descr->height;
1087 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1088 &rect, NULL, 0, NULL );
1090 if (rect.right < descr->width)
1092 rect.left = rect.right;
1093 rect.right = descr->width;
1094 rect.top = 0;
1095 rect.bottom = descr->height;
1096 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1097 &rect, NULL, 0, NULL );
1100 if (oldFont) SelectObject( hdc, oldFont );
1101 if (oldBrush) SelectObject( hdc, oldBrush );
1102 return 0;
1106 /***********************************************************************
1107 * LISTBOX_InvalidateItems
1109 * Invalidate all items from a given item. If the specified item is not
1110 * visible, nothing happens.
1112 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1114 RECT rect;
1116 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1118 if (descr->style & LBS_NOREDRAW)
1120 descr->style |= LBS_DISPLAYCHANGED;
1121 return;
1123 rect.bottom = descr->height;
1124 InvalidateRect( hwnd, &rect, TRUE );
1125 if (descr->style & LBS_MULTICOLUMN)
1127 /* Repaint the other columns */
1128 rect.left = rect.right;
1129 rect.right = descr->width;
1130 rect.top = 0;
1131 InvalidateRect( hwnd, &rect, TRUE );
1137 /***********************************************************************
1138 * LISTBOX_GetItemHeight
1140 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1142 if (descr->style & LBS_OWNERDRAWVARIABLE)
1144 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1145 return descr->items[index].height;
1147 else return descr->item_height;
1151 /***********************************************************************
1152 * LISTBOX_SetItemHeight
1154 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1155 INT height, BOOL repaint )
1157 if (!height) height = 1;
1159 if (descr->style & LBS_OWNERDRAWVARIABLE)
1161 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1162 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1163 descr->items[index].height = height;
1164 LISTBOX_UpdateScroll( hwnd, descr );
1165 if (repaint)
1166 LISTBOX_InvalidateItems( hwnd, descr, index );
1168 else if (height != descr->item_height)
1170 TRACE("[%04x]: new height = %d\n", hwnd, height );
1171 descr->item_height = height;
1172 LISTBOX_UpdatePage( hwnd, descr );
1173 LISTBOX_UpdateScroll( hwnd, descr );
1174 if (repaint)
1175 InvalidateRect( hwnd, 0, TRUE );
1177 return LB_OKAY;
1181 /***********************************************************************
1182 * LISTBOX_SetHorizontalPos
1184 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1186 INT diff;
1188 if (pos > descr->horz_extent - descr->width)
1189 pos = descr->horz_extent - descr->width;
1190 if (pos < 0) pos = 0;
1191 if (!(diff = descr->horz_pos - pos)) return;
1192 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1193 descr->horz_pos = pos;
1194 LISTBOX_UpdateScroll( hwnd, descr );
1195 if (abs(diff) < descr->width)
1196 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1197 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1198 else
1199 InvalidateRect( hwnd, NULL, TRUE );
1203 /***********************************************************************
1204 * LISTBOX_SetHorizontalExtent
1206 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1207 INT extent )
1209 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1210 return LB_OKAY;
1211 if (extent <= 0) extent = 1;
1212 if (extent == descr->horz_extent) return LB_OKAY;
1213 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1214 descr->horz_extent = extent;
1215 if (descr->horz_pos > extent - descr->width)
1216 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1217 else
1218 LISTBOX_UpdateScroll( hwnd, descr );
1219 return LB_OKAY;
1223 /***********************************************************************
1224 * LISTBOX_SetColumnWidth
1226 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1228 if (width == descr->column_width) return LB_OKAY;
1229 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1230 descr->column_width = width;
1231 LISTBOX_UpdatePage( hwnd, descr );
1232 return LB_OKAY;
1236 /***********************************************************************
1237 * LISTBOX_SetFont
1239 * Returns the item height.
1241 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1243 HDC hdc;
1244 HFONT oldFont = 0;
1245 TEXTMETRICW tm;
1247 descr->font = font;
1249 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1251 ERR("unable to get DC.\n" );
1252 return 16;
1254 if (font) oldFont = SelectObject( hdc, font );
1255 GetTextMetricsW( hdc, &tm );
1256 if (oldFont) SelectObject( hdc, oldFont );
1257 ReleaseDC( hwnd, hdc );
1258 if (!IS_OWNERDRAW(descr))
1259 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1260 return tm.tmHeight ;
1264 /***********************************************************************
1265 * LISTBOX_MakeItemVisible
1267 * Make sure that a given item is partially or fully visible.
1269 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1270 BOOL fully )
1272 INT top;
1274 if (index <= descr->top_item) top = index;
1275 else if (descr->style & LBS_MULTICOLUMN)
1277 INT cols = descr->width;
1278 if (!fully) cols += descr->column_width - 1;
1279 if (cols >= descr->column_width) cols /= descr->column_width;
1280 else cols = 1;
1281 if (index < descr->top_item + (descr->page_size * cols)) return;
1282 top = index - descr->page_size * (cols - 1);
1284 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1286 INT height = fully ? descr->items[index].height : 1;
1287 for (top = index; top > descr->top_item; top--)
1288 if ((height += descr->items[top-1].height) > descr->height) break;
1290 else
1292 if (index < descr->top_item + descr->page_size) return;
1293 if (!fully && (index == descr->top_item + descr->page_size) &&
1294 (descr->height > (descr->page_size * descr->item_height))) return;
1295 top = index - descr->page_size + 1;
1297 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1300 /***********************************************************************
1301 * LISTBOX_SetCaretIndex
1303 * NOTES
1304 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1307 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1308 BOOL fully_visible )
1310 INT oldfocus = descr->focus_item;
1312 if (descr->style & LBS_NOSEL) return LB_ERR;
1313 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1314 if (index == oldfocus) return LB_OKAY;
1315 descr->focus_item = index;
1316 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1317 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1319 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1320 if (descr->caret_on && (descr->in_focus))
1321 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1323 return LB_OKAY;
1327 /***********************************************************************
1328 * LISTBOX_SelectItemRange
1330 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1332 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1333 INT last, BOOL on )
1335 INT i;
1337 /* A few sanity checks */
1339 if (descr->style & LBS_NOSEL) return LB_ERR;
1340 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1341 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1342 if (last == -1) last = descr->nb_items - 1;
1343 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1344 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1345 /* selected_item reflects last selected/unselected item on multiple sel */
1346 descr->selected_item = last;
1348 if (on) /* Turn selection on */
1350 for (i = first; i <= last; i++)
1352 if (descr->items[i].selected) continue;
1353 descr->items[i].selected = TRUE;
1354 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1356 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1358 else /* Turn selection off */
1360 for (i = first; i <= last; i++)
1362 if (!descr->items[i].selected) continue;
1363 descr->items[i].selected = FALSE;
1364 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1367 return LB_OKAY;
1370 /***********************************************************************
1371 * LISTBOX_SetSelection
1373 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1374 BOOL on, BOOL send_notify )
1376 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1378 if (descr->style & LBS_NOSEL) return LB_ERR;
1379 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1380 if (descr->style & LBS_MULTIPLESEL)
1382 if (index == -1) /* Select all items */
1383 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1384 else /* Only one item */
1385 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1387 else
1389 INT oldsel = descr->selected_item;
1390 if (index == oldsel) return LB_OKAY;
1391 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1392 if (index != -1) descr->items[index].selected = TRUE;
1393 descr->selected_item = index;
1394 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1395 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1396 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1397 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1398 else
1399 if( descr->lphc ) /* set selection change flag for parent combo */
1400 descr->lphc->wState |= CBF_SELCHANGE;
1402 return LB_OKAY;
1406 /***********************************************************************
1407 * LISTBOX_MoveCaret
1409 * Change the caret position and extend the selection to the new caret.
1411 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1412 BOOL fully_visible )
1414 INT oldfocus = descr->focus_item;
1416 if ((index < 0) || (index >= descr->nb_items))
1417 return;
1419 /* Important, repaint needs to be done in this order if
1420 you want to mimic Windows behavior:
1421 1. Remove the focus and paint the item
1422 2. Remove the selection and paint the item(s)
1423 3. Set the selection and repaint the item(s)
1424 4. Set the focus to 'index' and repaint the item */
1426 /* 1. remove the focus and repaint the item */
1427 descr->focus_item = -1;
1428 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1429 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1431 /* 2. then turn off the previous selection */
1432 /* 3. repaint the new selected item */
1433 if (descr->style & LBS_EXTENDEDSEL)
1435 if (descr->anchor_item != -1)
1437 INT first = min( index, descr->anchor_item );
1438 INT last = max( index, descr->anchor_item );
1439 if (first > 0)
1440 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1441 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1442 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1445 else if (!(descr->style & LBS_MULTIPLESEL))
1447 /* Set selection to new caret item */
1448 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1451 /* 4. repaint the new item with the focus */
1452 descr->focus_item = index;
1453 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1454 if (descr->caret_on && (descr->in_focus))
1455 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1459 /***********************************************************************
1460 * LISTBOX_InsertItem
1462 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1463 LPWSTR str, DWORD data )
1465 LB_ITEMDATA *item;
1466 INT max_items;
1467 INT oldfocus = descr->focus_item;
1469 if (index == -1) index = descr->nb_items;
1470 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1471 if (!descr->items) max_items = 0;
1472 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1473 if (descr->nb_items == max_items)
1475 /* We need to grow the array */
1476 max_items += LB_ARRAY_GRANULARITY;
1477 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1478 max_items * sizeof(LB_ITEMDATA) )))
1480 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1481 return LB_ERRSPACE;
1483 descr->items = item;
1486 /* Insert the item structure */
1488 item = &descr->items[index];
1489 if (index < descr->nb_items)
1490 RtlMoveMemory( item + 1, item,
1491 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1492 item->str = str;
1493 item->data = data;
1494 item->height = 0;
1495 item->selected = FALSE;
1496 descr->nb_items++;
1498 /* Get item height */
1500 if (descr->style & LBS_OWNERDRAWVARIABLE)
1502 MEASUREITEMSTRUCT mis;
1503 UINT id = GetWindowLongA( hwnd, GWL_ID );
1505 mis.CtlType = ODT_LISTBOX;
1506 mis.CtlID = id;
1507 mis.itemID = index;
1508 mis.itemData = descr->items[index].data;
1509 mis.itemHeight = descr->item_height;
1510 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1511 item->height = mis.itemHeight ? mis.itemHeight : 1;
1512 TRACE("[%04x]: measure item %d (%s) = %d\n",
1513 hwnd, index, str ? debugstr_w(str) : "", item->height );
1516 /* Repaint the items */
1518 LISTBOX_UpdateScroll( hwnd, descr );
1519 LISTBOX_InvalidateItems( hwnd, descr, index );
1521 /* Move selection and focused item */
1522 /* If listbox was empty, set focus to the first item */
1523 if (descr->nb_items == 1)
1524 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1525 /* single select don't change selection index in win31 */
1526 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1528 descr->selected_item++;
1529 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1531 else
1533 if (index <= descr->selected_item)
1535 descr->selected_item++;
1536 descr->focus_item = oldfocus; /* focus not changed */
1539 return LB_OKAY;
1543 /***********************************************************************
1544 * LISTBOX_InsertString
1546 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1547 LPCWSTR str )
1549 LPWSTR new_str = NULL;
1550 DWORD data = 0;
1551 LRESULT ret;
1553 if (HAS_STRINGS(descr))
1555 static const WCHAR empty_stringW[] = { 0 };
1556 if (!str) str = empty_stringW;
1557 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1559 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1560 return LB_ERRSPACE;
1562 strcpyW(new_str, str);
1564 else data = (DWORD)str;
1566 if (index == -1) index = descr->nb_items;
1567 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1569 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1570 return ret;
1573 TRACE("[%04x]: added item %d %s\n",
1574 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1575 return index;
1579 /***********************************************************************
1580 * LISTBOX_DeleteItem
1582 * Delete the content of an item. 'index' must be a valid index.
1584 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1586 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1587 * while Win95 sends it for all items with user data.
1588 * It's probably better to send it too often than not
1589 * often enough, so this is what we do here.
1591 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1593 DELETEITEMSTRUCT dis;
1594 UINT id = GetWindowLongA( hwnd, GWL_ID );
1596 dis.CtlType = ODT_LISTBOX;
1597 dis.CtlID = id;
1598 dis.itemID = index;
1599 dis.hwndItem = hwnd;
1600 dis.itemData = descr->items[index].data;
1601 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1603 if (HAS_STRINGS(descr) && descr->items[index].str)
1604 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1608 /***********************************************************************
1609 * LISTBOX_RemoveItem
1611 * Remove an item from the listbox and delete its content.
1613 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1615 LB_ITEMDATA *item;
1616 INT max_items;
1618 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1619 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1621 /* We need to invalidate the original rect instead of the updated one. */
1622 LISTBOX_InvalidateItems( hwnd, descr, index );
1624 LISTBOX_DeleteItem( hwnd, descr, index );
1626 /* Remove the item */
1628 item = &descr->items[index];
1629 if (index < descr->nb_items-1)
1630 RtlMoveMemory( item, item + 1,
1631 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1632 descr->nb_items--;
1633 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1635 /* Shrink the item array if possible */
1637 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1638 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1640 max_items -= LB_ARRAY_GRANULARITY;
1641 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1642 max_items * sizeof(LB_ITEMDATA) );
1643 if (item) descr->items = item;
1645 /* Repaint the items */
1647 LISTBOX_UpdateScroll( hwnd, descr );
1648 /* if we removed the scrollbar, reset the top of the list
1649 (correct for owner-drawn ???) */
1650 if (descr->nb_items == descr->page_size)
1651 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1653 /* Move selection and focused item */
1654 if (!IS_MULTISELECT(descr))
1656 if (index == descr->selected_item)
1657 descr->selected_item = -1;
1658 else if (index < descr->selected_item)
1660 descr->selected_item--;
1661 if (ISWIN31) /* win 31 do not change the selected item number */
1662 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1666 if (descr->focus_item >= descr->nb_items)
1668 descr->focus_item = descr->nb_items - 1;
1669 if (descr->focus_item < 0) descr->focus_item = 0;
1671 return LB_OKAY;
1675 /***********************************************************************
1676 * LISTBOX_ResetContent
1678 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1680 INT i;
1682 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1683 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1684 descr->nb_items = 0;
1685 descr->top_item = 0;
1686 descr->selected_item = -1;
1687 descr->focus_item = 0;
1688 descr->anchor_item = -1;
1689 descr->items = NULL;
1693 /***********************************************************************
1694 * LISTBOX_SetCount
1696 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1698 LRESULT ret;
1700 if (HAS_STRINGS(descr)) return LB_ERR;
1701 /* FIXME: this is far from optimal... */
1702 if (count > descr->nb_items)
1704 while (count > descr->nb_items)
1705 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1706 return ret;
1708 else if (count < descr->nb_items)
1710 while (count < descr->nb_items)
1711 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1712 return ret;
1714 return LB_OKAY;
1718 /***********************************************************************
1719 * LISTBOX_Directory
1721 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1722 LPCWSTR filespec, BOOL long_names )
1724 HANDLE handle;
1725 LRESULT ret = LB_OKAY;
1726 WIN32_FIND_DATAW entry;
1727 int pos;
1729 /* don't scan directory if we just want drives exclusively */
1730 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1731 /* scan directory */
1732 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1734 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1736 else
1740 WCHAR buffer[270];
1741 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1743 static const WCHAR bracketW[] = { ']',0 };
1744 static const WCHAR dotW[] = { '.',0 };
1745 if (!(attrib & DDL_DIRECTORY) ||
1746 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1747 buffer[0] = '[';
1748 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1749 else strcpyW( buffer + 1, entry.cAlternateFileName );
1750 strcatW(buffer, bracketW);
1752 else /* not a directory */
1754 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1755 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1757 if ((attrib & DDL_EXCLUSIVE) &&
1758 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1759 continue;
1760 #undef ATTRIBS
1761 if (long_names) strcpyW( buffer, entry.cFileName );
1762 else strcpyW( buffer, entry.cAlternateFileName );
1764 if (!long_names) CharLowerW( buffer );
1765 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1766 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1767 break;
1768 } while (FindNextFileW( handle, &entry ));
1769 FindClose( handle );
1773 /* scan drives */
1774 if ((ret >= 0) && (attrib & DDL_DRIVES))
1776 WCHAR buffer[] = {'[','-','a','-',']',0};
1777 WCHAR root[] = {'A',':','\\',0};
1778 int drive;
1779 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1781 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1782 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1783 break;
1786 return ret;
1790 /***********************************************************************
1791 * LISTBOX_HandleVScroll
1793 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1795 SCROLLINFO info;
1797 if (descr->style & LBS_MULTICOLUMN) return 0;
1798 switch(LOWORD(wParam))
1800 case SB_LINEUP:
1801 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1802 break;
1803 case SB_LINEDOWN:
1804 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1805 break;
1806 case SB_PAGEUP:
1807 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1808 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1809 break;
1810 case SB_PAGEDOWN:
1811 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1812 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1813 break;
1814 case SB_THUMBPOSITION:
1815 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1816 break;
1817 case SB_THUMBTRACK:
1818 info.cbSize = sizeof(info);
1819 info.fMask = SIF_TRACKPOS;
1820 GetScrollInfo( hwnd, SB_VERT, &info );
1821 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1822 break;
1823 case SB_TOP:
1824 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1825 break;
1826 case SB_BOTTOM:
1827 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1828 break;
1830 return 0;
1834 /***********************************************************************
1835 * LISTBOX_HandleHScroll
1837 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1839 SCROLLINFO info;
1840 INT page;
1842 if (descr->style & LBS_MULTICOLUMN)
1844 switch(LOWORD(wParam))
1846 case SB_LINELEFT:
1847 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1848 TRUE );
1849 break;
1850 case SB_LINERIGHT:
1851 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1852 TRUE );
1853 break;
1854 case SB_PAGELEFT:
1855 page = descr->width / descr->column_width;
1856 if (page < 1) page = 1;
1857 LISTBOX_SetTopItem( hwnd, descr,
1858 descr->top_item - page * descr->page_size, TRUE );
1859 break;
1860 case SB_PAGERIGHT:
1861 page = descr->width / descr->column_width;
1862 if (page < 1) page = 1;
1863 LISTBOX_SetTopItem( hwnd, descr,
1864 descr->top_item + page * descr->page_size, TRUE );
1865 break;
1866 case SB_THUMBPOSITION:
1867 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1868 TRUE );
1869 break;
1870 case SB_THUMBTRACK:
1871 info.cbSize = sizeof(info);
1872 info.fMask = SIF_TRACKPOS;
1873 GetScrollInfo( hwnd, SB_VERT, &info );
1874 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1875 TRUE );
1876 break;
1877 case SB_LEFT:
1878 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1879 break;
1880 case SB_RIGHT:
1881 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1882 break;
1885 else if (descr->horz_extent)
1887 switch(LOWORD(wParam))
1889 case SB_LINELEFT:
1890 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1891 break;
1892 case SB_LINERIGHT:
1893 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1894 break;
1895 case SB_PAGELEFT:
1896 LISTBOX_SetHorizontalPos( hwnd, descr,
1897 descr->horz_pos - descr->width );
1898 break;
1899 case SB_PAGERIGHT:
1900 LISTBOX_SetHorizontalPos( hwnd, descr,
1901 descr->horz_pos + descr->width );
1902 break;
1903 case SB_THUMBPOSITION:
1904 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1905 break;
1906 case SB_THUMBTRACK:
1907 info.cbSize = sizeof(info);
1908 info.fMask = SIF_TRACKPOS;
1909 GetScrollInfo( hwnd, SB_HORZ, &info );
1910 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1911 break;
1912 case SB_LEFT:
1913 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1914 break;
1915 case SB_RIGHT:
1916 LISTBOX_SetHorizontalPos( hwnd, descr,
1917 descr->horz_extent - descr->width );
1918 break;
1921 return 0;
1924 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1926 short gcWheelDelta = 0;
1927 UINT pulScrollLines = 3;
1929 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1931 gcWheelDelta -= (short) HIWORD(wParam);
1933 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1935 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1936 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1937 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1939 return 0;
1942 /***********************************************************************
1943 * LISTBOX_HandleLButtonDown
1945 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1946 WPARAM wParam, INT x, INT y )
1948 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1949 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1950 if (!descr->caret_on && (descr->in_focus)) return 0;
1952 if (!descr->in_focus)
1954 if( !descr->lphc ) SetFocus( hwnd );
1955 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1958 if (index == -1) return 0;
1960 if (descr->style & LBS_EXTENDEDSEL)
1962 /* we should perhaps make sure that all items are deselected
1963 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1964 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1965 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1968 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1969 if (wParam & MK_CONTROL)
1971 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1972 LISTBOX_SetSelection( hwnd, descr, index,
1973 !descr->items[index].selected,
1974 (descr->style & LBS_NOTIFY) != 0);
1976 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1978 else
1980 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1981 LISTBOX_SetSelection( hwnd, descr, index,
1982 (!(descr->style & LBS_MULTIPLESEL) ||
1983 !descr->items[index].selected),
1984 (descr->style & LBS_NOTIFY) != 0 );
1987 descr->captured = TRUE;
1988 SetCapture( hwnd );
1990 if (!descr->lphc)
1992 if (descr->style & LBS_NOTIFY )
1993 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1994 MAKELPARAM( x, y ) );
1995 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1997 POINT pt;
1999 pt.x = x;
2000 pt.y = y;
2002 if (DragDetect( hwnd, pt ))
2003 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2006 return 0;
2010 /*************************************************************************
2011 * LISTBOX_HandleLButtonDownCombo [Internal]
2013 * Process LButtonDown message for the ComboListBox
2015 nn * PARAMS
2016 * pWnd [I] The windows internal structure
2017 * pDescr [I] The ListBox internal structure
2018 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2019 * x [I] X Mouse Coordinate
2020 * y [I] Y Mouse Coordinate
2022 * RETURNS
2023 * 0 since we are processing the WM_LBUTTONDOWN Message
2025 * NOTES
2026 * This function is only to be used when a ListBox is a ComboListBox
2029 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2030 UINT msg, WPARAM wParam, INT x, INT y)
2032 RECT clientRect, screenRect;
2033 POINT mousePos;
2035 mousePos.x = x;
2036 mousePos.y = y;
2038 GetClientRect(hwnd, &clientRect);
2040 if(PtInRect(&clientRect, mousePos))
2042 /* MousePos is in client, resume normal processing */
2043 if (msg == WM_LBUTTONDOWN)
2045 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2046 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2048 else if (pDescr->style & LBS_NOTIFY)
2049 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2050 return 0;
2052 else
2054 POINT screenMousePos;
2055 HWND hWndOldCapture;
2057 /* Check the Non-Client Area */
2058 screenMousePos = mousePos;
2059 hWndOldCapture = GetCapture();
2060 ReleaseCapture();
2061 GetWindowRect(hwnd, &screenRect);
2062 ClientToScreen(hwnd, &screenMousePos);
2064 if(!PtInRect(&screenRect, screenMousePos))
2066 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2067 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2068 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2069 return 0;
2071 else
2073 /* Check to see the NC is a scrollbar */
2074 INT nHitTestType=0;
2075 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2076 /* Check Vertical scroll bar */
2077 if (style & WS_VSCROLL)
2079 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2080 if (PtInRect( &clientRect, mousePos ))
2082 nHitTestType = HTVSCROLL;
2085 /* Check horizontal scroll bar */
2086 if (style & WS_HSCROLL)
2088 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2089 if (PtInRect( &clientRect, mousePos ))
2091 nHitTestType = HTHSCROLL;
2094 /* Windows sends this message when a scrollbar is clicked
2097 if(nHitTestType != 0)
2099 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2100 MAKELONG(screenMousePos.x, screenMousePos.y));
2102 /* Resume the Capture after scrolling is complete
2104 if(hWndOldCapture != 0)
2106 SetCapture(hWndOldCapture);
2110 return 0;
2113 /***********************************************************************
2114 * LISTBOX_HandleLButtonUp
2116 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2118 if (LISTBOX_Timer != LB_TIMER_NONE)
2119 KillSystemTimer( hwnd, LB_TIMER_ID );
2120 LISTBOX_Timer = LB_TIMER_NONE;
2121 if (descr->captured)
2123 descr->captured = FALSE;
2124 if (GetCapture() == hwnd) ReleaseCapture();
2125 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2126 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2128 return 0;
2132 /***********************************************************************
2133 * LISTBOX_HandleTimer
2135 * Handle scrolling upon a timer event.
2136 * Return TRUE if scrolling should continue.
2138 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2139 INT index, TIMER_DIRECTION dir )
2141 switch(dir)
2143 case LB_TIMER_UP:
2144 if (descr->top_item) index = descr->top_item - 1;
2145 else index = 0;
2146 break;
2147 case LB_TIMER_LEFT:
2148 if (descr->top_item) index -= descr->page_size;
2149 break;
2150 case LB_TIMER_DOWN:
2151 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2152 if (index == descr->focus_item) index++;
2153 if (index >= descr->nb_items) index = descr->nb_items - 1;
2154 break;
2155 case LB_TIMER_RIGHT:
2156 if (index + descr->page_size < descr->nb_items)
2157 index += descr->page_size;
2158 break;
2159 case LB_TIMER_NONE:
2160 break;
2162 if (index == descr->focus_item) return FALSE;
2163 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2164 return TRUE;
2168 /***********************************************************************
2169 * LISTBOX_HandleSystemTimer
2171 * WM_SYSTIMER handler.
2173 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2175 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2177 KillSystemTimer( hwnd, LB_TIMER_ID );
2178 LISTBOX_Timer = LB_TIMER_NONE;
2180 return 0;
2184 /***********************************************************************
2185 * LISTBOX_HandleMouseMove
2187 * WM_MOUSEMOVE handler.
2189 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2190 INT x, INT y )
2192 INT index;
2193 TIMER_DIRECTION dir = LB_TIMER_NONE;
2195 if (!descr->captured) return;
2197 if (descr->style & LBS_MULTICOLUMN)
2199 if (y < 0) y = 0;
2200 else if (y >= descr->item_height * descr->page_size)
2201 y = descr->item_height * descr->page_size - 1;
2203 if (x < 0)
2205 dir = LB_TIMER_LEFT;
2206 x = 0;
2208 else if (x >= descr->width)
2210 dir = LB_TIMER_RIGHT;
2211 x = descr->width - 1;
2214 else
2216 if (y < 0) dir = LB_TIMER_UP; /* above */
2217 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2220 index = LISTBOX_GetItemFromPoint( descr, x, y );
2221 if (index == -1) index = descr->focus_item;
2222 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2224 /* Start/stop the system timer */
2226 if (dir != LB_TIMER_NONE)
2227 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2228 else if (LISTBOX_Timer != LB_TIMER_NONE)
2229 KillSystemTimer( hwnd, LB_TIMER_ID );
2230 LISTBOX_Timer = dir;
2234 /***********************************************************************
2235 * LISTBOX_HandleKeyDown
2237 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2239 INT caret = -1;
2240 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2241 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2242 bForceSelection = FALSE; /* only for single select list */
2244 if (descr->style & LBS_WANTKEYBOARDINPUT)
2246 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2247 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2248 (LPARAM)hwnd );
2249 if (caret == -2) return 0;
2251 if (caret == -1) switch(wParam)
2253 case VK_LEFT:
2254 if (descr->style & LBS_MULTICOLUMN)
2256 bForceSelection = FALSE;
2257 if (descr->focus_item >= descr->page_size)
2258 caret = descr->focus_item - descr->page_size;
2259 break;
2261 /* fall through */
2262 case VK_UP:
2263 caret = descr->focus_item - 1;
2264 if (caret < 0) caret = 0;
2265 break;
2266 case VK_RIGHT:
2267 if (descr->style & LBS_MULTICOLUMN)
2269 bForceSelection = FALSE;
2270 if (descr->focus_item + descr->page_size < descr->nb_items)
2271 caret = descr->focus_item + descr->page_size;
2272 break;
2274 /* fall through */
2275 case VK_DOWN:
2276 caret = descr->focus_item + 1;
2277 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2278 break;
2280 case VK_PRIOR:
2281 if (descr->style & LBS_MULTICOLUMN)
2283 INT page = descr->width / descr->column_width;
2284 if (page < 1) page = 1;
2285 caret = descr->focus_item - (page * descr->page_size) + 1;
2287 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2288 if (caret < 0) caret = 0;
2289 break;
2290 case VK_NEXT:
2291 if (descr->style & LBS_MULTICOLUMN)
2293 INT page = descr->width / descr->column_width;
2294 if (page < 1) page = 1;
2295 caret = descr->focus_item + (page * descr->page_size) - 1;
2297 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2298 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2299 break;
2300 case VK_HOME:
2301 caret = 0;
2302 break;
2303 case VK_END:
2304 caret = descr->nb_items - 1;
2305 break;
2306 case VK_SPACE:
2307 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2308 else if (descr->style & LBS_MULTIPLESEL)
2310 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2311 !descr->items[descr->focus_item].selected,
2312 (descr->style & LBS_NOTIFY) != 0 );
2314 break;
2315 default:
2316 bForceSelection = FALSE;
2318 if (bForceSelection) /* focused item is used instead of key */
2319 caret = descr->focus_item;
2320 if (caret >= 0)
2322 if ((descr->style & LBS_EXTENDEDSEL) &&
2323 !(GetKeyState( VK_SHIFT ) & 0x8000))
2324 descr->anchor_item = caret;
2325 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2326 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2327 if (descr->style & LBS_NOTIFY)
2329 if( descr->lphc )
2331 /* make sure that combo parent doesn't hide us */
2332 descr->lphc->wState |= CBF_NOROLLUP;
2334 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2337 return 0;
2341 /***********************************************************************
2342 * LISTBOX_HandleChar
2344 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2346 INT caret = -1;
2347 WCHAR str[2];
2349 str[0] = charW;
2350 str[1] = '\0';
2352 if (descr->style & LBS_WANTKEYBOARDINPUT)
2354 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2355 MAKEWPARAM(charW, descr->focus_item),
2356 (LPARAM)hwnd );
2357 if (caret == -2) return 0;
2359 if (caret == -1)
2360 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2361 if (caret != -1)
2363 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2364 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2365 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2366 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2367 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2369 return 0;
2373 /***********************************************************************
2374 * LISTBOX_Create
2376 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2378 LB_DESCR *descr;
2379 MEASUREITEMSTRUCT mis;
2380 RECT rect;
2382 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2383 return FALSE;
2385 GetClientRect( hwnd, &rect );
2386 descr->owner = GetParent( hwnd );
2387 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2388 descr->width = rect.right - rect.left;
2389 descr->height = rect.bottom - rect.top;
2390 descr->items = NULL;
2391 descr->nb_items = 0;
2392 descr->top_item = 0;
2393 descr->selected_item = -1;
2394 descr->focus_item = 0;
2395 descr->anchor_item = -1;
2396 descr->item_height = 1;
2397 descr->page_size = 1;
2398 descr->column_width = 150;
2399 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2400 descr->horz_pos = 0;
2401 descr->nb_tabs = 0;
2402 descr->tabs = NULL;
2403 descr->caret_on = lphc ? FALSE : TRUE;
2404 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2405 descr->in_focus = FALSE;
2406 descr->captured = FALSE;
2407 descr->font = 0;
2408 descr->locale = 0; /* FIXME */
2409 descr->lphc = lphc;
2411 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2413 /* Win95 document "List Box Differences" from MSDN:
2414 If a list box in a version 3.x application has either the
2415 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2416 horizontal and vertical scroll bars.
2418 descr->style |= WS_VSCROLL | WS_HSCROLL;
2421 if( lphc )
2423 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2424 hwnd, descr->owner, lphc->self );
2425 descr->owner = lphc->self;
2428 SetWindowLongA( hwnd, 0, (LONG)descr );
2430 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2432 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2433 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2434 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2435 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2437 if (descr->style & LBS_OWNERDRAWFIXED)
2439 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2441 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2442 descr->item_height = lphc->fixedOwnerDrawHeight;
2444 else
2446 UINT id = GetWindowLongA( hwnd, GWL_ID );
2447 mis.CtlType = ODT_LISTBOX;
2448 mis.CtlID = id;
2449 mis.itemID = -1;
2450 mis.itemWidth = 0;
2451 mis.itemData = 0;
2452 mis.itemHeight = descr->item_height;
2453 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2454 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2458 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2459 return TRUE;
2463 /***********************************************************************
2464 * LISTBOX_Destroy
2466 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2468 LISTBOX_ResetContent( hwnd, descr );
2469 SetWindowLongA( hwnd, 0, 0 );
2470 HeapFree( GetProcessHeap(), 0, descr );
2471 return TRUE;
2475 /***********************************************************************
2476 * ListBoxWndProc_common
2478 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2479 WPARAM wParam, LPARAM lParam, BOOL unicode )
2481 LRESULT ret;
2482 LB_DESCR *descr;
2484 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2486 if (msg == WM_CREATE)
2488 if (!LISTBOX_Create( hwnd, NULL ))
2489 return -1;
2490 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2491 return 0;
2493 /* Ignore all other messages before we get a WM_CREATE */
2494 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2495 DefWindowProcA( hwnd, msg, wParam, lParam );
2498 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2499 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2500 switch(msg)
2502 case LB_RESETCONTENT16:
2503 case LB_RESETCONTENT:
2504 LISTBOX_ResetContent( hwnd, descr );
2505 LISTBOX_UpdateScroll( hwnd, descr );
2506 InvalidateRect( hwnd, NULL, TRUE );
2507 return 0;
2509 case LB_ADDSTRING16:
2510 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2511 /* fall through */
2512 case LB_ADDSTRING:
2514 INT ret;
2515 LPWSTR textW;
2516 if(unicode || !HAS_STRINGS(descr))
2517 textW = (LPWSTR)lParam;
2518 else
2520 LPSTR textA = (LPSTR)lParam;
2521 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2522 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2523 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2525 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2526 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2527 if (!unicode && HAS_STRINGS(descr))
2528 HeapFree(GetProcessHeap(), 0, textW);
2529 return ret;
2532 case LB_INSERTSTRING16:
2533 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2534 wParam = (INT)(INT16)wParam;
2535 /* fall through */
2536 case LB_INSERTSTRING:
2538 INT ret;
2539 LPWSTR textW;
2540 if(unicode || !HAS_STRINGS(descr))
2541 textW = (LPWSTR)lParam;
2542 else
2544 LPSTR textA = (LPSTR)lParam;
2545 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2546 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2547 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2549 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2550 if(!unicode && HAS_STRINGS(descr))
2551 HeapFree(GetProcessHeap(), 0, textW);
2552 return ret;
2555 case LB_ADDFILE16:
2556 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2557 /* fall through */
2558 case LB_ADDFILE:
2560 INT ret;
2561 LPWSTR textW;
2562 if(unicode || !HAS_STRINGS(descr))
2563 textW = (LPWSTR)lParam;
2564 else
2566 LPSTR textA = (LPSTR)lParam;
2567 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2568 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2569 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2571 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2572 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2573 if(!unicode && HAS_STRINGS(descr))
2574 HeapFree(GetProcessHeap(), 0, textW);
2575 return ret;
2578 case LB_DELETESTRING16:
2579 case LB_DELETESTRING:
2580 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2581 return descr->nb_items;
2582 else
2583 return LB_ERR;
2585 case LB_GETITEMDATA16:
2586 case LB_GETITEMDATA:
2587 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2588 return LB_ERR;
2589 return descr->items[wParam].data;
2591 case LB_SETITEMDATA16:
2592 case LB_SETITEMDATA:
2593 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2594 return LB_ERR;
2595 descr->items[wParam].data = (DWORD)lParam;
2596 return LB_OKAY;
2598 case LB_GETCOUNT16:
2599 case LB_GETCOUNT:
2600 return descr->nb_items;
2602 case LB_GETTEXT16:
2603 lParam = (LPARAM)MapSL(lParam);
2604 /* fall through */
2605 case LB_GETTEXT:
2606 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2608 case LB_GETTEXTLEN16:
2609 /* fall through */
2610 case LB_GETTEXTLEN:
2611 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2612 return LB_ERR;
2613 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2614 if (unicode) return strlenW( descr->items[wParam].str );
2615 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2616 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2618 case LB_GETCURSEL16:
2619 case LB_GETCURSEL:
2620 if (descr->nb_items==0)
2621 return LB_ERR;
2622 if (!IS_MULTISELECT(descr))
2623 return descr->selected_item;
2624 /* else */
2625 if (descr->selected_item!=-1)
2626 return descr->selected_item;
2627 /* else */
2628 return descr->focus_item;
2629 /* otherwise, if the user tries to move the selection with the */
2630 /* arrow keys, we will give the application something to choke on */
2631 case LB_GETTOPINDEX16:
2632 case LB_GETTOPINDEX:
2633 return descr->top_item;
2635 case LB_GETITEMHEIGHT16:
2636 case LB_GETITEMHEIGHT:
2637 return LISTBOX_GetItemHeight( descr, wParam );
2639 case LB_SETITEMHEIGHT16:
2640 lParam = LOWORD(lParam);
2641 /* fall through */
2642 case LB_SETITEMHEIGHT:
2643 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2645 case LB_ITEMFROMPOINT:
2647 POINT pt;
2648 RECT rect;
2650 pt.x = LOWORD(lParam);
2651 pt.y = HIWORD(lParam);
2652 rect.left = 0;
2653 rect.top = 0;
2654 rect.right = descr->width;
2655 rect.bottom = descr->height;
2657 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2658 !PtInRect( &rect, pt ) );
2661 case LB_SETCARETINDEX16:
2662 case LB_SETCARETINDEX:
2663 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2664 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2665 return LB_ERR;
2666 else if (ISWIN31)
2667 return wParam;
2668 else
2669 return LB_OKAY;
2671 case LB_GETCARETINDEX16:
2672 case LB_GETCARETINDEX:
2673 return descr->focus_item;
2675 case LB_SETTOPINDEX16:
2676 case LB_SETTOPINDEX:
2677 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2679 case LB_SETCOLUMNWIDTH16:
2680 case LB_SETCOLUMNWIDTH:
2681 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2683 case LB_GETITEMRECT16:
2685 RECT rect;
2686 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2687 CONV_RECT32TO16( &rect, MapSL(lParam) );
2689 return ret;
2691 case LB_GETITEMRECT:
2692 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2694 case LB_FINDSTRING16:
2695 wParam = (INT)(INT16)wParam;
2696 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2697 /* fall through */
2698 case LB_FINDSTRING:
2700 INT ret;
2701 LPWSTR textW;
2702 if(unicode || !HAS_STRINGS(descr))
2703 textW = (LPWSTR)lParam;
2704 else
2706 LPSTR textA = (LPSTR)lParam;
2707 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2708 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2709 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2711 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2712 if(!unicode && HAS_STRINGS(descr))
2713 HeapFree(GetProcessHeap(), 0, textW);
2714 return ret;
2717 case LB_FINDSTRINGEXACT16:
2718 wParam = (INT)(INT16)wParam;
2719 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2720 /* fall through */
2721 case LB_FINDSTRINGEXACT:
2723 INT ret;
2724 LPWSTR textW;
2725 if(unicode || !HAS_STRINGS(descr))
2726 textW = (LPWSTR)lParam;
2727 else
2729 LPSTR textA = (LPSTR)lParam;
2730 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2731 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2732 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2734 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2735 if(!unicode && HAS_STRINGS(descr))
2736 HeapFree(GetProcessHeap(), 0, textW);
2737 return ret;
2740 case LB_SELECTSTRING16:
2741 wParam = (INT)(INT16)wParam;
2742 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2743 /* fall through */
2744 case LB_SELECTSTRING:
2746 INT index;
2747 LPWSTR textW;
2749 if(HAS_STRINGS(descr))
2750 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2751 debugstr_a((LPSTR)lParam));
2752 if(unicode || !HAS_STRINGS(descr))
2753 textW = (LPWSTR)lParam;
2754 else
2756 LPSTR textA = (LPSTR)lParam;
2757 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2758 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2759 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2761 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2762 if(!unicode && HAS_STRINGS(descr))
2763 HeapFree(GetProcessHeap(), 0, textW);
2764 if (index != LB_ERR)
2766 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2767 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2769 return index;
2772 case LB_GETSEL16:
2773 wParam = (INT)(INT16)wParam;
2774 /* fall through */
2775 case LB_GETSEL:
2776 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2777 return LB_ERR;
2778 return descr->items[wParam].selected;
2780 case LB_SETSEL16:
2781 lParam = (INT)(INT16)lParam;
2782 /* fall through */
2783 case LB_SETSEL:
2784 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2786 case LB_SETCURSEL16:
2787 wParam = (INT)(INT16)wParam;
2788 /* fall through */
2789 case LB_SETCURSEL:
2790 if (IS_MULTISELECT(descr)) return LB_ERR;
2791 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2792 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2794 case LB_GETSELCOUNT16:
2795 case LB_GETSELCOUNT:
2796 return LISTBOX_GetSelCount( descr );
2798 case LB_GETSELITEMS16:
2799 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2801 case LB_GETSELITEMS:
2802 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2804 case LB_SELITEMRANGE16:
2805 case LB_SELITEMRANGE:
2806 if (LOWORD(lParam) <= HIWORD(lParam))
2807 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2808 HIWORD(lParam), wParam );
2809 else
2810 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2811 LOWORD(lParam), wParam );
2813 case LB_SELITEMRANGEEX16:
2814 case LB_SELITEMRANGEEX:
2815 if ((INT)lParam >= (INT)wParam)
2816 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2817 else
2818 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2820 case LB_GETHORIZONTALEXTENT16:
2821 case LB_GETHORIZONTALEXTENT:
2822 return descr->horz_extent;
2824 case LB_SETHORIZONTALEXTENT16:
2825 case LB_SETHORIZONTALEXTENT:
2826 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2828 case LB_GETANCHORINDEX16:
2829 case LB_GETANCHORINDEX:
2830 return descr->anchor_item;
2832 case LB_SETANCHORINDEX16:
2833 wParam = (INT)(INT16)wParam;
2834 /* fall through */
2835 case LB_SETANCHORINDEX:
2836 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2837 return LB_ERR;
2838 descr->anchor_item = (INT)wParam;
2839 return LB_OKAY;
2841 case LB_DIR16:
2842 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2843 * be set automatically (this is different in Win32) */
2844 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2845 lParam = (LPARAM)MapSL(lParam);
2846 /* fall through */
2847 case LB_DIR:
2849 INT ret;
2850 LPWSTR textW;
2851 if(unicode)
2852 textW = (LPWSTR)lParam;
2853 else
2855 LPSTR textA = (LPSTR)lParam;
2856 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2857 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2858 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2860 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2861 if(!unicode)
2862 HeapFree(GetProcessHeap(), 0, textW);
2863 return ret;
2866 case LB_GETLOCALE:
2867 return descr->locale;
2869 case LB_SETLOCALE:
2870 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2871 return LB_OKAY;
2873 case LB_INITSTORAGE:
2874 return LISTBOX_InitStorage( hwnd, descr, wParam );
2876 case LB_SETCOUNT:
2877 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2879 case LB_SETTABSTOPS16:
2880 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2882 case LB_SETTABSTOPS:
2883 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2885 case LB_CARETON16:
2886 case LB_CARETON:
2887 if (descr->caret_on)
2888 return LB_OKAY;
2889 descr->caret_on = TRUE;
2890 if ((descr->focus_item != -1) && (descr->in_focus))
2891 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2892 return LB_OKAY;
2894 case LB_CARETOFF16:
2895 case LB_CARETOFF:
2896 if (!descr->caret_on)
2897 return LB_OKAY;
2898 descr->caret_on = FALSE;
2899 if ((descr->focus_item != -1) && (descr->in_focus))
2900 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2901 return LB_OKAY;
2903 case WM_DESTROY:
2904 return LISTBOX_Destroy( hwnd, descr );
2906 case WM_ENABLE:
2907 InvalidateRect( hwnd, NULL, TRUE );
2908 return 0;
2910 case WM_SETREDRAW:
2911 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2912 return 0;
2914 case WM_GETDLGCODE:
2915 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2917 case WM_PAINT:
2919 PAINTSTRUCT ps;
2920 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2921 ret = LISTBOX_Paint( hwnd, descr, hdc );
2922 if( !wParam ) EndPaint( hwnd, &ps );
2924 return ret;
2925 case WM_SIZE:
2926 LISTBOX_UpdateSize( hwnd, descr );
2927 return 0;
2928 case WM_GETFONT:
2929 return (LRESULT)descr->font;
2930 case WM_SETFONT:
2931 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2932 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2933 return 0;
2934 case WM_SETFOCUS:
2935 descr->in_focus = TRUE;
2936 descr->caret_on = TRUE;
2937 if (descr->focus_item != -1)
2938 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2939 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2940 return 0;
2941 case WM_KILLFOCUS:
2942 descr->in_focus = FALSE;
2943 if ((descr->focus_item != -1) && descr->caret_on)
2944 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2945 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2946 return 0;
2947 case WM_HSCROLL:
2948 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2949 case WM_VSCROLL:
2950 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2951 case WM_MOUSEWHEEL:
2952 if (wParam & (MK_SHIFT | MK_CONTROL))
2953 return DefWindowProcW( hwnd, msg, wParam, lParam );
2954 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2955 case WM_LBUTTONDOWN:
2956 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2957 (INT16)LOWORD(lParam),
2958 (INT16)HIWORD(lParam) );
2959 case WM_LBUTTONDBLCLK:
2960 if (descr->style & LBS_NOTIFY)
2961 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2962 return 0;
2963 case WM_MOUSEMOVE:
2964 if (GetCapture() == hwnd)
2965 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2966 (INT16)HIWORD(lParam) );
2967 return 0;
2968 case WM_LBUTTONUP:
2969 return LISTBOX_HandleLButtonUp( hwnd, descr );
2970 case WM_KEYDOWN:
2971 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2972 case WM_CHAR:
2974 WCHAR charW;
2975 if(unicode)
2976 charW = (WCHAR)wParam;
2977 else
2979 CHAR charA = (CHAR)wParam;
2980 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2982 return LISTBOX_HandleChar( hwnd, descr, charW );
2984 case WM_SYSTIMER:
2985 return LISTBOX_HandleSystemTimer( hwnd, descr );
2986 case WM_ERASEBKGND:
2987 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2989 RECT rect;
2990 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2991 wParam, (LPARAM)hwnd );
2992 TRACE("hbrush = %04x\n", hbrush);
2993 if(!hbrush)
2994 hbrush = GetSysColorBrush(COLOR_WINDOW);
2995 if(hbrush)
2997 GetClientRect(hwnd, &rect);
2998 FillRect((HDC)wParam, &rect, hbrush);
3001 return 1;
3002 case WM_DROPFILES:
3003 if( !descr->lphc )
3004 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3005 SendMessageA( descr->owner, msg, wParam, lParam );
3006 break;
3008 default:
3009 if ((msg >= WM_USER) && (msg < 0xc000))
3010 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3011 hwnd, msg, wParam, lParam );
3012 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3013 DefWindowProcA( hwnd, msg, wParam, lParam );
3015 return 0;
3018 /***********************************************************************
3019 * ListBoxWndProcA
3021 * This is just a wrapper for the real wndproc, it only does window locking
3022 * and unlocking.
3024 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3026 if (!IsWindow(hwnd)) return 0;
3027 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3030 /***********************************************************************
3031 * ListBoxWndProcW
3033 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3035 if (!IsWindow(hwnd)) return 0;
3036 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3039 /***********************************************************************
3040 * ComboLBWndProc_common
3042 * The real combo listbox wndproc
3044 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3045 WPARAM wParam, LPARAM lParam, BOOL unicode )
3047 LRESULT lRet = 0;
3048 LB_DESCR *descr;
3049 LPHEADCOMBO lphc;
3051 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
3053 if (msg == WM_CREATE)
3055 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3056 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3057 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3058 return LISTBOX_Create( hwnd, lphc );
3060 /* Ignore all other messages before we get a WM_CREATE */
3061 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3062 DefWindowProcA( hwnd, msg, wParam, lParam );
3065 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3066 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3068 if ((lphc = descr->lphc) != NULL)
3070 switch( msg )
3072 case WM_MOUSEMOVE:
3073 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3074 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3076 POINT mousePos;
3077 BOOL captured;
3078 RECT clientRect;
3080 mousePos.x = (INT16)LOWORD(lParam);
3081 mousePos.y = (INT16)HIWORD(lParam);
3084 * If we are in a dropdown combobox, we simulate that
3085 * the mouse is captured to show the tracking of the item.
3087 GetClientRect(hwnd, &clientRect);
3089 if (PtInRect( &clientRect, mousePos ))
3091 captured = descr->captured;
3092 descr->captured = TRUE;
3094 LISTBOX_HandleMouseMove( hwnd, descr,
3095 mousePos.x, mousePos.y);
3097 descr->captured = captured;
3100 else
3102 LISTBOX_HandleMouseMove( hwnd, descr,
3103 mousePos.x, mousePos.y);
3106 return 0;
3109 /* else we are in Win3.1 look, go with the default behavior. */
3110 break;
3112 case WM_LBUTTONUP:
3113 if (TWEAK_WineLook > WIN31_LOOK)
3115 POINT mousePos;
3116 RECT clientRect;
3119 * If the mouse button "up" is not in the listbox,
3120 * we make sure there is no selection by re-selecting the
3121 * item that was selected when the listbox was made visible.
3123 mousePos.x = (INT16)LOWORD(lParam);
3124 mousePos.y = (INT16)HIWORD(lParam);
3126 GetClientRect(hwnd, &clientRect);
3129 * When the user clicks outside the combobox and the focus
3130 * is lost, the owning combobox will send a fake buttonup with
3131 * 0xFFFFFFF as the mouse location, we must also revert the
3132 * selection to the original selection.
3134 if ( (lParam == (LPARAM)-1) ||
3135 (!PtInRect( &clientRect, mousePos )) )
3137 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3140 return LISTBOX_HandleLButtonUp( hwnd, descr );
3141 case WM_LBUTTONDBLCLK:
3142 case WM_LBUTTONDOWN:
3143 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3144 (INT16)LOWORD(lParam),
3145 (INT16)HIWORD(lParam) );
3146 case WM_NCACTIVATE:
3147 return FALSE;
3148 case WM_KEYDOWN:
3149 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3151 /* for some reason(?) Windows makes it possible to
3152 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3154 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3155 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3156 && (wParam == VK_DOWN || wParam == VK_UP)) )
3158 COMBO_FlipListbox( lphc, FALSE, FALSE );
3159 return 0;
3162 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3164 case LB_SETCURSEL16:
3165 case LB_SETCURSEL:
3166 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3167 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3168 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3169 return lRet;
3170 case WM_NCDESTROY:
3171 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3172 lphc->hWndLBox = 0;
3173 break;
3177 /* default handling: call listbox wnd proc */
3178 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3179 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3181 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3183 return lRet;
3186 /***********************************************************************
3187 * ComboLBWndProcA
3189 * NOTE: in Windows, winproc address of the ComboLBox is the same
3190 * as that of the Listbox.
3192 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3194 if (!IsWindow(hwnd)) return 0;
3195 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3198 /***********************************************************************
3199 * ComboLBWndProcW
3201 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3203 if (!IsWindow(hwnd)) return 0;
3204 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );