Rewrote the collapsing of . and .. in RtlGetFullPathName_U for better
[wine.git] / controls / listbox.c
blob5c5cca44603f1372e435fc66ed22fc89ee9e2adb
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 <stdarg.h>
24 #include <stdio.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wingdi.h"
28 #include "wine/winuser16.h"
29 #include "wine/winbase16.h"
30 #include "wownt32.h"
31 #include "wine/unicode.h"
32 #include "winuser.h"
33 #include "winerror.h"
34 #include "message.h"
35 #include "user.h"
36 #include "controls.h"
37 #include "wine/debug.h"
38 #include "win.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
41 WINE_DECLARE_DEBUG_CHANNEL(combo);
43 /* Unimplemented yet:
44 * - LBS_USETABSTOPS
45 * - Locale handling
47 * Probably needs improvement:
48 * - LBS_NOSEL
51 /* Items array granularity */
52 #define LB_ARRAY_GRANULARITY 16
54 /* Scrolling timeout in ms */
55 #define LB_SCROLL_TIMEOUT 50
57 /* Listbox system timer id */
58 #define LB_TIMER_ID 2
60 /* flag listbox changed while setredraw false - internal style */
61 #define LBS_DISPLAYCHANGED 0x80000000
63 /* Item structure */
64 typedef struct
66 LPWSTR str; /* Item text */
67 BOOL selected; /* Is item selected? */
68 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
69 DWORD data; /* User data */
70 } LB_ITEMDATA;
72 /* Listbox structure */
73 typedef struct
75 HWND owner; /* Owner window to send notifications to */
76 UINT style; /* Window style */
77 INT width; /* Window width */
78 INT height; /* Window height */
79 LB_ITEMDATA *items; /* Array of items */
80 INT nb_items; /* Number of items */
81 INT top_item; /* Top visible item */
82 INT selected_item; /* Selected item */
83 INT focus_item; /* Item that has the focus */
84 INT anchor_item; /* Anchor item for extended selection */
85 INT item_height; /* Default item height */
86 INT page_size; /* Items per listbox page */
87 INT column_width; /* Column width for multi-column listboxes */
88 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
89 INT horz_pos; /* Horizontal position */
90 INT nb_tabs; /* Number of tabs in array */
91 INT *tabs; /* Array of tabs */
92 BOOL caret_on; /* Is caret on? */
93 BOOL captured; /* Is mouse captured? */
94 BOOL in_focus;
95 HFONT font; /* Current font */
96 LCID locale; /* Current locale for string comparisons */
97 LPHEADCOMBO lphc; /* ComboLBox */
98 } LB_DESCR;
101 #define IS_OWNERDRAW(descr) \
102 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
104 #define HAS_STRINGS(descr) \
105 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
108 #define IS_MULTISELECT(descr) \
109 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
111 #define SEND_NOTIFICATION(hwnd,descr,code) \
112 (SendMessageW( (descr)->owner, WM_COMMAND, \
113 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
115 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
117 /* Current timer status */
118 typedef enum
120 LB_TIMER_NONE,
121 LB_TIMER_UP,
122 LB_TIMER_LEFT,
123 LB_TIMER_DOWN,
124 LB_TIMER_RIGHT
125 } TIMER_DIRECTION;
127 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
129 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
131 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
134 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
136 /*********************************************************************
137 * listbox class descriptor
139 const struct builtin_class_descr LISTBOX_builtin_class =
141 "ListBox", /* name */
142 CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
143 ListBoxWndProcA, /* procA */
144 ListBoxWndProcW, /* procW */
145 sizeof(LB_DESCR *), /* extra */
146 IDC_ARROW, /* cursor */
147 0 /* brush */
151 /*********************************************************************
152 * combolbox class descriptor
154 const struct builtin_class_descr COMBOLBOX_builtin_class =
156 "ComboLBox", /* name */
157 CS_DBLCLKS | CS_SAVEBITS, /* style */
158 ComboLBWndProcA, /* procA */
159 ComboLBWndProcW, /* procW */
160 sizeof(LB_DESCR *), /* extra */
161 IDC_ARROW, /* cursor */
162 0 /* brush */
166 /* check whether app is a Win 3.1 app */
167 inline static BOOL is_old_app( HWND hwnd )
169 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
173 /***********************************************************************
174 * LISTBOX_Dump
176 void LISTBOX_Dump( HWND hwnd )
178 INT i;
179 LB_ITEMDATA *item;
180 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
182 TRACE( "Listbox:\n" );
183 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
184 hwnd, (UINT)descr, descr->nb_items, descr->top_item );
185 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
187 TRACE( "%4d: %-40s %d %08lx %3d\n",
188 i, debugstr_w(item->str), item->selected, item->data, item->height );
193 /***********************************************************************
194 * LISTBOX_GetCurrentPageSize
196 * Return the current page size
198 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
200 INT i, height;
201 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
202 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
204 if ((height += descr->items[i].height) > descr->height) break;
206 if (i == descr->top_item) return 1;
207 else return i - descr->top_item;
211 /***********************************************************************
212 * LISTBOX_GetMaxTopIndex
214 * Return the maximum possible index for the top of the listbox.
216 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
218 INT max, page;
220 if (descr->style & LBS_OWNERDRAWVARIABLE)
222 page = descr->height;
223 for (max = descr->nb_items - 1; max >= 0; max--)
224 if ((page -= descr->items[max].height) < 0) break;
225 if (max < descr->nb_items - 1) max++;
227 else if (descr->style & LBS_MULTICOLUMN)
229 if ((page = descr->width / descr->column_width) < 1) page = 1;
230 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
231 max = (max - page) * descr->page_size;
233 else
235 max = descr->nb_items - descr->page_size;
237 if (max < 0) max = 0;
238 return max;
242 /***********************************************************************
243 * LISTBOX_UpdateScroll
245 * Update the scrollbars. Should be called whenever the content
246 * of the listbox changes.
248 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
250 SCROLLINFO info;
252 /* Check the listbox scroll bar flags individually before we call
253 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
254 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
255 scroll bar when we do not need one.
256 if (!(descr->style & WS_VSCROLL)) return;
259 /* It is important that we check descr->style, and not wnd->dwStyle,
260 for WS_VSCROLL, as the former is exactly the one passed in
261 argument to CreateWindow.
262 In Windows (and from now on in Wine :) a listbox created
263 with such a style (no WS_SCROLL) does not update
264 the scrollbar with listbox-related data, thus letting
265 the programmer use it for his/her own purposes. */
267 if (descr->style & LBS_NOREDRAW) return;
268 info.cbSize = sizeof(info);
270 if (descr->style & LBS_MULTICOLUMN)
272 info.nMin = 0;
273 info.nMax = (descr->nb_items - 1) / descr->page_size;
274 info.nPos = descr->top_item / descr->page_size;
275 info.nPage = descr->width / descr->column_width;
276 if (info.nPage < 1) info.nPage = 1;
277 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
278 if (descr->style & LBS_DISABLENOSCROLL)
279 info.fMask |= SIF_DISABLENOSCROLL;
280 if (descr->style & WS_HSCROLL)
281 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
282 info.nMax = 0;
283 info.fMask = SIF_RANGE;
284 if (descr->style & WS_VSCROLL)
285 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
287 else
289 info.nMin = 0;
290 info.nMax = descr->nb_items - 1;
291 info.nPos = descr->top_item;
292 info.nPage = LISTBOX_GetCurrentPageSize( descr );
293 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
294 if (descr->style & LBS_DISABLENOSCROLL)
295 info.fMask |= SIF_DISABLENOSCROLL;
296 if (descr->style & WS_VSCROLL)
297 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
299 if (descr->horz_extent)
301 info.nMin = 0;
302 info.nMax = descr->horz_extent - 1;
303 info.nPos = descr->horz_pos;
304 info.nPage = descr->width;
305 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
306 if (descr->style & LBS_DISABLENOSCROLL)
307 info.fMask |= SIF_DISABLENOSCROLL;
308 if (descr->style & WS_HSCROLL)
309 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
315 /***********************************************************************
316 * LISTBOX_SetTopItem
318 * Set the top item of the listbox, scrolling up or down if necessary.
320 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
321 BOOL scroll )
323 INT max = LISTBOX_GetMaxTopIndex( descr );
324 if (index > max) index = max;
325 if (index < 0) index = 0;
326 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
327 if (descr->top_item == index) return LB_OKAY;
328 if (descr->style & LBS_MULTICOLUMN)
330 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
331 if (scroll && (abs(diff) < descr->width))
332 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
333 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
335 else
336 scroll = FALSE;
338 else if (scroll)
340 INT diff;
341 if (descr->style & LBS_OWNERDRAWVARIABLE)
343 INT i;
344 diff = 0;
345 if (index > descr->top_item)
347 for (i = index - 1; i >= descr->top_item; i--)
348 diff -= descr->items[i].height;
350 else
352 for (i = index; i < descr->top_item; i++)
353 diff += descr->items[i].height;
356 else
357 diff = (descr->top_item - index) * descr->item_height;
359 if (abs(diff) < descr->height)
360 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
361 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
362 else
363 scroll = FALSE;
365 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
366 descr->top_item = index;
367 LISTBOX_UpdateScroll( hwnd, descr );
368 return LB_OKAY;
372 /***********************************************************************
373 * LISTBOX_UpdatePage
375 * Update the page size. Should be called when the size of
376 * the client area or the item height changes.
378 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
380 INT page_size;
382 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
383 page_size = 1;
384 if (page_size == descr->page_size) return;
385 descr->page_size = page_size;
386 if (descr->style & LBS_MULTICOLUMN)
387 InvalidateRect( hwnd, NULL, TRUE );
388 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
392 /***********************************************************************
393 * LISTBOX_UpdateSize
395 * Update the size of the listbox. Should be called when the size of
396 * the client area changes.
398 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
400 RECT rect;
402 GetClientRect( hwnd, &rect );
403 descr->width = rect.right - rect.left;
404 descr->height = rect.bottom - rect.top;
405 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
407 INT remaining;
408 RECT rect;
410 GetWindowRect( hwnd, &rect );
411 if(descr->item_height != 0)
412 remaining = descr->height % descr->item_height;
413 else
414 remaining = 0;
415 if ((descr->height > descr->item_height) && remaining)
417 if (is_old_app(hwnd))
418 { /* give a margin for error to 16 bits programs - if we need
419 less than the height of the nonclient area, round to the
420 *next* number of items */
421 int ncheight = rect.bottom - rect.top - descr->height;
422 if ((descr->item_height - remaining) <= ncheight)
423 remaining = remaining - descr->item_height;
425 TRACE("[%p]: changing height %d -> %d\n",
426 hwnd, descr->height, descr->height - remaining );
427 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
428 rect.bottom - rect.top - remaining,
429 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
430 return;
433 TRACE("[%p]: new size = %d,%d\n", hwnd, descr->width, descr->height );
434 LISTBOX_UpdatePage( hwnd, descr );
435 LISTBOX_UpdateScroll( hwnd, descr );
437 /* Invalidate the focused item so it will be repainted correctly */
438 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
440 InvalidateRect( hwnd, &rect, FALSE );
445 /***********************************************************************
446 * LISTBOX_GetItemRect
448 * Get the rectangle enclosing an item, in listbox client coordinates.
449 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
451 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
453 /* Index <= 0 is legal even on empty listboxes */
454 if (index && (index >= descr->nb_items)) return -1;
455 SetRect( rect, 0, 0, descr->width, descr->height );
456 if (descr->style & LBS_MULTICOLUMN)
458 INT col = (index / descr->page_size) -
459 (descr->top_item / descr->page_size);
460 rect->left += col * descr->column_width;
461 rect->right = rect->left + descr->column_width;
462 rect->top += (index % descr->page_size) * descr->item_height;
463 rect->bottom = rect->top + descr->item_height;
465 else if (descr->style & LBS_OWNERDRAWVARIABLE)
467 INT i;
468 rect->right += descr->horz_pos;
469 if ((index >= 0) && (index < descr->nb_items))
471 if (index < descr->top_item)
473 for (i = descr->top_item-1; i >= index; i--)
474 rect->top -= descr->items[i].height;
476 else
478 for (i = descr->top_item; i < index; i++)
479 rect->top += descr->items[i].height;
481 rect->bottom = rect->top + descr->items[index].height;
485 else
487 rect->top += (index - descr->top_item) * descr->item_height;
488 rect->bottom = rect->top + descr->item_height;
489 rect->right += descr->horz_pos;
492 return ((rect->left < descr->width) && (rect->right > 0) &&
493 (rect->top < descr->height) && (rect->bottom > 0));
497 /***********************************************************************
498 * LISTBOX_GetItemFromPoint
500 * Return the item nearest from point (x,y) (in client coordinates).
502 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
504 INT index = descr->top_item;
506 if (!descr->nb_items) return -1; /* No items */
507 if (descr->style & LBS_OWNERDRAWVARIABLE)
509 INT pos = 0;
510 if (y >= 0)
512 while (index < descr->nb_items)
514 if ((pos += descr->items[index].height) > y) break;
515 index++;
518 else
520 while (index > 0)
522 index--;
523 if ((pos -= descr->items[index].height) <= y) break;
527 else if (descr->style & LBS_MULTICOLUMN)
529 if (y >= descr->item_height * descr->page_size) return -1;
530 if (y >= 0) index += y / descr->item_height;
531 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
532 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
534 else
536 index += (y / descr->item_height);
538 if (index < 0) return 0;
539 if (index >= descr->nb_items) return -1;
540 return index;
544 /***********************************************************************
545 * LISTBOX_PaintItem
547 * Paint an item.
549 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
550 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
552 LB_ITEMDATA *item = NULL;
553 if (index < descr->nb_items) item = &descr->items[index];
555 if (IS_OWNERDRAW(descr))
557 DRAWITEMSTRUCT dis;
558 RECT r;
559 HRGN hrgn;
560 UINT id = GetWindowLongA( hwnd, GWL_ID );
562 if (!item)
564 if (action == ODA_FOCUS)
565 DrawFocusRect( hdc, rect );
566 else
567 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
568 return;
571 /* some programs mess with the clipping region when
572 drawing the item, *and* restore the previous region
573 after they are done, so a region has better to exist
574 else everything ends clipped */
575 GetClientRect(hwnd, &r);
576 hrgn = CreateRectRgnIndirect(&r);
577 SelectClipRgn( hdc, hrgn);
578 DeleteObject( hrgn );
580 dis.CtlType = ODT_LISTBOX;
581 dis.CtlID = id;
582 dis.hwndItem = hwnd;
583 dis.itemAction = action;
584 dis.hDC = hdc;
585 dis.itemID = index;
586 dis.itemState = 0;
587 if (item && item->selected) dis.itemState |= ODS_SELECTED;
588 if (!ignoreFocus && (descr->focus_item == index) &&
589 (descr->caret_on) &&
590 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
591 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
592 dis.itemData = item ? item->data : 0;
593 dis.rcItem = *rect;
594 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
595 hwnd, index, item ? debugstr_w(item->str) : "", action,
596 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
597 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
599 else
601 COLORREF oldText = 0, oldBk = 0;
603 if (action == ODA_FOCUS)
605 DrawFocusRect( hdc, rect );
606 return;
608 if (item && item->selected)
610 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
611 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
614 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
615 hwnd, index, item ? debugstr_w(item->str) : "", action,
616 rect->left, rect->top, rect->right, rect->bottom );
617 if (!item)
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
620 else if (!(descr->style & LBS_USETABSTOPS))
621 ExtTextOutW( hdc, rect->left + 1, rect->top,
622 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
623 strlenW(item->str), NULL );
624 else
626 /* Output empty string to paint background in the full width. */
627 ExtTextOutW( hdc, rect->left + 1, rect->top,
628 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
629 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
630 item->str, strlenW(item->str),
631 descr->nb_tabs, descr->tabs, 0);
633 if (item && item->selected)
635 SetBkColor( hdc, oldBk );
636 SetTextColor( hdc, oldText );
638 if (!ignoreFocus && (descr->focus_item == index) &&
639 (descr->caret_on) &&
640 (descr->in_focus)) DrawFocusRect( hdc, rect );
645 /***********************************************************************
646 * LISTBOX_SetRedraw
648 * Change the redraw flag.
650 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
652 if (on)
654 if (!(descr->style & LBS_NOREDRAW)) return;
655 descr->style &= ~LBS_NOREDRAW;
656 if (descr->style & LBS_DISPLAYCHANGED)
657 { /* page was changed while setredraw false, refresh automatically */
658 InvalidateRect(hwnd, NULL, TRUE);
659 if ((descr->top_item + descr->page_size) > descr->nb_items)
660 { /* reset top of page if less than number of items/page */
661 descr->top_item = descr->nb_items - descr->page_size;
662 if (descr->top_item < 0) descr->top_item = 0;
664 descr->style &= ~LBS_DISPLAYCHANGED;
666 LISTBOX_UpdateScroll( hwnd, descr );
668 else descr->style |= LBS_NOREDRAW;
672 /***********************************************************************
673 * LISTBOX_RepaintItem
675 * Repaint a single item synchronously.
677 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
678 UINT action )
680 HDC hdc;
681 RECT rect;
682 HFONT oldFont = 0;
683 HBRUSH hbrush, oldBrush = 0;
685 /* Do not repaint the item if the item is not visible */
686 if (!IsWindowVisible(hwnd)) return;
687 if (descr->style & LBS_NOREDRAW)
689 descr->style |= LBS_DISPLAYCHANGED;
690 return;
692 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
693 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
694 if (descr->font) oldFont = SelectObject( hdc, descr->font );
695 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
696 (WPARAM)hdc, (LPARAM)hwnd );
697 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
698 if (!IsWindowEnabled(hwnd))
699 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
700 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
701 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
702 if (oldFont) SelectObject( hdc, oldFont );
703 if (oldBrush) SelectObject( hdc, oldBrush );
704 ReleaseDC( hwnd, hdc );
708 /***********************************************************************
709 * LISTBOX_InitStorage
711 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
713 LB_ITEMDATA *item;
715 nb_items += LB_ARRAY_GRANULARITY - 1;
716 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
717 if (descr->items) {
718 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
719 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
720 nb_items * sizeof(LB_ITEMDATA));
722 else {
723 item = HeapAlloc( GetProcessHeap(), 0,
724 nb_items * sizeof(LB_ITEMDATA));
727 if (!item)
729 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
730 return LB_ERRSPACE;
732 descr->items = item;
733 return LB_OKAY;
737 /***********************************************************************
738 * LISTBOX_SetTabStops
740 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
741 LPINT tabs, BOOL short_ints )
743 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
744 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
745 if (!(descr->nb_tabs = count))
747 descr->tabs = NULL;
748 return TRUE;
750 /* FIXME: count = 1 */
751 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
752 descr->nb_tabs * sizeof(INT) )))
753 return FALSE;
754 if (short_ints)
756 INT i;
757 LPINT16 p = (LPINT16)tabs;
759 TRACE("[%p]: settabstops ", hwnd );
760 for (i = 0; i < descr->nb_tabs; i++) {
761 descr->tabs[i] = *p++<<1; /* FIXME */
762 if (TRACE_ON(listbox)) TRACE("%hd ", descr->tabs[i]);
764 if (TRACE_ON(listbox)) TRACE("\n");
766 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
767 /* FIXME: repaint the window? */
768 return TRUE;
772 /***********************************************************************
773 * LISTBOX_GetText
775 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
777 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
778 if (HAS_STRINGS(descr))
780 if (!lParam)
781 return strlenW(descr->items[index].str);
783 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
785 if(unicode)
787 LPWSTR buffer = (LPWSTR)lParam;
788 strcpyW( buffer, descr->items[index].str );
789 return strlenW(buffer);
791 else
793 LPSTR buffer = (LPSTR)lParam;
794 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
796 } else {
797 if (lParam)
798 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
799 return sizeof(DWORD);
804 /***********************************************************************
805 * LISTBOX_FindStringPos
807 * Find the nearest string located before a given string in sort order.
808 * If 'exact' is TRUE, return an error if we don't get an exact match.
810 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
811 BOOL exact )
813 INT index, min, max, res = -1;
815 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
816 min = 0;
817 max = descr->nb_items;
818 while (min != max)
820 index = (min + max) / 2;
821 if (HAS_STRINGS(descr))
822 res = lstrcmpiW( str, descr->items[index].str);
823 else
825 COMPAREITEMSTRUCT cis;
826 UINT id = GetWindowLongA( hwnd, GWL_ID );
828 cis.CtlType = ODT_LISTBOX;
829 cis.CtlID = id;
830 cis.hwndItem = hwnd;
831 /* note that some application (MetaStock) expects the second item
832 * to be in the listbox */
833 cis.itemID1 = -1;
834 cis.itemData1 = (DWORD)str;
835 cis.itemID2 = index;
836 cis.itemData2 = descr->items[index].data;
837 cis.dwLocaleId = descr->locale;
838 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
840 if (!res) return index;
841 if (res < 0) max = index;
842 else min = index + 1;
844 return exact ? -1 : max;
848 /***********************************************************************
849 * LISTBOX_FindFileStrPos
851 * Find the nearest string located before a given string in directory
852 * sort order (i.e. first files, then directories, then drives).
854 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
856 INT min, max, res = -1;
858 if (!HAS_STRINGS(descr))
859 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
860 min = 0;
861 max = descr->nb_items;
862 while (min != max)
864 INT index = (min + max) / 2;
865 LPCWSTR p = descr->items[index].str;
866 if (*p == '[') /* drive or directory */
868 if (*str != '[') res = -1;
869 else if (p[1] == '-') /* drive */
871 if (str[1] == '-') res = str[2] - p[2];
872 else res = -1;
874 else /* directory */
876 if (str[1] == '-') res = 1;
877 else res = lstrcmpiW( str, p );
880 else /* filename */
882 if (*str == '[') res = 1;
883 else res = lstrcmpiW( str, p );
885 if (!res) return index;
886 if (res < 0) max = index;
887 else min = index + 1;
889 return max;
893 /***********************************************************************
894 * LISTBOX_FindString
896 * Find the item beginning with a given string.
898 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
899 LPCWSTR str, BOOL exact )
901 INT i;
902 LB_ITEMDATA *item;
904 if (start >= descr->nb_items) start = -1;
905 item = descr->items + start + 1;
906 if (HAS_STRINGS(descr))
908 if (!str || ! str[0] ) return LB_ERR;
909 if (exact)
911 for (i = start + 1; i < descr->nb_items; i++, item++)
912 if (!lstrcmpiW( str, item->str )) return i;
913 for (i = 0, item = descr->items; i <= start; i++, item++)
914 if (!lstrcmpiW( str, item->str )) return i;
916 else
918 /* Special case for drives and directories: ignore prefix */
919 #define CHECK_DRIVE(item) \
920 if ((item)->str[0] == '[') \
922 if (!strncmpiW( str, (item)->str+1, len )) return i; \
923 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
924 return i; \
927 INT len = strlenW(str);
928 for (i = start + 1; i < descr->nb_items; i++, item++)
930 if (!strncmpiW( str, item->str, len )) return i;
931 CHECK_DRIVE(item);
933 for (i = 0, item = descr->items; i <= start; i++, item++)
935 if (!strncmpiW( str, item->str, len )) return i;
936 CHECK_DRIVE(item);
938 #undef CHECK_DRIVE
941 else
943 if (exact && (descr->style & LBS_SORT))
944 /* If sorted, use a WM_COMPAREITEM binary search */
945 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
947 /* Otherwise use a linear search */
948 for (i = start + 1; i < descr->nb_items; i++, item++)
949 if (item->data == (DWORD)str) return i;
950 for (i = 0, item = descr->items; i <= start; i++, item++)
951 if (item->data == (DWORD)str) return i;
953 return LB_ERR;
957 /***********************************************************************
958 * LISTBOX_GetSelCount
960 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
962 INT i, count;
963 LB_ITEMDATA *item = descr->items;
965 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
966 for (i = count = 0; i < descr->nb_items; i++, item++)
967 if (item->selected) count++;
968 return count;
972 /***********************************************************************
973 * LISTBOX_GetSelItems16
975 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
977 INT i, count;
978 LB_ITEMDATA *item = descr->items;
980 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
981 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
982 if (item->selected) array[count++] = (INT16)i;
983 return count;
987 /***********************************************************************
988 * LISTBOX_GetSelItems
990 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
992 INT i, count;
993 LB_ITEMDATA *item = descr->items;
995 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
996 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
997 if (item->selected) array[count++] = i;
998 return count;
1002 /***********************************************************************
1003 * LISTBOX_Paint
1005 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
1007 INT i, col_pos = descr->page_size - 1;
1008 RECT rect;
1009 RECT focusRect = {-1, -1, -1, -1};
1010 HFONT oldFont = 0;
1011 HBRUSH hbrush, oldBrush = 0;
1013 if (descr->style & LBS_NOREDRAW) return 0;
1015 SetRect( &rect, 0, 0, descr->width, descr->height );
1016 if (descr->style & LBS_MULTICOLUMN)
1017 rect.right = rect.left + descr->column_width;
1018 else if (descr->horz_pos)
1020 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1021 rect.right += descr->horz_pos;
1024 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1025 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1026 (WPARAM)hdc, (LPARAM)hwnd );
1027 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1028 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1030 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1031 (descr->in_focus))
1033 /* Special case for empty listbox: paint focus rect */
1034 rect.bottom = rect.top + descr->item_height;
1035 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1036 ODA_FOCUS, FALSE );
1037 rect.top = rect.bottom;
1040 /* Paint all the item, regarding the selection
1041 Focus state will be painted after */
1043 for (i = descr->top_item; i < descr->nb_items; i++)
1045 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1046 rect.bottom = rect.top + descr->item_height;
1047 else
1048 rect.bottom = rect.top + descr->items[i].height;
1050 if (i == descr->focus_item)
1052 /* keep the focus rect, to paint the focus item after */
1053 focusRect.left = rect.left;
1054 focusRect.right = rect.right;
1055 focusRect.top = rect.top;
1056 focusRect.bottom = rect.bottom;
1058 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1059 rect.top = rect.bottom;
1061 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1063 if (!IS_OWNERDRAW(descr))
1065 /* Clear the bottom of the column */
1066 if (rect.top < descr->height)
1068 rect.bottom = descr->height;
1069 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1070 &rect, NULL, 0, NULL );
1074 /* Go to the next column */
1075 rect.left += descr->column_width;
1076 rect.right += descr->column_width;
1077 rect.top = 0;
1078 col_pos = descr->page_size - 1;
1080 else
1082 col_pos--;
1083 if (rect.top >= descr->height) break;
1087 /* Paint the focus item now */
1088 if (focusRect.top != focusRect.bottom && descr->caret_on)
1089 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1091 if (!IS_OWNERDRAW(descr))
1093 /* Clear the remainder of the client area */
1094 if (rect.top < descr->height)
1096 rect.bottom = descr->height;
1097 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1098 &rect, NULL, 0, NULL );
1100 if (rect.right < descr->width)
1102 rect.left = rect.right;
1103 rect.right = descr->width;
1104 rect.top = 0;
1105 rect.bottom = descr->height;
1106 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1107 &rect, NULL, 0, NULL );
1110 if (oldFont) SelectObject( hdc, oldFont );
1111 if (oldBrush) SelectObject( hdc, oldBrush );
1112 return 0;
1116 /***********************************************************************
1117 * LISTBOX_InvalidateItems
1119 * Invalidate all items from a given item. If the specified item is not
1120 * visible, nothing happens.
1122 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1124 RECT rect;
1126 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1128 if (descr->style & LBS_NOREDRAW)
1130 descr->style |= LBS_DISPLAYCHANGED;
1131 return;
1133 rect.bottom = descr->height;
1134 InvalidateRect( hwnd, &rect, TRUE );
1135 if (descr->style & LBS_MULTICOLUMN)
1137 /* Repaint the other columns */
1138 rect.left = rect.right;
1139 rect.right = descr->width;
1140 rect.top = 0;
1141 InvalidateRect( hwnd, &rect, TRUE );
1146 static void LISTBOX_InvalidateItemRect( HWND hwnd, LB_DESCR *descr, INT index )
1148 RECT rect;
1150 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1151 InvalidateRect( hwnd, &rect, TRUE );
1154 /***********************************************************************
1155 * LISTBOX_GetItemHeight
1157 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1159 if (descr->style & LBS_OWNERDRAWVARIABLE)
1161 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1162 return descr->items[index].height;
1164 else return descr->item_height;
1168 /***********************************************************************
1169 * LISTBOX_SetItemHeight
1171 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1172 INT height, BOOL repaint )
1174 if (!height) height = 1;
1176 if (descr->style & LBS_OWNERDRAWVARIABLE)
1178 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1179 TRACE("[%p]: item %d height = %d\n", hwnd, index, height );
1180 descr->items[index].height = height;
1181 LISTBOX_UpdateScroll( hwnd, descr );
1182 if (repaint)
1183 LISTBOX_InvalidateItems( hwnd, descr, index );
1185 else if (height != descr->item_height)
1187 TRACE("[%p]: new height = %d\n", hwnd, height );
1188 descr->item_height = height;
1189 LISTBOX_UpdatePage( hwnd, descr );
1190 LISTBOX_UpdateScroll( hwnd, descr );
1191 if (repaint)
1192 InvalidateRect( hwnd, 0, TRUE );
1194 return LB_OKAY;
1198 /***********************************************************************
1199 * LISTBOX_SetHorizontalPos
1201 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1203 INT diff;
1205 if (pos > descr->horz_extent - descr->width)
1206 pos = descr->horz_extent - descr->width;
1207 if (pos < 0) pos = 0;
1208 if (!(diff = descr->horz_pos - pos)) return;
1209 TRACE("[%p]: new horz pos = %d\n", hwnd, pos );
1210 descr->horz_pos = pos;
1211 LISTBOX_UpdateScroll( hwnd, descr );
1212 if (abs(diff) < descr->width)
1213 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1214 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1215 else
1216 InvalidateRect( hwnd, NULL, TRUE );
1220 /***********************************************************************
1221 * LISTBOX_SetHorizontalExtent
1223 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1224 INT extent )
1226 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1227 return LB_OKAY;
1228 if (extent <= 0) extent = 1;
1229 if (extent == descr->horz_extent) return LB_OKAY;
1230 TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
1231 descr->horz_extent = extent;
1232 if (descr->horz_pos > extent - descr->width)
1233 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1234 else
1235 LISTBOX_UpdateScroll( hwnd, descr );
1236 return LB_OKAY;
1240 /***********************************************************************
1241 * LISTBOX_SetColumnWidth
1243 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1245 if (width == descr->column_width) return LB_OKAY;
1246 TRACE("[%p]: new column width = %d\n", hwnd, width );
1247 descr->column_width = width;
1248 LISTBOX_UpdatePage( hwnd, descr );
1249 return LB_OKAY;
1253 /***********************************************************************
1254 * LISTBOX_SetFont
1256 * Returns the item height.
1258 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1260 HDC hdc;
1261 HFONT oldFont = 0;
1262 TEXTMETRICW tm;
1264 descr->font = font;
1266 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1268 ERR("unable to get DC.\n" );
1269 return 16;
1271 if (font) oldFont = SelectObject( hdc, font );
1272 GetTextMetricsW( hdc, &tm );
1273 if (oldFont) SelectObject( hdc, oldFont );
1274 ReleaseDC( hwnd, hdc );
1275 if (!IS_OWNERDRAW(descr))
1276 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1277 return tm.tmHeight ;
1281 /***********************************************************************
1282 * LISTBOX_MakeItemVisible
1284 * Make sure that a given item is partially or fully visible.
1286 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1287 BOOL fully )
1289 INT top;
1291 if (index <= descr->top_item) top = index;
1292 else if (descr->style & LBS_MULTICOLUMN)
1294 INT cols = descr->width;
1295 if (!fully) cols += descr->column_width - 1;
1296 if (cols >= descr->column_width) cols /= descr->column_width;
1297 else cols = 1;
1298 if (index < descr->top_item + (descr->page_size * cols)) return;
1299 top = index - descr->page_size * (cols - 1);
1301 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1303 INT height = fully ? descr->items[index].height : 1;
1304 for (top = index; top > descr->top_item; top--)
1305 if ((height += descr->items[top-1].height) > descr->height) break;
1307 else
1309 if (index < descr->top_item + descr->page_size) return;
1310 if (!fully && (index == descr->top_item + descr->page_size) &&
1311 (descr->height > (descr->page_size * descr->item_height))) return;
1312 top = index - descr->page_size + 1;
1314 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1317 /***********************************************************************
1318 * LISTBOX_SetCaretIndex
1320 * NOTES
1321 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1324 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1325 BOOL fully_visible )
1327 INT oldfocus = descr->focus_item;
1329 if (descr->style & LBS_NOSEL) return LB_ERR;
1330 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1331 if (index == oldfocus) return LB_OKAY;
1332 descr->focus_item = index;
1333 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1334 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1336 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1337 if (descr->caret_on && (descr->in_focus))
1338 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1340 return LB_OKAY;
1344 /***********************************************************************
1345 * LISTBOX_SelectItemRange
1347 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1349 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1350 INT last, BOOL on )
1352 INT i;
1354 /* A few sanity checks */
1356 if (descr->style & LBS_NOSEL) return LB_ERR;
1357 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1358 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1359 if (last == -1) last = descr->nb_items - 1;
1360 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1361 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1362 /* selected_item reflects last selected/unselected item on multiple sel */
1363 descr->selected_item = last;
1365 if (on) /* Turn selection on */
1367 for (i = first; i <= last; i++)
1369 if (descr->items[i].selected) continue;
1370 descr->items[i].selected = TRUE;
1371 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1373 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1375 else /* Turn selection off */
1377 for (i = first; i <= last; i++)
1379 if (!descr->items[i].selected) continue;
1380 descr->items[i].selected = FALSE;
1381 LISTBOX_InvalidateItemRect(hwnd, descr, i);
1384 return LB_OKAY;
1387 /***********************************************************************
1388 * LISTBOX_SetSelection
1390 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1391 BOOL on, BOOL send_notify )
1393 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1395 if (descr->style & LBS_NOSEL) return LB_ERR;
1396 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1397 if (descr->style & LBS_MULTIPLESEL)
1399 if (index == -1) /* Select all items */
1400 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1401 else /* Only one item */
1402 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1404 else
1406 INT oldsel = descr->selected_item;
1407 if (index == oldsel) return LB_OKAY;
1408 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1409 if (index != -1) descr->items[index].selected = TRUE;
1410 descr->selected_item = index;
1411 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1412 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1413 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1414 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1415 else
1416 if( descr->lphc ) /* set selection change flag for parent combo */
1417 descr->lphc->wState |= CBF_SELCHANGE;
1419 return LB_OKAY;
1423 /***********************************************************************
1424 * LISTBOX_MoveCaret
1426 * Change the caret position and extend the selection to the new caret.
1428 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1429 BOOL fully_visible )
1431 INT oldfocus = descr->focus_item;
1433 if ((index < 0) || (index >= descr->nb_items))
1434 return;
1436 /* Important, repaint needs to be done in this order if
1437 you want to mimic Windows behavior:
1438 1. Remove the focus and paint the item
1439 2. Remove the selection and paint the item(s)
1440 3. Set the selection and repaint the item(s)
1441 4. Set the focus to 'index' and repaint the item */
1443 /* 1. remove the focus and repaint the item */
1444 descr->focus_item = -1;
1445 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1446 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1448 /* 2. then turn off the previous selection */
1449 /* 3. repaint the new selected item */
1450 if (descr->style & LBS_EXTENDEDSEL)
1452 if (descr->anchor_item != -1)
1454 INT first = min( index, descr->anchor_item );
1455 INT last = max( index, descr->anchor_item );
1456 if (first > 0)
1457 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1458 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1459 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1462 else if (!(descr->style & LBS_MULTIPLESEL))
1464 /* Set selection to new caret item */
1465 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1468 /* 4. repaint the new item with the focus */
1469 descr->focus_item = index;
1470 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1471 if (descr->caret_on && (descr->in_focus))
1472 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1476 /***********************************************************************
1477 * LISTBOX_InsertItem
1479 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1480 LPWSTR str, DWORD data )
1482 LB_ITEMDATA *item;
1483 INT max_items;
1484 INT oldfocus = descr->focus_item;
1486 if (index == -1) index = descr->nb_items;
1487 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1488 if (!descr->items) max_items = 0;
1489 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1490 if (descr->nb_items == max_items)
1492 /* We need to grow the array */
1493 max_items += LB_ARRAY_GRANULARITY;
1494 if (descr->items)
1495 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1496 max_items * sizeof(LB_ITEMDATA) );
1497 else
1498 item = HeapAlloc( GetProcessHeap(), 0,
1499 max_items * sizeof(LB_ITEMDATA) );
1500 if (!item)
1502 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1503 return LB_ERRSPACE;
1505 descr->items = item;
1508 /* Insert the item structure */
1510 item = &descr->items[index];
1511 if (index < descr->nb_items)
1512 RtlMoveMemory( item + 1, item,
1513 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1514 item->str = str;
1515 item->data = data;
1516 item->height = 0;
1517 item->selected = FALSE;
1518 descr->nb_items++;
1520 /* Get item height */
1522 if (descr->style & LBS_OWNERDRAWVARIABLE)
1524 MEASUREITEMSTRUCT mis;
1525 UINT id = GetWindowLongA( hwnd, GWL_ID );
1527 mis.CtlType = ODT_LISTBOX;
1528 mis.CtlID = id;
1529 mis.itemID = index;
1530 mis.itemData = descr->items[index].data;
1531 mis.itemHeight = descr->item_height;
1532 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1533 item->height = mis.itemHeight ? mis.itemHeight : 1;
1534 TRACE("[%p]: measure item %d (%s) = %d\n",
1535 hwnd, index, str ? debugstr_w(str) : "", item->height );
1538 /* Repaint the items */
1540 LISTBOX_UpdateScroll( hwnd, descr );
1541 LISTBOX_InvalidateItems( hwnd, descr, index );
1543 /* Move selection and focused item */
1544 /* If listbox was empty, set focus to the first item */
1545 if (descr->nb_items == 1)
1546 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1547 /* single select don't change selection index in win31 */
1548 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1550 descr->selected_item++;
1551 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1553 else
1555 if (index <= descr->selected_item)
1557 descr->selected_item++;
1558 descr->focus_item = oldfocus; /* focus not changed */
1561 return LB_OKAY;
1565 /***********************************************************************
1566 * LISTBOX_InsertString
1568 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1569 LPCWSTR str )
1571 LPWSTR new_str = NULL;
1572 DWORD data = 0;
1573 LRESULT ret;
1575 if (HAS_STRINGS(descr))
1577 static const WCHAR empty_stringW[] = { 0 };
1578 if (!str) str = empty_stringW;
1579 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1581 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1582 return LB_ERRSPACE;
1584 strcpyW(new_str, str);
1586 else data = (DWORD)str;
1588 if (index == -1) index = descr->nb_items;
1589 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1591 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1592 return ret;
1595 TRACE("[%p]: added item %d %s\n",
1596 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1597 return index;
1601 /***********************************************************************
1602 * LISTBOX_DeleteItem
1604 * Delete the content of an item. 'index' must be a valid index.
1606 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1608 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1609 * while Win95 sends it for all items with user data.
1610 * It's probably better to send it too often than not
1611 * often enough, so this is what we do here.
1613 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1615 DELETEITEMSTRUCT dis;
1616 UINT id = GetWindowLongA( hwnd, GWL_ID );
1618 dis.CtlType = ODT_LISTBOX;
1619 dis.CtlID = id;
1620 dis.itemID = index;
1621 dis.hwndItem = hwnd;
1622 dis.itemData = descr->items[index].data;
1623 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1625 if (HAS_STRINGS(descr) && descr->items[index].str)
1626 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1630 /***********************************************************************
1631 * LISTBOX_RemoveItem
1633 * Remove an item from the listbox and delete its content.
1635 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1637 LB_ITEMDATA *item;
1638 INT max_items;
1640 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1641 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1643 /* We need to invalidate the original rect instead of the updated one. */
1644 LISTBOX_InvalidateItems( hwnd, descr, index );
1646 LISTBOX_DeleteItem( hwnd, descr, index );
1648 /* Remove the item */
1650 item = &descr->items[index];
1651 if (index < descr->nb_items-1)
1652 RtlMoveMemory( item, item + 1,
1653 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1654 descr->nb_items--;
1655 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1657 /* Shrink the item array if possible */
1659 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1660 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1662 max_items -= LB_ARRAY_GRANULARITY;
1663 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1664 max_items * sizeof(LB_ITEMDATA) );
1665 if (item) descr->items = item;
1667 /* Repaint the items */
1669 LISTBOX_UpdateScroll( hwnd, descr );
1670 /* if we removed the scrollbar, reset the top of the list
1671 (correct for owner-drawn ???) */
1672 if (descr->nb_items == descr->page_size)
1673 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1675 /* Move selection and focused item */
1676 if (!IS_MULTISELECT(descr))
1678 if (index == descr->selected_item)
1679 descr->selected_item = -1;
1680 else if (index < descr->selected_item)
1682 descr->selected_item--;
1683 if (ISWIN31) /* win 31 do not change the selected item number */
1684 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1688 if (descr->focus_item >= descr->nb_items)
1690 descr->focus_item = descr->nb_items - 1;
1691 if (descr->focus_item < 0) descr->focus_item = 0;
1693 return LB_OKAY;
1697 /***********************************************************************
1698 * LISTBOX_ResetContent
1700 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1702 INT i;
1704 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1705 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1706 descr->nb_items = 0;
1707 descr->top_item = 0;
1708 descr->selected_item = -1;
1709 descr->focus_item = 0;
1710 descr->anchor_item = -1;
1711 descr->items = NULL;
1715 /***********************************************************************
1716 * LISTBOX_SetCount
1718 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1720 LRESULT ret;
1722 if (HAS_STRINGS(descr)) return LB_ERR;
1723 /* FIXME: this is far from optimal... */
1724 if (count > descr->nb_items)
1726 while (count > descr->nb_items)
1727 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1728 return ret;
1730 else if (count < descr->nb_items)
1732 while (count < descr->nb_items)
1733 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1734 return ret;
1736 return LB_OKAY;
1740 /***********************************************************************
1741 * LISTBOX_Directory
1743 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1744 LPCWSTR filespec, BOOL long_names )
1746 HANDLE handle;
1747 LRESULT ret = LB_OKAY;
1748 WIN32_FIND_DATAW entry;
1749 int pos;
1751 /* don't scan directory if we just want drives exclusively */
1752 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1753 /* scan directory */
1754 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1756 int le = GetLastError();
1757 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1759 else
1763 WCHAR buffer[270];
1764 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1766 static const WCHAR bracketW[] = { ']',0 };
1767 static const WCHAR dotW[] = { '.',0 };
1768 if (!(attrib & DDL_DIRECTORY) ||
1769 !strcmpW( entry.cFileName, dotW )) continue;
1770 buffer[0] = '[';
1771 if (!long_names && entry.cAlternateFileName[0])
1772 strcpyW( buffer + 1, entry.cAlternateFileName );
1773 else
1774 strcpyW( buffer + 1, entry.cFileName );
1775 strcatW(buffer, bracketW);
1777 else /* not a directory */
1779 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1780 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1782 if ((attrib & DDL_EXCLUSIVE) &&
1783 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1784 continue;
1785 #undef ATTRIBS
1786 if (!long_names && entry.cAlternateFileName[0])
1787 strcpyW( buffer + 1, entry.cAlternateFileName );
1788 else
1789 strcpyW( buffer + 1, entry.cFileName );
1791 if (!long_names) CharLowerW( buffer );
1792 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1793 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1794 break;
1795 } while (FindNextFileW( handle, &entry ));
1796 FindClose( handle );
1800 /* scan drives */
1801 if ((ret >= 0) && (attrib & DDL_DRIVES))
1803 WCHAR buffer[] = {'[','-','a','-',']',0};
1804 WCHAR root[] = {'A',':','\\',0};
1805 int drive;
1806 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1808 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1809 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1810 break;
1813 return ret;
1817 /***********************************************************************
1818 * LISTBOX_HandleVScroll
1820 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1822 SCROLLINFO info;
1824 if (descr->style & LBS_MULTICOLUMN) return 0;
1825 switch(LOWORD(wParam))
1827 case SB_LINEUP:
1828 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1829 break;
1830 case SB_LINEDOWN:
1831 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1832 break;
1833 case SB_PAGEUP:
1834 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1835 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1836 break;
1837 case SB_PAGEDOWN:
1838 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1839 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1840 break;
1841 case SB_THUMBPOSITION:
1842 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1843 break;
1844 case SB_THUMBTRACK:
1845 info.cbSize = sizeof(info);
1846 info.fMask = SIF_TRACKPOS;
1847 GetScrollInfo( hwnd, SB_VERT, &info );
1848 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1849 break;
1850 case SB_TOP:
1851 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1852 break;
1853 case SB_BOTTOM:
1854 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1855 break;
1857 return 0;
1861 /***********************************************************************
1862 * LISTBOX_HandleHScroll
1864 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1866 SCROLLINFO info;
1867 INT page;
1869 if (descr->style & LBS_MULTICOLUMN)
1871 switch(LOWORD(wParam))
1873 case SB_LINELEFT:
1874 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1875 TRUE );
1876 break;
1877 case SB_LINERIGHT:
1878 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1879 TRUE );
1880 break;
1881 case SB_PAGELEFT:
1882 page = descr->width / descr->column_width;
1883 if (page < 1) page = 1;
1884 LISTBOX_SetTopItem( hwnd, descr,
1885 descr->top_item - page * descr->page_size, TRUE );
1886 break;
1887 case SB_PAGERIGHT:
1888 page = descr->width / descr->column_width;
1889 if (page < 1) page = 1;
1890 LISTBOX_SetTopItem( hwnd, descr,
1891 descr->top_item + page * descr->page_size, TRUE );
1892 break;
1893 case SB_THUMBPOSITION:
1894 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1895 TRUE );
1896 break;
1897 case SB_THUMBTRACK:
1898 info.cbSize = sizeof(info);
1899 info.fMask = SIF_TRACKPOS;
1900 GetScrollInfo( hwnd, SB_VERT, &info );
1901 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1902 TRUE );
1903 break;
1904 case SB_LEFT:
1905 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1906 break;
1907 case SB_RIGHT:
1908 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1909 break;
1912 else if (descr->horz_extent)
1914 switch(LOWORD(wParam))
1916 case SB_LINELEFT:
1917 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1918 break;
1919 case SB_LINERIGHT:
1920 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1921 break;
1922 case SB_PAGELEFT:
1923 LISTBOX_SetHorizontalPos( hwnd, descr,
1924 descr->horz_pos - descr->width );
1925 break;
1926 case SB_PAGERIGHT:
1927 LISTBOX_SetHorizontalPos( hwnd, descr,
1928 descr->horz_pos + descr->width );
1929 break;
1930 case SB_THUMBPOSITION:
1931 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1932 break;
1933 case SB_THUMBTRACK:
1934 info.cbSize = sizeof(info);
1935 info.fMask = SIF_TRACKPOS;
1936 GetScrollInfo( hwnd, SB_HORZ, &info );
1937 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1938 break;
1939 case SB_LEFT:
1940 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1941 break;
1942 case SB_RIGHT:
1943 LISTBOX_SetHorizontalPos( hwnd, descr,
1944 descr->horz_extent - descr->width );
1945 break;
1948 return 0;
1951 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1953 short gcWheelDelta = 0;
1954 UINT pulScrollLines = 3;
1956 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1958 gcWheelDelta -= (short) HIWORD(wParam);
1960 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1962 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1963 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1964 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1966 return 0;
1969 /***********************************************************************
1970 * LISTBOX_HandleLButtonDown
1972 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1973 WPARAM wParam, INT x, INT y )
1975 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1976 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1977 if (!descr->caret_on && (descr->in_focus)) return 0;
1979 if (!descr->in_focus)
1981 if( !descr->lphc ) SetFocus( hwnd );
1982 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1985 if (index == -1) return 0;
1987 if (descr->style & LBS_EXTENDEDSEL)
1989 /* we should perhaps make sure that all items are deselected
1990 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1991 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1992 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1995 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1996 if (wParam & MK_CONTROL)
1998 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1999 LISTBOX_SetSelection( hwnd, descr, index,
2000 !descr->items[index].selected,
2001 (descr->style & LBS_NOTIFY) != 0);
2003 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2005 else
2007 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2008 LISTBOX_SetSelection( hwnd, descr, index,
2009 (!(descr->style & LBS_MULTIPLESEL) ||
2010 !descr->items[index].selected),
2011 (descr->style & LBS_NOTIFY) != 0 );
2014 descr->captured = TRUE;
2015 SetCapture( hwnd );
2017 if (!descr->lphc)
2019 if (descr->style & LBS_NOTIFY )
2020 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
2021 MAKELPARAM( x, y ) );
2022 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
2024 POINT pt;
2026 pt.x = x;
2027 pt.y = y;
2029 if (DragDetect( hwnd, pt ))
2030 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2033 return 0;
2037 /*************************************************************************
2038 * LISTBOX_HandleLButtonDownCombo [Internal]
2040 * Process LButtonDown message for the ComboListBox
2042 nn * PARAMS
2043 * pWnd [I] The windows internal structure
2044 * pDescr [I] The ListBox internal structure
2045 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2046 * x [I] X Mouse Coordinate
2047 * y [I] Y Mouse Coordinate
2049 * RETURNS
2050 * 0 since we are processing the WM_LBUTTONDOWN Message
2052 * NOTES
2053 * This function is only to be used when a ListBox is a ComboListBox
2056 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2057 UINT msg, WPARAM wParam, INT x, INT y)
2059 RECT clientRect, screenRect;
2060 POINT mousePos;
2062 mousePos.x = x;
2063 mousePos.y = y;
2065 GetClientRect(hwnd, &clientRect);
2067 if(PtInRect(&clientRect, mousePos))
2069 /* MousePos is in client, resume normal processing */
2070 if (msg == WM_LBUTTONDOWN)
2072 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2073 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2075 else if (pDescr->style & LBS_NOTIFY)
2076 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2077 return 0;
2079 else
2081 POINT screenMousePos;
2082 HWND hWndOldCapture;
2084 /* Check the Non-Client Area */
2085 screenMousePos = mousePos;
2086 hWndOldCapture = GetCapture();
2087 ReleaseCapture();
2088 GetWindowRect(hwnd, &screenRect);
2089 ClientToScreen(hwnd, &screenMousePos);
2091 if(!PtInRect(&screenRect, screenMousePos))
2093 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2094 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2095 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2096 return 0;
2098 else
2100 /* Check to see the NC is a scrollbar */
2101 INT nHitTestType=0;
2102 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2103 /* Check Vertical scroll bar */
2104 if (style & WS_VSCROLL)
2106 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2107 if (PtInRect( &clientRect, mousePos ))
2109 nHitTestType = HTVSCROLL;
2112 /* Check horizontal scroll bar */
2113 if (style & WS_HSCROLL)
2115 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2116 if (PtInRect( &clientRect, mousePos ))
2118 nHitTestType = HTHSCROLL;
2121 /* Windows sends this message when a scrollbar is clicked
2124 if(nHitTestType != 0)
2126 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2127 MAKELONG(screenMousePos.x, screenMousePos.y));
2129 /* Resume the Capture after scrolling is complete
2131 if(hWndOldCapture != 0)
2133 SetCapture(hWndOldCapture);
2137 return 0;
2140 /***********************************************************************
2141 * LISTBOX_HandleLButtonUp
2143 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2145 if (LISTBOX_Timer != LB_TIMER_NONE)
2146 KillSystemTimer( hwnd, LB_TIMER_ID );
2147 LISTBOX_Timer = LB_TIMER_NONE;
2148 if (descr->captured)
2150 descr->captured = FALSE;
2151 if (GetCapture() == hwnd) ReleaseCapture();
2152 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2153 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2155 return 0;
2159 /***********************************************************************
2160 * LISTBOX_HandleTimer
2162 * Handle scrolling upon a timer event.
2163 * Return TRUE if scrolling should continue.
2165 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2166 INT index, TIMER_DIRECTION dir )
2168 switch(dir)
2170 case LB_TIMER_UP:
2171 if (descr->top_item) index = descr->top_item - 1;
2172 else index = 0;
2173 break;
2174 case LB_TIMER_LEFT:
2175 if (descr->top_item) index -= descr->page_size;
2176 break;
2177 case LB_TIMER_DOWN:
2178 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2179 if (index == descr->focus_item) index++;
2180 if (index >= descr->nb_items) index = descr->nb_items - 1;
2181 break;
2182 case LB_TIMER_RIGHT:
2183 if (index + descr->page_size < descr->nb_items)
2184 index += descr->page_size;
2185 break;
2186 case LB_TIMER_NONE:
2187 break;
2189 if (index == descr->focus_item) return FALSE;
2190 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2191 return TRUE;
2195 /***********************************************************************
2196 * LISTBOX_HandleSystemTimer
2198 * WM_SYSTIMER handler.
2200 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2202 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2204 KillSystemTimer( hwnd, LB_TIMER_ID );
2205 LISTBOX_Timer = LB_TIMER_NONE;
2207 return 0;
2211 /***********************************************************************
2212 * LISTBOX_HandleMouseMove
2214 * WM_MOUSEMOVE handler.
2216 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2217 INT x, INT y )
2219 INT index;
2220 TIMER_DIRECTION dir = LB_TIMER_NONE;
2222 if (!descr->captured) return;
2224 if (descr->style & LBS_MULTICOLUMN)
2226 if (y < 0) y = 0;
2227 else if (y >= descr->item_height * descr->page_size)
2228 y = descr->item_height * descr->page_size - 1;
2230 if (x < 0)
2232 dir = LB_TIMER_LEFT;
2233 x = 0;
2235 else if (x >= descr->width)
2237 dir = LB_TIMER_RIGHT;
2238 x = descr->width - 1;
2241 else
2243 if (y < 0) dir = LB_TIMER_UP; /* above */
2244 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2247 index = LISTBOX_GetItemFromPoint( descr, x, y );
2248 if (index == -1) index = descr->focus_item;
2249 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2251 /* Start/stop the system timer */
2253 if (dir != LB_TIMER_NONE)
2254 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2255 else if (LISTBOX_Timer != LB_TIMER_NONE)
2256 KillSystemTimer( hwnd, LB_TIMER_ID );
2257 LISTBOX_Timer = dir;
2261 /***********************************************************************
2262 * LISTBOX_HandleKeyDown
2264 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2266 INT caret = -1;
2267 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2268 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2269 bForceSelection = FALSE; /* only for single select list */
2271 if (descr->style & LBS_WANTKEYBOARDINPUT)
2273 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2274 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2275 (LPARAM)hwnd );
2276 if (caret == -2) return 0;
2278 if (caret == -1) switch(wParam)
2280 case VK_LEFT:
2281 if (descr->style & LBS_MULTICOLUMN)
2283 bForceSelection = FALSE;
2284 if (descr->focus_item >= descr->page_size)
2285 caret = descr->focus_item - descr->page_size;
2286 break;
2288 /* fall through */
2289 case VK_UP:
2290 caret = descr->focus_item - 1;
2291 if (caret < 0) caret = 0;
2292 break;
2293 case VK_RIGHT:
2294 if (descr->style & LBS_MULTICOLUMN)
2296 bForceSelection = FALSE;
2297 if (descr->focus_item + descr->page_size < descr->nb_items)
2298 caret = descr->focus_item + descr->page_size;
2299 break;
2301 /* fall through */
2302 case VK_DOWN:
2303 caret = descr->focus_item + 1;
2304 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2305 break;
2307 case VK_PRIOR:
2308 if (descr->style & LBS_MULTICOLUMN)
2310 INT page = descr->width / descr->column_width;
2311 if (page < 1) page = 1;
2312 caret = descr->focus_item - (page * descr->page_size) + 1;
2314 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2315 if (caret < 0) caret = 0;
2316 break;
2317 case VK_NEXT:
2318 if (descr->style & LBS_MULTICOLUMN)
2320 INT page = descr->width / descr->column_width;
2321 if (page < 1) page = 1;
2322 caret = descr->focus_item + (page * descr->page_size) - 1;
2324 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2325 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2326 break;
2327 case VK_HOME:
2328 caret = 0;
2329 break;
2330 case VK_END:
2331 caret = descr->nb_items - 1;
2332 break;
2333 case VK_SPACE:
2334 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2335 else if (descr->style & LBS_MULTIPLESEL)
2337 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2338 !descr->items[descr->focus_item].selected,
2339 (descr->style & LBS_NOTIFY) != 0 );
2341 break;
2342 default:
2343 bForceSelection = FALSE;
2345 if (bForceSelection) /* focused item is used instead of key */
2346 caret = descr->focus_item;
2347 if (caret >= 0)
2349 if ((descr->style & LBS_EXTENDEDSEL) &&
2350 !(GetKeyState( VK_SHIFT ) & 0x8000))
2351 descr->anchor_item = caret;
2352 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2353 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2354 if (descr->style & LBS_NOTIFY)
2356 if( descr->lphc )
2358 /* make sure that combo parent doesn't hide us */
2359 descr->lphc->wState |= CBF_NOROLLUP;
2361 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2364 return 0;
2368 /***********************************************************************
2369 * LISTBOX_HandleChar
2371 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2373 INT caret = -1;
2374 WCHAR str[2];
2376 str[0] = charW;
2377 str[1] = '\0';
2379 if (descr->style & LBS_WANTKEYBOARDINPUT)
2381 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2382 MAKEWPARAM(charW, descr->focus_item),
2383 (LPARAM)hwnd );
2384 if (caret == -2) return 0;
2386 if (caret == -1)
2387 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2388 if (caret != -1)
2390 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2391 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2392 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2393 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2394 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2396 return 0;
2400 /***********************************************************************
2401 * LISTBOX_Create
2403 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2405 LB_DESCR *descr;
2406 MEASUREITEMSTRUCT mis;
2407 RECT rect;
2409 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2410 return FALSE;
2412 GetClientRect( hwnd, &rect );
2413 descr->owner = GetParent( hwnd );
2414 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2415 descr->width = rect.right - rect.left;
2416 descr->height = rect.bottom - rect.top;
2417 descr->items = NULL;
2418 descr->nb_items = 0;
2419 descr->top_item = 0;
2420 descr->selected_item = -1;
2421 descr->focus_item = 0;
2422 descr->anchor_item = -1;
2423 descr->item_height = 1;
2424 descr->page_size = 1;
2425 descr->column_width = 150;
2426 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2427 descr->horz_pos = 0;
2428 descr->nb_tabs = 0;
2429 descr->tabs = NULL;
2430 descr->caret_on = lphc ? FALSE : TRUE;
2431 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2432 descr->in_focus = FALSE;
2433 descr->captured = FALSE;
2434 descr->font = 0;
2435 descr->locale = 0; /* FIXME */
2436 descr->lphc = lphc;
2438 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2440 /* Win95 document "List Box Differences" from MSDN:
2441 If a list box in a version 3.x application has either the
2442 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2443 horizontal and vertical scroll bars.
2445 descr->style |= WS_VSCROLL | WS_HSCROLL;
2448 if( lphc )
2450 TRACE_(combo)("[%p]: resetting owner %p -> %p\n", hwnd, descr->owner, lphc->self );
2451 descr->owner = lphc->self;
2454 SetWindowLongA( hwnd, 0, (LONG)descr );
2456 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2458 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2459 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2460 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2461 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2463 if (descr->style & LBS_OWNERDRAWFIXED)
2465 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2467 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2468 descr->item_height = lphc->fixedOwnerDrawHeight;
2470 else
2472 UINT id = GetWindowLongA( hwnd, GWL_ID );
2473 mis.CtlType = ODT_LISTBOX;
2474 mis.CtlID = id;
2475 mis.itemID = -1;
2476 mis.itemWidth = 0;
2477 mis.itemData = 0;
2478 mis.itemHeight = descr->item_height;
2479 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2480 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2484 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2485 return TRUE;
2489 /***********************************************************************
2490 * LISTBOX_Destroy
2492 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2494 LISTBOX_ResetContent( hwnd, descr );
2495 SetWindowLongA( hwnd, 0, 0 );
2496 HeapFree( GetProcessHeap(), 0, descr );
2497 return TRUE;
2501 /***********************************************************************
2502 * ListBoxWndProc_common
2504 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2505 WPARAM wParam, LPARAM lParam, BOOL unicode )
2507 LRESULT ret;
2508 LB_DESCR *descr;
2510 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2512 if (msg == WM_CREATE)
2514 if (!LISTBOX_Create( hwnd, NULL ))
2515 return -1;
2516 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2517 return 0;
2519 /* Ignore all other messages before we get a WM_CREATE */
2520 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2521 DefWindowProcA( hwnd, msg, wParam, lParam );
2524 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2525 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2526 switch(msg)
2528 case LB_RESETCONTENT16:
2529 case LB_RESETCONTENT:
2530 LISTBOX_ResetContent( hwnd, descr );
2531 LISTBOX_UpdateScroll( hwnd, descr );
2532 InvalidateRect( hwnd, NULL, TRUE );
2533 return 0;
2535 case LB_ADDSTRING16:
2536 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2537 /* fall through */
2538 case LB_ADDSTRING:
2540 INT ret;
2541 LPWSTR textW;
2542 if(unicode || !HAS_STRINGS(descr))
2543 textW = (LPWSTR)lParam;
2544 else
2546 LPSTR textA = (LPSTR)lParam;
2547 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2548 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2549 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2551 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2552 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2553 if (!unicode && HAS_STRINGS(descr))
2554 HeapFree(GetProcessHeap(), 0, textW);
2555 return ret;
2558 case LB_INSERTSTRING16:
2559 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2560 wParam = (INT)(INT16)wParam;
2561 /* fall through */
2562 case LB_INSERTSTRING:
2564 INT ret;
2565 LPWSTR textW;
2566 if(unicode || !HAS_STRINGS(descr))
2567 textW = (LPWSTR)lParam;
2568 else
2570 LPSTR textA = (LPSTR)lParam;
2571 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2572 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2573 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2575 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2576 if(!unicode && HAS_STRINGS(descr))
2577 HeapFree(GetProcessHeap(), 0, textW);
2578 return ret;
2581 case LB_ADDFILE16:
2582 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2583 /* fall through */
2584 case LB_ADDFILE:
2586 INT ret;
2587 LPWSTR textW;
2588 if(unicode || !HAS_STRINGS(descr))
2589 textW = (LPWSTR)lParam;
2590 else
2592 LPSTR textA = (LPSTR)lParam;
2593 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2594 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2595 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2597 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2598 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2599 if(!unicode && HAS_STRINGS(descr))
2600 HeapFree(GetProcessHeap(), 0, textW);
2601 return ret;
2604 case LB_DELETESTRING16:
2605 case LB_DELETESTRING:
2606 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2607 return descr->nb_items;
2608 else
2609 return LB_ERR;
2611 case LB_GETITEMDATA16:
2612 case LB_GETITEMDATA:
2613 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2614 return LB_ERR;
2615 return descr->items[wParam].data;
2617 case LB_SETITEMDATA16:
2618 case LB_SETITEMDATA:
2619 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2620 return LB_ERR;
2621 descr->items[wParam].data = (DWORD)lParam;
2622 return LB_OKAY;
2624 case LB_GETCOUNT16:
2625 case LB_GETCOUNT:
2626 return descr->nb_items;
2628 case LB_GETTEXT16:
2629 lParam = (LPARAM)MapSL(lParam);
2630 /* fall through */
2631 case LB_GETTEXT:
2632 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2634 case LB_GETTEXTLEN16:
2635 /* fall through */
2636 case LB_GETTEXTLEN:
2637 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2638 return LB_ERR;
2639 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2640 if (unicode) return strlenW( descr->items[wParam].str );
2641 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2642 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2644 case LB_GETCURSEL16:
2645 case LB_GETCURSEL:
2646 if (descr->nb_items==0)
2647 return LB_ERR;
2648 if (!IS_MULTISELECT(descr))
2649 return descr->selected_item;
2650 /* else */
2651 if (descr->selected_item!=-1)
2652 return descr->selected_item;
2653 /* else */
2654 return descr->focus_item;
2655 /* otherwise, if the user tries to move the selection with the */
2656 /* arrow keys, we will give the application something to choke on */
2657 case LB_GETTOPINDEX16:
2658 case LB_GETTOPINDEX:
2659 return descr->top_item;
2661 case LB_GETITEMHEIGHT16:
2662 case LB_GETITEMHEIGHT:
2663 return LISTBOX_GetItemHeight( descr, wParam );
2665 case LB_SETITEMHEIGHT16:
2666 lParam = LOWORD(lParam);
2667 /* fall through */
2668 case LB_SETITEMHEIGHT:
2669 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2671 case LB_ITEMFROMPOINT:
2673 POINT pt;
2674 RECT rect;
2676 pt.x = LOWORD(lParam);
2677 pt.y = HIWORD(lParam);
2678 rect.left = 0;
2679 rect.top = 0;
2680 rect.right = descr->width;
2681 rect.bottom = descr->height;
2683 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2684 !PtInRect( &rect, pt ) );
2687 case LB_SETCARETINDEX16:
2688 case LB_SETCARETINDEX:
2689 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2690 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2691 return LB_ERR;
2692 else if (ISWIN31)
2693 return wParam;
2694 else
2695 return LB_OKAY;
2697 case LB_GETCARETINDEX16:
2698 case LB_GETCARETINDEX:
2699 return descr->focus_item;
2701 case LB_SETTOPINDEX16:
2702 case LB_SETTOPINDEX:
2703 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2705 case LB_SETCOLUMNWIDTH16:
2706 case LB_SETCOLUMNWIDTH:
2707 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2709 case LB_GETITEMRECT16:
2711 RECT rect;
2712 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2713 CONV_RECT32TO16( &rect, MapSL(lParam) );
2715 return ret;
2717 case LB_GETITEMRECT:
2718 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2720 case LB_FINDSTRING16:
2721 wParam = (INT)(INT16)wParam;
2722 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2723 /* fall through */
2724 case LB_FINDSTRING:
2726 INT ret;
2727 LPWSTR textW;
2728 if(unicode || !HAS_STRINGS(descr))
2729 textW = (LPWSTR)lParam;
2730 else
2732 LPSTR textA = (LPSTR)lParam;
2733 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2734 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2735 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2737 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2738 if(!unicode && HAS_STRINGS(descr))
2739 HeapFree(GetProcessHeap(), 0, textW);
2740 return ret;
2743 case LB_FINDSTRINGEXACT16:
2744 wParam = (INT)(INT16)wParam;
2745 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2746 /* fall through */
2747 case LB_FINDSTRINGEXACT:
2749 INT ret;
2750 LPWSTR textW;
2751 if(unicode || !HAS_STRINGS(descr))
2752 textW = (LPWSTR)lParam;
2753 else
2755 LPSTR textA = (LPSTR)lParam;
2756 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2757 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2758 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2760 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2761 if(!unicode && HAS_STRINGS(descr))
2762 HeapFree(GetProcessHeap(), 0, textW);
2763 return ret;
2766 case LB_SELECTSTRING16:
2767 wParam = (INT)(INT16)wParam;
2768 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2769 /* fall through */
2770 case LB_SELECTSTRING:
2772 INT index;
2773 LPWSTR textW;
2775 if(HAS_STRINGS(descr))
2776 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2777 debugstr_a((LPSTR)lParam));
2778 if(unicode || !HAS_STRINGS(descr))
2779 textW = (LPWSTR)lParam;
2780 else
2782 LPSTR textA = (LPSTR)lParam;
2783 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2784 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2785 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2787 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2788 if(!unicode && HAS_STRINGS(descr))
2789 HeapFree(GetProcessHeap(), 0, textW);
2790 if (index != LB_ERR)
2792 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2793 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2795 return index;
2798 case LB_GETSEL16:
2799 wParam = (INT)(INT16)wParam;
2800 /* fall through */
2801 case LB_GETSEL:
2802 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2803 return LB_ERR;
2804 return descr->items[wParam].selected;
2806 case LB_SETSEL16:
2807 lParam = (INT)(INT16)lParam;
2808 /* fall through */
2809 case LB_SETSEL:
2810 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2812 case LB_SETCURSEL16:
2813 wParam = (INT)(INT16)wParam;
2814 /* fall through */
2815 case LB_SETCURSEL:
2816 if (IS_MULTISELECT(descr)) return LB_ERR;
2817 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2818 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2820 case LB_GETSELCOUNT16:
2821 case LB_GETSELCOUNT:
2822 return LISTBOX_GetSelCount( descr );
2824 case LB_GETSELITEMS16:
2825 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2827 case LB_GETSELITEMS:
2828 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2830 case LB_SELITEMRANGE16:
2831 case LB_SELITEMRANGE:
2832 if (LOWORD(lParam) <= HIWORD(lParam))
2833 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2834 HIWORD(lParam), wParam );
2835 else
2836 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2837 LOWORD(lParam), wParam );
2839 case LB_SELITEMRANGEEX16:
2840 case LB_SELITEMRANGEEX:
2841 if ((INT)lParam >= (INT)wParam)
2842 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2843 else
2844 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2846 case LB_GETHORIZONTALEXTENT16:
2847 case LB_GETHORIZONTALEXTENT:
2848 return descr->horz_extent;
2850 case LB_SETHORIZONTALEXTENT16:
2851 case LB_SETHORIZONTALEXTENT:
2852 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2854 case LB_GETANCHORINDEX16:
2855 case LB_GETANCHORINDEX:
2856 return descr->anchor_item;
2858 case LB_SETANCHORINDEX16:
2859 wParam = (INT)(INT16)wParam;
2860 /* fall through */
2861 case LB_SETANCHORINDEX:
2862 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2863 return LB_ERR;
2864 descr->anchor_item = (INT)wParam;
2865 return LB_OKAY;
2867 case LB_DIR16:
2868 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2869 * be set automatically (this is different in Win32) */
2870 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2871 lParam = (LPARAM)MapSL(lParam);
2872 /* fall through */
2873 case LB_DIR:
2875 INT ret;
2876 LPWSTR textW;
2877 if(unicode)
2878 textW = (LPWSTR)lParam;
2879 else
2881 LPSTR textA = (LPSTR)lParam;
2882 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2883 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2884 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2886 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2887 if(!unicode)
2888 HeapFree(GetProcessHeap(), 0, textW);
2889 return ret;
2892 case LB_GETLOCALE:
2893 return descr->locale;
2895 case LB_SETLOCALE:
2896 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2897 return LB_OKAY;
2899 case LB_INITSTORAGE:
2900 return LISTBOX_InitStorage( hwnd, descr, wParam );
2902 case LB_SETCOUNT:
2903 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2905 case LB_SETTABSTOPS16:
2906 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2908 case LB_SETTABSTOPS:
2909 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2911 case LB_CARETON16:
2912 case LB_CARETON:
2913 if (descr->caret_on)
2914 return LB_OKAY;
2915 descr->caret_on = TRUE;
2916 if ((descr->focus_item != -1) && (descr->in_focus))
2917 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2918 return LB_OKAY;
2920 case LB_CARETOFF16:
2921 case LB_CARETOFF:
2922 if (!descr->caret_on)
2923 return LB_OKAY;
2924 descr->caret_on = FALSE;
2925 if ((descr->focus_item != -1) && (descr->in_focus))
2926 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2927 return LB_OKAY;
2929 case WM_DESTROY:
2930 return LISTBOX_Destroy( hwnd, descr );
2932 case WM_ENABLE:
2933 InvalidateRect( hwnd, NULL, TRUE );
2934 return 0;
2936 case WM_SETREDRAW:
2937 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2938 return 0;
2940 case WM_GETDLGCODE:
2941 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2943 case WM_PAINT:
2945 PAINTSTRUCT ps;
2946 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2947 ret = LISTBOX_Paint( hwnd, descr, hdc );
2948 if( !wParam ) EndPaint( hwnd, &ps );
2950 return ret;
2951 case WM_SIZE:
2952 LISTBOX_UpdateSize( hwnd, descr );
2953 return 0;
2954 case WM_GETFONT:
2955 return (LRESULT)descr->font;
2956 case WM_SETFONT:
2957 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2958 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2959 return 0;
2960 case WM_SETFOCUS:
2961 descr->in_focus = TRUE;
2962 descr->caret_on = TRUE;
2963 if (descr->focus_item != -1)
2964 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2965 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2966 return 0;
2967 case WM_KILLFOCUS:
2968 descr->in_focus = FALSE;
2969 if ((descr->focus_item != -1) && descr->caret_on)
2970 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2971 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2972 return 0;
2973 case WM_HSCROLL:
2974 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2975 case WM_VSCROLL:
2976 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2977 case WM_MOUSEWHEEL:
2978 if (wParam & (MK_SHIFT | MK_CONTROL))
2979 return DefWindowProcW( hwnd, msg, wParam, lParam );
2980 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2981 case WM_LBUTTONDOWN:
2982 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2983 (INT16)LOWORD(lParam),
2984 (INT16)HIWORD(lParam) );
2985 case WM_LBUTTONDBLCLK:
2986 if (descr->style & LBS_NOTIFY)
2987 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2988 return 0;
2989 case WM_MOUSEMOVE:
2990 if (GetCapture() == hwnd)
2991 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2992 (INT16)HIWORD(lParam) );
2993 return 0;
2994 case WM_LBUTTONUP:
2995 return LISTBOX_HandleLButtonUp( hwnd, descr );
2996 case WM_KEYDOWN:
2997 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2998 case WM_CHAR:
3000 WCHAR charW;
3001 if(unicode)
3002 charW = (WCHAR)wParam;
3003 else
3005 CHAR charA = (CHAR)wParam;
3006 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
3008 return LISTBOX_HandleChar( hwnd, descr, charW );
3010 case WM_SYSTIMER:
3011 return LISTBOX_HandleSystemTimer( hwnd, descr );
3012 case WM_ERASEBKGND:
3013 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
3015 RECT rect;
3016 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
3017 wParam, (LPARAM)hwnd );
3018 TRACE("hbrush = %p\n", hbrush);
3019 if(!hbrush)
3020 hbrush = GetSysColorBrush(COLOR_WINDOW);
3021 if(hbrush)
3023 GetClientRect(hwnd, &rect);
3024 FillRect((HDC)wParam, &rect, hbrush);
3027 return 1;
3028 case WM_DROPFILES:
3029 if( !descr->lphc )
3030 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3031 SendMessageA( descr->owner, msg, wParam, lParam );
3032 break;
3034 default:
3035 if ((msg >= WM_USER) && (msg < 0xc000))
3036 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3037 hwnd, msg, wParam, lParam );
3038 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3039 DefWindowProcA( hwnd, msg, wParam, lParam );
3041 return 0;
3044 /***********************************************************************
3045 * ListBoxWndProcA
3047 * This is just a wrapper for the real wndproc, it only does window locking
3048 * and unlocking.
3050 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3052 if (!IsWindow(hwnd)) return 0;
3053 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3056 /***********************************************************************
3057 * ListBoxWndProcW
3059 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3061 if (!IsWindow(hwnd)) return 0;
3062 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3065 /***********************************************************************
3066 * ComboLBWndProc_common
3068 * The real combo listbox wndproc
3070 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3071 WPARAM wParam, LPARAM lParam, BOOL unicode )
3073 LRESULT lRet = 0;
3074 LB_DESCR *descr;
3075 LPHEADCOMBO lphc;
3077 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
3079 if (msg == WM_CREATE)
3081 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3082 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3083 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3084 return LISTBOX_Create( hwnd, lphc );
3086 /* Ignore all other messages before we get a WM_CREATE */
3087 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3088 DefWindowProcA( hwnd, msg, wParam, lParam );
3091 TRACE_(combo)("[%p]: msg %s wp %08x lp %08lx\n",
3092 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3094 if ((lphc = descr->lphc) != NULL)
3096 switch( msg )
3098 case WM_MOUSEMOVE:
3099 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3100 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3102 POINT mousePos;
3103 BOOL captured;
3104 RECT clientRect;
3106 mousePos.x = (INT16)LOWORD(lParam);
3107 mousePos.y = (INT16)HIWORD(lParam);
3110 * If we are in a dropdown combobox, we simulate that
3111 * the mouse is captured to show the tracking of the item.
3113 GetClientRect(hwnd, &clientRect);
3115 if (PtInRect( &clientRect, mousePos ))
3117 captured = descr->captured;
3118 descr->captured = TRUE;
3120 LISTBOX_HandleMouseMove( hwnd, descr,
3121 mousePos.x, mousePos.y);
3123 descr->captured = captured;
3126 else
3128 LISTBOX_HandleMouseMove( hwnd, descr,
3129 mousePos.x, mousePos.y);
3132 return 0;
3135 /* else we are in Win3.1 look, go with the default behavior. */
3136 break;
3138 case WM_LBUTTONUP:
3139 if (TWEAK_WineLook > WIN31_LOOK)
3141 POINT mousePos;
3142 RECT clientRect;
3145 * If the mouse button "up" is not in the listbox,
3146 * we make sure there is no selection by re-selecting the
3147 * item that was selected when the listbox was made visible.
3149 mousePos.x = (INT16)LOWORD(lParam);
3150 mousePos.y = (INT16)HIWORD(lParam);
3152 GetClientRect(hwnd, &clientRect);
3155 * When the user clicks outside the combobox and the focus
3156 * is lost, the owning combobox will send a fake buttonup with
3157 * 0xFFFFFFF as the mouse location, we must also revert the
3158 * selection to the original selection.
3160 if ( (lParam == (LPARAM)-1) ||
3161 (!PtInRect( &clientRect, mousePos )) )
3163 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3166 return LISTBOX_HandleLButtonUp( hwnd, descr );
3167 case WM_LBUTTONDBLCLK:
3168 case WM_LBUTTONDOWN:
3169 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3170 (INT16)LOWORD(lParam),
3171 (INT16)HIWORD(lParam) );
3172 case WM_NCACTIVATE:
3173 return FALSE;
3174 case WM_KEYDOWN:
3175 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3177 /* for some reason(?) Windows makes it possible to
3178 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3180 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3181 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3182 && (wParam == VK_DOWN || wParam == VK_UP)) )
3184 COMBO_FlipListbox( lphc, FALSE, FALSE );
3185 return 0;
3188 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3190 case LB_SETCURSEL16:
3191 case LB_SETCURSEL:
3192 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3193 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3194 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3195 return lRet;
3196 case WM_NCDESTROY:
3197 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3198 lphc->hWndLBox = 0;
3199 break;
3203 /* default handling: call listbox wnd proc */
3204 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3205 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3207 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3209 return lRet;
3212 /***********************************************************************
3213 * ComboLBWndProcA
3215 * NOTE: in Windows, winproc address of the ComboLBox is the same
3216 * as that of the Listbox.
3218 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3220 if (!IsWindow(hwnd)) return 0;
3221 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3224 /***********************************************************************
3225 * ComboLBWndProcW
3227 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3229 if (!IsWindow(hwnd)) return 0;
3230 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );