Add definitions for the S_IS* macros.
[wine/multimedia.git] / controls / listbox.c
blobcb4e7b41eebfbf098edf85fd9a6f6d0bf3d7d8a2
1 /*
2 * Listbox controls
4 * Copyright 1996 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include "windef.h"
25 #include "wingdi.h"
26 #include "wine/winuser16.h"
27 #include "wine/winbase16.h"
28 #include "wownt32.h"
29 #include "wine/unicode.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "message.h"
33 #include "user.h"
34 #include "controls.h"
35 #include "wine/debug.h"
36 #include "win.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(listbox);
39 WINE_DECLARE_DEBUG_CHANNEL(combo);
41 /* Unimplemented yet:
42 * - LBS_USETABSTOPS
43 * - Locale handling
45 * Probably needs improvement:
46 * - LBS_NOSEL
49 /* Items array granularity */
50 #define LB_ARRAY_GRANULARITY 16
52 /* Scrolling timeout in ms */
53 #define LB_SCROLL_TIMEOUT 50
55 /* Listbox system timer id */
56 #define LB_TIMER_ID 2
58 /* flag listbox changed while setredraw false - internal style */
59 #define LBS_DISPLAYCHANGED 0x80000000
61 /* Item structure */
62 typedef struct
64 LPWSTR str; /* Item text */
65 BOOL selected; /* Is item selected? */
66 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
67 DWORD data; /* User data */
68 } LB_ITEMDATA;
70 /* Listbox structure */
71 typedef struct
73 HWND owner; /* Owner window to send notifications to */
74 UINT style; /* Window style */
75 INT width; /* Window width */
76 INT height; /* Window height */
77 LB_ITEMDATA *items; /* Array of items */
78 INT nb_items; /* Number of items */
79 INT top_item; /* Top visible item */
80 INT selected_item; /* Selected item */
81 INT focus_item; /* Item that has the focus */
82 INT anchor_item; /* Anchor item for extended selection */
83 INT item_height; /* Default item height */
84 INT page_size; /* Items per listbox page */
85 INT column_width; /* Column width for multi-column listboxes */
86 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
87 INT horz_pos; /* Horizontal position */
88 INT nb_tabs; /* Number of tabs in array */
89 INT *tabs; /* Array of tabs */
90 BOOL caret_on; /* Is caret on? */
91 BOOL captured; /* Is mouse captured? */
92 BOOL in_focus;
93 HFONT font; /* Current font */
94 LCID locale; /* Current locale for string comparisons */
95 LPHEADCOMBO lphc; /* ComboLBox */
96 } LB_DESCR;
99 #define IS_OWNERDRAW(descr) \
100 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
102 #define HAS_STRINGS(descr) \
103 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
106 #define IS_MULTISELECT(descr) \
107 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
109 #define SEND_NOTIFICATION(hwnd,descr,code) \
110 (SendMessageW( (descr)->owner, WM_COMMAND, \
111 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (LPARAM)(hwnd) ))
113 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
115 /* Current timer status */
116 typedef enum
118 LB_TIMER_NONE,
119 LB_TIMER_UP,
120 LB_TIMER_LEFT,
121 LB_TIMER_DOWN,
122 LB_TIMER_RIGHT
123 } TIMER_DIRECTION;
125 static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
127 static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
128 static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
129 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
130 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
132 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
134 /*********************************************************************
135 * listbox class descriptor
137 const struct builtin_class_descr LISTBOX_builtin_class =
139 "ListBox", /* name */
140 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
141 ListBoxWndProcA, /* procA */
142 ListBoxWndProcW, /* procW */
143 sizeof(LB_DESCR *), /* extra */
144 IDC_ARROWA, /* cursor */
145 0 /* brush */
149 /*********************************************************************
150 * combolbox class descriptor
152 const struct builtin_class_descr COMBOLBOX_builtin_class =
154 "ComboLBox", /* name */
155 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
156 ComboLBWndProcA, /* procA */
157 ComboLBWndProcW, /* procW */
158 sizeof(LB_DESCR *), /* extra */
159 IDC_ARROWA, /* cursor */
160 0 /* brush */
164 /* check whether app is a Win 3.1 app */
165 inline static BOOL is_old_app( HWND hwnd )
167 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
171 /***********************************************************************
172 * LISTBOX_Dump
174 void LISTBOX_Dump( HWND hwnd )
176 INT i;
177 LB_ITEMDATA *item;
178 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
180 TRACE( "Listbox:\n" );
181 TRACE( "hwnd=%p descr=%08x items=%d top=%d\n",
182 hwnd, (UINT)descr, descr->nb_items, descr->top_item );
183 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
185 TRACE( "%4d: %-40s %d %08lx %3d\n",
186 i, debugstr_w(item->str), item->selected, item->data, item->height );
191 /***********************************************************************
192 * LISTBOX_GetCurrentPageSize
194 * Return the current page size
196 static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
198 INT i, height;
199 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
200 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
202 if ((height += descr->items[i].height) > descr->height) break;
204 if (i == descr->top_item) return 1;
205 else return i - descr->top_item;
209 /***********************************************************************
210 * LISTBOX_GetMaxTopIndex
212 * Return the maximum possible index for the top of the listbox.
214 static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
216 INT max, page;
218 if (descr->style & LBS_OWNERDRAWVARIABLE)
220 page = descr->height;
221 for (max = descr->nb_items - 1; max >= 0; max--)
222 if ((page -= descr->items[max].height) < 0) break;
223 if (max < descr->nb_items - 1) max++;
225 else if (descr->style & LBS_MULTICOLUMN)
227 if ((page = descr->width / descr->column_width) < 1) page = 1;
228 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
229 max = (max - page) * descr->page_size;
231 else
233 max = descr->nb_items - descr->page_size;
235 if (max < 0) max = 0;
236 return max;
240 /***********************************************************************
241 * LISTBOX_UpdateScroll
243 * Update the scrollbars. Should be called whenever the content
244 * of the listbox changes.
246 static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
248 SCROLLINFO info;
250 /* Check the listbox scroll bar flags individually before we call
251 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
252 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
253 scroll bar when we do not need one.
254 if (!(descr->style & WS_VSCROLL)) return;
257 /* It is important that we check descr->style, and not wnd->dwStyle,
258 for WS_VSCROLL, as the former is exactly the one passed in
259 argument to CreateWindow.
260 In Windows (and from now on in Wine :) a listbox created
261 with such a style (no WS_SCROLL) does not update
262 the scrollbar with listbox-related data, thus letting
263 the programmer use it for his/her own purposes. */
265 if (descr->style & LBS_NOREDRAW) return;
266 info.cbSize = sizeof(info);
268 if (descr->style & LBS_MULTICOLUMN)
270 info.nMin = 0;
271 info.nMax = (descr->nb_items - 1) / descr->page_size;
272 info.nPos = descr->top_item / descr->page_size;
273 info.nPage = descr->width / descr->column_width;
274 if (info.nPage < 1) info.nPage = 1;
275 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
276 if (descr->style & LBS_DISABLENOSCROLL)
277 info.fMask |= SIF_DISABLENOSCROLL;
278 if (descr->style & WS_HSCROLL)
279 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
280 info.nMax = 0;
281 info.fMask = SIF_RANGE;
282 if (descr->style & WS_VSCROLL)
283 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
285 else
287 info.nMin = 0;
288 info.nMax = descr->nb_items - 1;
289 info.nPos = descr->top_item;
290 info.nPage = LISTBOX_GetCurrentPageSize( descr );
291 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
292 if (descr->style & LBS_DISABLENOSCROLL)
293 info.fMask |= SIF_DISABLENOSCROLL;
294 if (descr->style & WS_VSCROLL)
295 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
297 if (descr->horz_extent)
299 info.nMin = 0;
300 info.nMax = descr->horz_extent - 1;
301 info.nPos = descr->horz_pos;
302 info.nPage = descr->width;
303 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
304 if (descr->style & LBS_DISABLENOSCROLL)
305 info.fMask |= SIF_DISABLENOSCROLL;
306 if (descr->style & WS_HSCROLL)
307 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
313 /***********************************************************************
314 * LISTBOX_SetTopItem
316 * Set the top item of the listbox, scrolling up or down if necessary.
318 static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
319 BOOL scroll )
321 INT max = LISTBOX_GetMaxTopIndex( descr );
322 if (index > max) index = max;
323 if (index < 0) index = 0;
324 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
325 if (descr->top_item == index) return LB_OKAY;
326 if (descr->style & LBS_MULTICOLUMN)
328 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
329 if (scroll && (abs(diff) < descr->width))
330 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
331 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
333 else
334 scroll = FALSE;
336 else if (scroll)
338 INT diff;
339 if (descr->style & LBS_OWNERDRAWVARIABLE)
341 INT i;
342 diff = 0;
343 if (index > descr->top_item)
345 for (i = index - 1; i >= descr->top_item; i--)
346 diff -= descr->items[i].height;
348 else
350 for (i = index; i < descr->top_item; i++)
351 diff += descr->items[i].height;
354 else
355 diff = (descr->top_item - index) * descr->item_height;
357 if (abs(diff) < descr->height)
358 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
359 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
360 else
361 scroll = FALSE;
363 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
364 descr->top_item = index;
365 LISTBOX_UpdateScroll( hwnd, descr );
366 return LB_OKAY;
370 /***********************************************************************
371 * LISTBOX_UpdatePage
373 * Update the page size. Should be called when the size of
374 * the client area or the item height changes.
376 static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
378 INT page_size;
380 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
381 page_size = 1;
382 if (page_size == descr->page_size) return;
383 descr->page_size = page_size;
384 if (descr->style & LBS_MULTICOLUMN)
385 InvalidateRect( hwnd, NULL, TRUE );
386 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
390 /***********************************************************************
391 * LISTBOX_UpdateSize
393 * Update the size of the listbox. Should be called when the size of
394 * the client area changes.
396 static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
398 RECT rect;
400 GetClientRect( hwnd, &rect );
401 descr->width = rect.right - rect.left;
402 descr->height = rect.bottom - rect.top;
403 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
405 INT remaining;
406 RECT rect;
408 GetWindowRect( hwnd, &rect );
409 if(descr->item_height != 0)
410 remaining = descr->height % descr->item_height;
411 else
412 remaining = 0;
413 if ((descr->height > descr->item_height) && remaining)
415 if (is_old_app(hwnd))
416 { /* give a margin for error to 16 bits programs - if we need
417 less than the height of the nonclient area, round to the
418 *next* number of items */
419 int ncheight = rect.bottom - rect.top - descr->height;
420 if ((descr->item_height - remaining) <= ncheight)
421 remaining = remaining - descr->item_height;
423 TRACE("[%p]: changing height %d -> %d\n",
424 hwnd, descr->height, descr->height - remaining );
425 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
426 rect.bottom - rect.top - remaining,
427 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
428 return;
431 TRACE("[%p]: new size = %d,%d\n", hwnd, descr->width, descr->height );
432 LISTBOX_UpdatePage( hwnd, descr );
433 LISTBOX_UpdateScroll( hwnd, descr );
435 /* Invalidate the focused item so it will be repainted correctly */
436 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
438 InvalidateRect( hwnd, &rect, FALSE );
443 /***********************************************************************
444 * LISTBOX_GetItemRect
446 * Get the rectangle enclosing an item, in listbox client coordinates.
447 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
449 static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
451 /* Index <= 0 is legal even on empty listboxes */
452 if (index && (index >= descr->nb_items)) return -1;
453 SetRect( rect, 0, 0, descr->width, descr->height );
454 if (descr->style & LBS_MULTICOLUMN)
456 INT col = (index / descr->page_size) -
457 (descr->top_item / descr->page_size);
458 rect->left += col * descr->column_width;
459 rect->right = rect->left + descr->column_width;
460 rect->top += (index % descr->page_size) * descr->item_height;
461 rect->bottom = rect->top + descr->item_height;
463 else if (descr->style & LBS_OWNERDRAWVARIABLE)
465 INT i;
466 rect->right += descr->horz_pos;
467 if ((index >= 0) && (index < descr->nb_items))
469 if (index < descr->top_item)
471 for (i = descr->top_item-1; i >= index; i--)
472 rect->top -= descr->items[i].height;
474 else
476 for (i = descr->top_item; i < index; i++)
477 rect->top += descr->items[i].height;
479 rect->bottom = rect->top + descr->items[index].height;
483 else
485 rect->top += (index - descr->top_item) * descr->item_height;
486 rect->bottom = rect->top + descr->item_height;
487 rect->right += descr->horz_pos;
490 return ((rect->left < descr->width) && (rect->right > 0) &&
491 (rect->top < descr->height) && (rect->bottom > 0));
495 /***********************************************************************
496 * LISTBOX_GetItemFromPoint
498 * Return the item nearest from point (x,y) (in client coordinates).
500 static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
502 INT index = descr->top_item;
504 if (!descr->nb_items) return -1; /* No items */
505 if (descr->style & LBS_OWNERDRAWVARIABLE)
507 INT pos = 0;
508 if (y >= 0)
510 while (index < descr->nb_items)
512 if ((pos += descr->items[index].height) > y) break;
513 index++;
516 else
518 while (index > 0)
520 index--;
521 if ((pos -= descr->items[index].height) <= y) break;
525 else if (descr->style & LBS_MULTICOLUMN)
527 if (y >= descr->item_height * descr->page_size) return -1;
528 if (y >= 0) index += y / descr->item_height;
529 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
530 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
532 else
534 index += (y / descr->item_height);
536 if (index < 0) return 0;
537 if (index >= descr->nb_items) return -1;
538 return index;
542 /***********************************************************************
543 * LISTBOX_PaintItem
545 * Paint an item.
547 static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
548 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
550 LB_ITEMDATA *item = NULL;
551 if (index < descr->nb_items) item = &descr->items[index];
553 if (IS_OWNERDRAW(descr))
555 DRAWITEMSTRUCT dis;
556 RECT r;
557 HRGN hrgn;
558 UINT id = GetWindowLongA( hwnd, GWL_ID );
560 if (!item)
562 if (action == ODA_FOCUS)
563 DrawFocusRect( hdc, rect );
564 else
565 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
566 return;
569 /* some programs mess with the clipping region when
570 drawing the item, *and* restore the previous region
571 after they are done, so a region has better to exist
572 else everything ends clipped */
573 GetClientRect(hwnd, &r);
574 hrgn = CreateRectRgnIndirect(&r);
575 SelectClipRgn( hdc, hrgn);
576 DeleteObject( hrgn );
578 dis.CtlType = ODT_LISTBOX;
579 dis.CtlID = id;
580 dis.hwndItem = hwnd;
581 dis.itemAction = action;
582 dis.hDC = hdc;
583 dis.itemID = index;
584 dis.itemState = 0;
585 if (item && item->selected) dis.itemState |= ODS_SELECTED;
586 if (!ignoreFocus && (descr->focus_item == index) &&
587 (descr->caret_on) &&
588 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
589 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
590 dis.itemData = item ? item->data : 0;
591 dis.rcItem = *rect;
592 TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%ld,%ld-%ld,%ld\n",
593 hwnd, index, item ? debugstr_w(item->str) : "", action,
594 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
595 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
597 else
599 COLORREF oldText = 0, oldBk = 0;
601 if (action == ODA_FOCUS)
603 DrawFocusRect( hdc, rect );
604 return;
606 if (item && item->selected)
608 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
609 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
612 TRACE("[%p]: painting %d (%s) action=%02x rect=%ld,%ld-%ld,%ld\n",
613 hwnd, index, item ? debugstr_w(item->str) : "", action,
614 rect->left, rect->top, rect->right, rect->bottom );
615 if (!item)
616 ExtTextOutW( hdc, rect->left + 1, rect->top,
617 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
618 else if (!(descr->style & LBS_USETABSTOPS))
619 ExtTextOutW( hdc, rect->left + 1, rect->top,
620 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
621 strlenW(item->str), NULL );
622 else
624 /* Output empty string to paint background in the full width. */
625 ExtTextOutW( hdc, rect->left + 1, rect->top,
626 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
627 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
628 item->str, strlenW(item->str),
629 descr->nb_tabs, descr->tabs, 0);
631 if (item && item->selected)
633 SetBkColor( hdc, oldBk );
634 SetTextColor( hdc, oldText );
636 if (!ignoreFocus && (descr->focus_item == index) &&
637 (descr->caret_on) &&
638 (descr->in_focus)) DrawFocusRect( hdc, rect );
643 /***********************************************************************
644 * LISTBOX_SetRedraw
646 * Change the redraw flag.
648 static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
650 if (on)
652 if (!(descr->style & LBS_NOREDRAW)) return;
653 descr->style &= ~LBS_NOREDRAW;
654 if (descr->style & LBS_DISPLAYCHANGED)
655 { /* page was changed while setredraw false, refresh automatically */
656 InvalidateRect(hwnd, NULL, TRUE);
657 if ((descr->top_item + descr->page_size) > descr->nb_items)
658 { /* reset top of page if less than number of items/page */
659 descr->top_item = descr->nb_items - descr->page_size;
660 if (descr->top_item < 0) descr->top_item = 0;
662 descr->style &= ~LBS_DISPLAYCHANGED;
664 LISTBOX_UpdateScroll( hwnd, descr );
666 else descr->style |= LBS_NOREDRAW;
670 /***********************************************************************
671 * LISTBOX_RepaintItem
673 * Repaint a single item synchronously.
675 static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
676 UINT action )
678 HDC hdc;
679 RECT rect;
680 HFONT oldFont = 0;
681 HBRUSH hbrush, oldBrush = 0;
683 /* Do not repaint the item if the item is not visible */
684 if (!IsWindowVisible(hwnd)) return;
685 if (descr->style & LBS_NOREDRAW)
687 descr->style |= LBS_DISPLAYCHANGED;
688 return;
690 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
691 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
692 if (descr->font) oldFont = SelectObject( hdc, descr->font );
693 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
694 (WPARAM)hdc, (LPARAM)hwnd );
695 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
696 if (!IsWindowEnabled(hwnd))
697 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
698 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
699 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
700 if (oldFont) SelectObject( hdc, oldFont );
701 if (oldBrush) SelectObject( hdc, oldBrush );
702 ReleaseDC( hwnd, hdc );
706 /***********************************************************************
707 * LISTBOX_InitStorage
709 static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
711 LB_ITEMDATA *item;
713 nb_items += LB_ARRAY_GRANULARITY - 1;
714 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
715 if (descr->items)
716 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
717 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
718 nb_items * sizeof(LB_ITEMDATA) )))
720 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
721 return LB_ERRSPACE;
723 descr->items = item;
724 return LB_OKAY;
728 /***********************************************************************
729 * LISTBOX_SetTabStops
731 static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
732 LPINT tabs, BOOL short_ints )
734 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
735 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
736 if (!(descr->nb_tabs = count))
738 descr->tabs = NULL;
739 return TRUE;
741 /* FIXME: count = 1 */
742 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
743 descr->nb_tabs * sizeof(INT) )))
744 return FALSE;
745 if (short_ints)
747 INT i;
748 LPINT16 p = (LPINT16)tabs;
750 TRACE("[%p]: settabstops ", hwnd );
751 for (i = 0; i < descr->nb_tabs; i++) {
752 descr->tabs[i] = *p++<<1; /* FIXME */
753 if (TRACE_ON(listbox)) TRACE("%hd ", descr->tabs[i]);
755 if (TRACE_ON(listbox)) TRACE("\n");
757 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
758 /* FIXME: repaint the window? */
759 return TRUE;
763 /***********************************************************************
764 * LISTBOX_GetText
766 static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
768 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
769 if (HAS_STRINGS(descr))
771 if (!lParam)
772 return strlenW(descr->items[index].str);
774 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
776 if(unicode)
778 LPWSTR buffer = (LPWSTR)lParam;
779 strcpyW( buffer, descr->items[index].str );
780 return strlenW(buffer);
782 else
784 LPSTR buffer = (LPSTR)lParam;
785 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
787 } else {
788 if (lParam)
789 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
790 return sizeof(DWORD);
795 /***********************************************************************
796 * LISTBOX_FindStringPos
798 * Find the nearest string located before a given string in sort order.
799 * If 'exact' is TRUE, return an error if we don't get an exact match.
801 static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
802 BOOL exact )
804 INT index, min, max, res = -1;
806 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
807 min = 0;
808 max = descr->nb_items;
809 while (min != max)
811 index = (min + max) / 2;
812 if (HAS_STRINGS(descr))
813 res = lstrcmpiW( str, descr->items[index].str);
814 else
816 COMPAREITEMSTRUCT cis;
817 UINT id = GetWindowLongA( hwnd, GWL_ID );
819 cis.CtlType = ODT_LISTBOX;
820 cis.CtlID = id;
821 cis.hwndItem = hwnd;
822 /* note that some application (MetaStock) expects the second item
823 * to be in the listbox */
824 cis.itemID1 = -1;
825 cis.itemData1 = (DWORD)str;
826 cis.itemID2 = index;
827 cis.itemData2 = descr->items[index].data;
828 cis.dwLocaleId = descr->locale;
829 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
831 if (!res) return index;
832 if (res < 0) max = index;
833 else min = index + 1;
835 return exact ? -1 : max;
839 /***********************************************************************
840 * LISTBOX_FindFileStrPos
842 * Find the nearest string located before a given string in directory
843 * sort order (i.e. first files, then directories, then drives).
845 static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
847 INT min, max, res = -1;
849 if (!HAS_STRINGS(descr))
850 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
851 min = 0;
852 max = descr->nb_items;
853 while (min != max)
855 INT index = (min + max) / 2;
856 LPCWSTR p = descr->items[index].str;
857 if (*p == '[') /* drive or directory */
859 if (*str != '[') res = -1;
860 else if (p[1] == '-') /* drive */
862 if (str[1] == '-') res = str[2] - p[2];
863 else res = -1;
865 else /* directory */
867 if (str[1] == '-') res = 1;
868 else res = lstrcmpiW( str, p );
871 else /* filename */
873 if (*str == '[') res = 1;
874 else res = lstrcmpiW( str, p );
876 if (!res) return index;
877 if (res < 0) max = index;
878 else min = index + 1;
880 return max;
884 /***********************************************************************
885 * LISTBOX_FindString
887 * Find the item beginning with a given string.
889 static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
890 LPCWSTR str, BOOL exact )
892 INT i;
893 LB_ITEMDATA *item;
895 if (start >= descr->nb_items) start = -1;
896 item = descr->items + start + 1;
897 if (HAS_STRINGS(descr))
899 if (!str || ! str[0] ) return LB_ERR;
900 if (exact)
902 for (i = start + 1; i < descr->nb_items; i++, item++)
903 if (!lstrcmpiW( str, item->str )) return i;
904 for (i = 0, item = descr->items; i <= start; i++, item++)
905 if (!lstrcmpiW( str, item->str )) return i;
907 else
909 /* Special case for drives and directories: ignore prefix */
910 #define CHECK_DRIVE(item) \
911 if ((item)->str[0] == '[') \
913 if (!strncmpiW( str, (item)->str+1, len )) return i; \
914 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
915 return i; \
918 INT len = strlenW(str);
919 for (i = start + 1; i < descr->nb_items; i++, item++)
921 if (!strncmpiW( str, item->str, len )) return i;
922 CHECK_DRIVE(item);
924 for (i = 0, item = descr->items; i <= start; i++, item++)
926 if (!strncmpiW( str, item->str, len )) return i;
927 CHECK_DRIVE(item);
929 #undef CHECK_DRIVE
932 else
934 if (exact && (descr->style & LBS_SORT))
935 /* If sorted, use a WM_COMPAREITEM binary search */
936 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
938 /* Otherwise use a linear search */
939 for (i = start + 1; i < descr->nb_items; i++, item++)
940 if (item->data == (DWORD)str) return i;
941 for (i = 0, item = descr->items; i <= start; i++, item++)
942 if (item->data == (DWORD)str) return i;
944 return LB_ERR;
948 /***********************************************************************
949 * LISTBOX_GetSelCount
951 static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
953 INT i, count;
954 LB_ITEMDATA *item = descr->items;
956 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
957 for (i = count = 0; i < descr->nb_items; i++, item++)
958 if (item->selected) count++;
959 return count;
963 /***********************************************************************
964 * LISTBOX_GetSelItems16
966 static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
968 INT i, count;
969 LB_ITEMDATA *item = descr->items;
971 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
972 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
973 if (item->selected) array[count++] = (INT16)i;
974 return count;
978 /***********************************************************************
979 * LISTBOX_GetSelItems
981 static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
983 INT i, count;
984 LB_ITEMDATA *item = descr->items;
986 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
987 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
988 if (item->selected) array[count++] = i;
989 return count;
993 /***********************************************************************
994 * LISTBOX_Paint
996 static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
998 INT i, col_pos = descr->page_size - 1;
999 RECT rect;
1000 RECT focusRect = {-1, -1, -1, -1};
1001 HFONT oldFont = 0;
1002 HBRUSH hbrush, oldBrush = 0;
1004 if (descr->style & LBS_NOREDRAW) return 0;
1006 SetRect( &rect, 0, 0, descr->width, descr->height );
1007 if (descr->style & LBS_MULTICOLUMN)
1008 rect.right = rect.left + descr->column_width;
1009 else if (descr->horz_pos)
1011 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1012 rect.right += descr->horz_pos;
1015 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1016 hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1017 (WPARAM)hdc, (LPARAM)hwnd );
1018 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1019 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1021 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1022 (descr->in_focus))
1024 /* Special case for empty listbox: paint focus rect */
1025 rect.bottom = rect.top + descr->item_height;
1026 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1027 ODA_FOCUS, FALSE );
1028 rect.top = rect.bottom;
1031 /* Paint all the item, regarding the selection
1032 Focus state will be painted after */
1034 for (i = descr->top_item; i < descr->nb_items; i++)
1036 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1037 rect.bottom = rect.top + descr->item_height;
1038 else
1039 rect.bottom = rect.top + descr->items[i].height;
1041 if (i == descr->focus_item)
1043 /* keep the focus rect, to paint the focus item after */
1044 focusRect.left = rect.left;
1045 focusRect.right = rect.right;
1046 focusRect.top = rect.top;
1047 focusRect.bottom = rect.bottom;
1049 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1050 rect.top = rect.bottom;
1052 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1054 if (!IS_OWNERDRAW(descr))
1056 /* Clear the bottom of the column */
1057 if (rect.top < descr->height)
1059 rect.bottom = descr->height;
1060 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1061 &rect, NULL, 0, NULL );
1065 /* Go to the next column */
1066 rect.left += descr->column_width;
1067 rect.right += descr->column_width;
1068 rect.top = 0;
1069 col_pos = descr->page_size - 1;
1071 else
1073 col_pos--;
1074 if (rect.top >= descr->height) break;
1078 /* Paint the focus item now */
1079 if (focusRect.top != focusRect.bottom && descr->caret_on)
1080 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1082 if (!IS_OWNERDRAW(descr))
1084 /* Clear the remainder of the client area */
1085 if (rect.top < descr->height)
1087 rect.bottom = descr->height;
1088 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1089 &rect, NULL, 0, NULL );
1091 if (rect.right < descr->width)
1093 rect.left = rect.right;
1094 rect.right = descr->width;
1095 rect.top = 0;
1096 rect.bottom = descr->height;
1097 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1098 &rect, NULL, 0, NULL );
1101 if (oldFont) SelectObject( hdc, oldFont );
1102 if (oldBrush) SelectObject( hdc, oldBrush );
1103 return 0;
1107 /***********************************************************************
1108 * LISTBOX_InvalidateItems
1110 * Invalidate all items from a given item. If the specified item is not
1111 * visible, nothing happens.
1113 static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1115 RECT rect;
1117 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1119 if (descr->style & LBS_NOREDRAW)
1121 descr->style |= LBS_DISPLAYCHANGED;
1122 return;
1124 rect.bottom = descr->height;
1125 InvalidateRect( hwnd, &rect, TRUE );
1126 if (descr->style & LBS_MULTICOLUMN)
1128 /* Repaint the other columns */
1129 rect.left = rect.right;
1130 rect.right = descr->width;
1131 rect.top = 0;
1132 InvalidateRect( hwnd, &rect, TRUE );
1138 /***********************************************************************
1139 * LISTBOX_GetItemHeight
1141 static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1143 if (descr->style & LBS_OWNERDRAWVARIABLE)
1145 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1146 return descr->items[index].height;
1148 else return descr->item_height;
1152 /***********************************************************************
1153 * LISTBOX_SetItemHeight
1155 static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1156 INT height, BOOL repaint )
1158 if (!height) height = 1;
1160 if (descr->style & LBS_OWNERDRAWVARIABLE)
1162 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1163 TRACE("[%p]: item %d height = %d\n", hwnd, index, height );
1164 descr->items[index].height = height;
1165 LISTBOX_UpdateScroll( hwnd, descr );
1166 if (repaint)
1167 LISTBOX_InvalidateItems( hwnd, descr, index );
1169 else if (height != descr->item_height)
1171 TRACE("[%p]: new height = %d\n", hwnd, height );
1172 descr->item_height = height;
1173 LISTBOX_UpdatePage( hwnd, descr );
1174 LISTBOX_UpdateScroll( hwnd, descr );
1175 if (repaint)
1176 InvalidateRect( hwnd, 0, TRUE );
1178 return LB_OKAY;
1182 /***********************************************************************
1183 * LISTBOX_SetHorizontalPos
1185 static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1187 INT diff;
1189 if (pos > descr->horz_extent - descr->width)
1190 pos = descr->horz_extent - descr->width;
1191 if (pos < 0) pos = 0;
1192 if (!(diff = descr->horz_pos - pos)) return;
1193 TRACE("[%p]: new horz pos = %d\n", hwnd, pos );
1194 descr->horz_pos = pos;
1195 LISTBOX_UpdateScroll( hwnd, descr );
1196 if (abs(diff) < descr->width)
1197 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1198 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1199 else
1200 InvalidateRect( hwnd, NULL, TRUE );
1204 /***********************************************************************
1205 * LISTBOX_SetHorizontalExtent
1207 static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1208 INT extent )
1210 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1211 return LB_OKAY;
1212 if (extent <= 0) extent = 1;
1213 if (extent == descr->horz_extent) return LB_OKAY;
1214 TRACE("[%p]: new horz extent = %d\n", hwnd, extent );
1215 descr->horz_extent = extent;
1216 if (descr->horz_pos > extent - descr->width)
1217 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1218 else
1219 LISTBOX_UpdateScroll( hwnd, descr );
1220 return LB_OKAY;
1224 /***********************************************************************
1225 * LISTBOX_SetColumnWidth
1227 static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1229 if (width == descr->column_width) return LB_OKAY;
1230 TRACE("[%p]: new column width = %d\n", hwnd, width );
1231 descr->column_width = width;
1232 LISTBOX_UpdatePage( hwnd, descr );
1233 return LB_OKAY;
1237 /***********************************************************************
1238 * LISTBOX_SetFont
1240 * Returns the item height.
1242 static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1244 HDC hdc;
1245 HFONT oldFont = 0;
1246 TEXTMETRICW tm;
1248 descr->font = font;
1250 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1252 ERR("unable to get DC.\n" );
1253 return 16;
1255 if (font) oldFont = SelectObject( hdc, font );
1256 GetTextMetricsW( hdc, &tm );
1257 if (oldFont) SelectObject( hdc, oldFont );
1258 ReleaseDC( hwnd, hdc );
1259 if (!IS_OWNERDRAW(descr))
1260 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1261 return tm.tmHeight ;
1265 /***********************************************************************
1266 * LISTBOX_MakeItemVisible
1268 * Make sure that a given item is partially or fully visible.
1270 static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1271 BOOL fully )
1273 INT top;
1275 if (index <= descr->top_item) top = index;
1276 else if (descr->style & LBS_MULTICOLUMN)
1278 INT cols = descr->width;
1279 if (!fully) cols += descr->column_width - 1;
1280 if (cols >= descr->column_width) cols /= descr->column_width;
1281 else cols = 1;
1282 if (index < descr->top_item + (descr->page_size * cols)) return;
1283 top = index - descr->page_size * (cols - 1);
1285 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1287 INT height = fully ? descr->items[index].height : 1;
1288 for (top = index; top > descr->top_item; top--)
1289 if ((height += descr->items[top-1].height) > descr->height) break;
1291 else
1293 if (index < descr->top_item + descr->page_size) return;
1294 if (!fully && (index == descr->top_item + descr->page_size) &&
1295 (descr->height > (descr->page_size * descr->item_height))) return;
1296 top = index - descr->page_size + 1;
1298 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1301 /***********************************************************************
1302 * LISTBOX_SetCaretIndex
1304 * NOTES
1305 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1308 static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1309 BOOL fully_visible )
1311 INT oldfocus = descr->focus_item;
1313 if (descr->style & LBS_NOSEL) return LB_ERR;
1314 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1315 if (index == oldfocus) return LB_OKAY;
1316 descr->focus_item = index;
1317 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1318 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1320 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1321 if (descr->caret_on && (descr->in_focus))
1322 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1324 return LB_OKAY;
1328 /***********************************************************************
1329 * LISTBOX_SelectItemRange
1331 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1333 static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1334 INT last, BOOL on )
1336 INT i;
1338 /* A few sanity checks */
1340 if (descr->style & LBS_NOSEL) return LB_ERR;
1341 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1342 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1343 if (last == -1) last = descr->nb_items - 1;
1344 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1345 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1346 /* selected_item reflects last selected/unselected item on multiple sel */
1347 descr->selected_item = last;
1349 if (on) /* Turn selection on */
1351 for (i = first; i <= last; i++)
1353 if (descr->items[i].selected) continue;
1354 descr->items[i].selected = TRUE;
1355 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1357 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1359 else /* Turn selection off */
1361 for (i = first; i <= last; i++)
1363 if (!descr->items[i].selected) continue;
1364 descr->items[i].selected = FALSE;
1365 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1368 return LB_OKAY;
1371 /***********************************************************************
1372 * LISTBOX_SetSelection
1374 static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1375 BOOL on, BOOL send_notify )
1377 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1379 if (descr->style & LBS_NOSEL) return LB_ERR;
1380 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1381 if (descr->style & LBS_MULTIPLESEL)
1383 if (index == -1) /* Select all items */
1384 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1385 else /* Only one item */
1386 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1388 else
1390 INT oldsel = descr->selected_item;
1391 if (index == oldsel) return LB_OKAY;
1392 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1393 if (index != -1) descr->items[index].selected = TRUE;
1394 descr->selected_item = index;
1395 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1396 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1397 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1398 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1399 else
1400 if( descr->lphc ) /* set selection change flag for parent combo */
1401 descr->lphc->wState |= CBF_SELCHANGE;
1403 return LB_OKAY;
1407 /***********************************************************************
1408 * LISTBOX_MoveCaret
1410 * Change the caret position and extend the selection to the new caret.
1412 static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1413 BOOL fully_visible )
1415 INT oldfocus = descr->focus_item;
1417 if ((index < 0) || (index >= descr->nb_items))
1418 return;
1420 /* Important, repaint needs to be done in this order if
1421 you want to mimic Windows behavior:
1422 1. Remove the focus and paint the item
1423 2. Remove the selection and paint the item(s)
1424 3. Set the selection and repaint the item(s)
1425 4. Set the focus to 'index' and repaint the item */
1427 /* 1. remove the focus and repaint the item */
1428 descr->focus_item = -1;
1429 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1430 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1432 /* 2. then turn off the previous selection */
1433 /* 3. repaint the new selected item */
1434 if (descr->style & LBS_EXTENDEDSEL)
1436 if (descr->anchor_item != -1)
1438 INT first = min( index, descr->anchor_item );
1439 INT last = max( index, descr->anchor_item );
1440 if (first > 0)
1441 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1442 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1443 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1446 else if (!(descr->style & LBS_MULTIPLESEL))
1448 /* Set selection to new caret item */
1449 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1452 /* 4. repaint the new item with the focus */
1453 descr->focus_item = index;
1454 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1455 if (descr->caret_on && (descr->in_focus))
1456 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1460 /***********************************************************************
1461 * LISTBOX_InsertItem
1463 static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1464 LPWSTR str, DWORD data )
1466 LB_ITEMDATA *item;
1467 INT max_items;
1468 INT oldfocus = descr->focus_item;
1470 if (index == -1) index = descr->nb_items;
1471 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1472 if (!descr->items) max_items = 0;
1473 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1474 if (descr->nb_items == max_items)
1476 /* We need to grow the array */
1477 max_items += LB_ARRAY_GRANULARITY;
1478 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1479 max_items * sizeof(LB_ITEMDATA) )))
1481 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1482 return LB_ERRSPACE;
1484 descr->items = item;
1487 /* Insert the item structure */
1489 item = &descr->items[index];
1490 if (index < descr->nb_items)
1491 RtlMoveMemory( item + 1, item,
1492 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1493 item->str = str;
1494 item->data = data;
1495 item->height = 0;
1496 item->selected = FALSE;
1497 descr->nb_items++;
1499 /* Get item height */
1501 if (descr->style & LBS_OWNERDRAWVARIABLE)
1503 MEASUREITEMSTRUCT mis;
1504 UINT id = GetWindowLongA( hwnd, GWL_ID );
1506 mis.CtlType = ODT_LISTBOX;
1507 mis.CtlID = id;
1508 mis.itemID = index;
1509 mis.itemData = descr->items[index].data;
1510 mis.itemHeight = descr->item_height;
1511 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1512 item->height = mis.itemHeight ? mis.itemHeight : 1;
1513 TRACE("[%p]: measure item %d (%s) = %d\n",
1514 hwnd, index, str ? debugstr_w(str) : "", item->height );
1517 /* Repaint the items */
1519 LISTBOX_UpdateScroll( hwnd, descr );
1520 LISTBOX_InvalidateItems( hwnd, descr, index );
1522 /* Move selection and focused item */
1523 /* If listbox was empty, set focus to the first item */
1524 if (descr->nb_items == 1)
1525 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1526 /* single select don't change selection index in win31 */
1527 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1529 descr->selected_item++;
1530 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1532 else
1534 if (index <= descr->selected_item)
1536 descr->selected_item++;
1537 descr->focus_item = oldfocus; /* focus not changed */
1540 return LB_OKAY;
1544 /***********************************************************************
1545 * LISTBOX_InsertString
1547 static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1548 LPCWSTR str )
1550 LPWSTR new_str = NULL;
1551 DWORD data = 0;
1552 LRESULT ret;
1554 if (HAS_STRINGS(descr))
1556 static const WCHAR empty_stringW[] = { 0 };
1557 if (!str) str = empty_stringW;
1558 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1560 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1561 return LB_ERRSPACE;
1563 strcpyW(new_str, str);
1565 else data = (DWORD)str;
1567 if (index == -1) index = descr->nb_items;
1568 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1570 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1571 return ret;
1574 TRACE("[%p]: added item %d %s\n",
1575 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1576 return index;
1580 /***********************************************************************
1581 * LISTBOX_DeleteItem
1583 * Delete the content of an item. 'index' must be a valid index.
1585 static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1587 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1588 * while Win95 sends it for all items with user data.
1589 * It's probably better to send it too often than not
1590 * often enough, so this is what we do here.
1592 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1594 DELETEITEMSTRUCT dis;
1595 UINT id = GetWindowLongA( hwnd, GWL_ID );
1597 dis.CtlType = ODT_LISTBOX;
1598 dis.CtlID = id;
1599 dis.itemID = index;
1600 dis.hwndItem = hwnd;
1601 dis.itemData = descr->items[index].data;
1602 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1604 if (HAS_STRINGS(descr) && descr->items[index].str)
1605 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1609 /***********************************************************************
1610 * LISTBOX_RemoveItem
1612 * Remove an item from the listbox and delete its content.
1614 static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1616 LB_ITEMDATA *item;
1617 INT max_items;
1619 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1620 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1622 /* We need to invalidate the original rect instead of the updated one. */
1623 LISTBOX_InvalidateItems( hwnd, descr, index );
1625 LISTBOX_DeleteItem( hwnd, descr, index );
1627 /* Remove the item */
1629 item = &descr->items[index];
1630 if (index < descr->nb_items-1)
1631 RtlMoveMemory( item, item + 1,
1632 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1633 descr->nb_items--;
1634 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1636 /* Shrink the item array if possible */
1638 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1639 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1641 max_items -= LB_ARRAY_GRANULARITY;
1642 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1643 max_items * sizeof(LB_ITEMDATA) );
1644 if (item) descr->items = item;
1646 /* Repaint the items */
1648 LISTBOX_UpdateScroll( hwnd, descr );
1649 /* if we removed the scrollbar, reset the top of the list
1650 (correct for owner-drawn ???) */
1651 if (descr->nb_items == descr->page_size)
1652 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1654 /* Move selection and focused item */
1655 if (!IS_MULTISELECT(descr))
1657 if (index == descr->selected_item)
1658 descr->selected_item = -1;
1659 else if (index < descr->selected_item)
1661 descr->selected_item--;
1662 if (ISWIN31) /* win 31 do not change the selected item number */
1663 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1667 if (descr->focus_item >= descr->nb_items)
1669 descr->focus_item = descr->nb_items - 1;
1670 if (descr->focus_item < 0) descr->focus_item = 0;
1672 return LB_OKAY;
1676 /***********************************************************************
1677 * LISTBOX_ResetContent
1679 static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1681 INT i;
1683 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1684 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1685 descr->nb_items = 0;
1686 descr->top_item = 0;
1687 descr->selected_item = -1;
1688 descr->focus_item = 0;
1689 descr->anchor_item = -1;
1690 descr->items = NULL;
1694 /***********************************************************************
1695 * LISTBOX_SetCount
1697 static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1699 LRESULT ret;
1701 if (HAS_STRINGS(descr)) return LB_ERR;
1702 /* FIXME: this is far from optimal... */
1703 if (count > descr->nb_items)
1705 while (count > descr->nb_items)
1706 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1707 return ret;
1709 else if (count < descr->nb_items)
1711 while (count < descr->nb_items)
1712 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1713 return ret;
1715 return LB_OKAY;
1719 /***********************************************************************
1720 * LISTBOX_Directory
1722 static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1723 LPCWSTR filespec, BOOL long_names )
1725 HANDLE handle;
1726 LRESULT ret = LB_OKAY;
1727 WIN32_FIND_DATAW entry;
1728 int pos;
1730 /* don't scan directory if we just want drives exclusively */
1731 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1732 /* scan directory */
1733 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1735 int le = GetLastError();
1736 if ((le != ERROR_NO_MORE_FILES) && (le != ERROR_FILE_NOT_FOUND)) return LB_ERR;
1738 else
1742 WCHAR buffer[270];
1743 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1745 static const WCHAR bracketW[] = { ']',0 };
1746 static const WCHAR dotW[] = { '.',0 };
1747 if (!(attrib & DDL_DIRECTORY) ||
1748 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1749 buffer[0] = '[';
1750 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1751 else strcpyW( buffer + 1, entry.cAlternateFileName );
1752 strcatW(buffer, bracketW);
1754 else /* not a directory */
1756 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1757 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1759 if ((attrib & DDL_EXCLUSIVE) &&
1760 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1761 continue;
1762 #undef ATTRIBS
1763 if (long_names) strcpyW( buffer, entry.cFileName );
1764 else strcpyW( buffer, entry.cAlternateFileName );
1766 if (!long_names) CharLowerW( buffer );
1767 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1768 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1769 break;
1770 } while (FindNextFileW( handle, &entry ));
1771 FindClose( handle );
1775 /* scan drives */
1776 if ((ret >= 0) && (attrib & DDL_DRIVES))
1778 WCHAR buffer[] = {'[','-','a','-',']',0};
1779 WCHAR root[] = {'A',':','\\',0};
1780 int drive;
1781 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1783 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1784 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1785 break;
1788 return ret;
1792 /***********************************************************************
1793 * LISTBOX_HandleVScroll
1795 static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1797 SCROLLINFO info;
1799 if (descr->style & LBS_MULTICOLUMN) return 0;
1800 switch(LOWORD(wParam))
1802 case SB_LINEUP:
1803 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1804 break;
1805 case SB_LINEDOWN:
1806 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1807 break;
1808 case SB_PAGEUP:
1809 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1810 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1811 break;
1812 case SB_PAGEDOWN:
1813 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1814 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1815 break;
1816 case SB_THUMBPOSITION:
1817 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1818 break;
1819 case SB_THUMBTRACK:
1820 info.cbSize = sizeof(info);
1821 info.fMask = SIF_TRACKPOS;
1822 GetScrollInfo( hwnd, SB_VERT, &info );
1823 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1824 break;
1825 case SB_TOP:
1826 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1827 break;
1828 case SB_BOTTOM:
1829 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1830 break;
1832 return 0;
1836 /***********************************************************************
1837 * LISTBOX_HandleHScroll
1839 static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1841 SCROLLINFO info;
1842 INT page;
1844 if (descr->style & LBS_MULTICOLUMN)
1846 switch(LOWORD(wParam))
1848 case SB_LINELEFT:
1849 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1850 TRUE );
1851 break;
1852 case SB_LINERIGHT:
1853 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1854 TRUE );
1855 break;
1856 case SB_PAGELEFT:
1857 page = descr->width / descr->column_width;
1858 if (page < 1) page = 1;
1859 LISTBOX_SetTopItem( hwnd, descr,
1860 descr->top_item - page * descr->page_size, TRUE );
1861 break;
1862 case SB_PAGERIGHT:
1863 page = descr->width / descr->column_width;
1864 if (page < 1) page = 1;
1865 LISTBOX_SetTopItem( hwnd, descr,
1866 descr->top_item + page * descr->page_size, TRUE );
1867 break;
1868 case SB_THUMBPOSITION:
1869 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1870 TRUE );
1871 break;
1872 case SB_THUMBTRACK:
1873 info.cbSize = sizeof(info);
1874 info.fMask = SIF_TRACKPOS;
1875 GetScrollInfo( hwnd, SB_VERT, &info );
1876 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1877 TRUE );
1878 break;
1879 case SB_LEFT:
1880 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1881 break;
1882 case SB_RIGHT:
1883 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1884 break;
1887 else if (descr->horz_extent)
1889 switch(LOWORD(wParam))
1891 case SB_LINELEFT:
1892 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1893 break;
1894 case SB_LINERIGHT:
1895 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1896 break;
1897 case SB_PAGELEFT:
1898 LISTBOX_SetHorizontalPos( hwnd, descr,
1899 descr->horz_pos - descr->width );
1900 break;
1901 case SB_PAGERIGHT:
1902 LISTBOX_SetHorizontalPos( hwnd, descr,
1903 descr->horz_pos + descr->width );
1904 break;
1905 case SB_THUMBPOSITION:
1906 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1907 break;
1908 case SB_THUMBTRACK:
1909 info.cbSize = sizeof(info);
1910 info.fMask = SIF_TRACKPOS;
1911 GetScrollInfo( hwnd, SB_HORZ, &info );
1912 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1913 break;
1914 case SB_LEFT:
1915 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1916 break;
1917 case SB_RIGHT:
1918 LISTBOX_SetHorizontalPos( hwnd, descr,
1919 descr->horz_extent - descr->width );
1920 break;
1923 return 0;
1926 static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1928 short gcWheelDelta = 0;
1929 UINT pulScrollLines = 3;
1931 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1933 gcWheelDelta -= (short) HIWORD(wParam);
1935 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1937 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1938 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1939 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1941 return 0;
1944 /***********************************************************************
1945 * LISTBOX_HandleLButtonDown
1947 static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1948 WPARAM wParam, INT x, INT y )
1950 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1951 TRACE("[%p]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1952 if (!descr->caret_on && (descr->in_focus)) return 0;
1954 if (!descr->in_focus)
1956 if( !descr->lphc ) SetFocus( hwnd );
1957 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1960 if (index == -1) return 0;
1962 if (descr->style & LBS_EXTENDEDSEL)
1964 /* we should perhaps make sure that all items are deselected
1965 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1966 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1967 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1970 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1971 if (wParam & MK_CONTROL)
1973 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1974 LISTBOX_SetSelection( hwnd, descr, index,
1975 !descr->items[index].selected,
1976 (descr->style & LBS_NOTIFY) != 0);
1978 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1980 else
1982 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1983 LISTBOX_SetSelection( hwnd, descr, index,
1984 (!(descr->style & LBS_MULTIPLESEL) ||
1985 !descr->items[index].selected),
1986 (descr->style & LBS_NOTIFY) != 0 );
1989 descr->captured = TRUE;
1990 SetCapture( hwnd );
1992 if (!descr->lphc)
1994 if (descr->style & LBS_NOTIFY )
1995 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1996 MAKELPARAM( x, y ) );
1997 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1999 POINT pt;
2001 pt.x = x;
2002 pt.y = y;
2004 if (DragDetect( hwnd, pt ))
2005 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2008 return 0;
2012 /*************************************************************************
2013 * LISTBOX_HandleLButtonDownCombo [Internal]
2015 * Process LButtonDown message for the ComboListBox
2017 nn * PARAMS
2018 * pWnd [I] The windows internal structure
2019 * pDescr [I] The ListBox internal structure
2020 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2021 * x [I] X Mouse Coordinate
2022 * y [I] Y Mouse Coordinate
2024 * RETURNS
2025 * 0 since we are processing the WM_LBUTTONDOWN Message
2027 * NOTES
2028 * This function is only to be used when a ListBox is a ComboListBox
2031 static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2032 UINT msg, WPARAM wParam, INT x, INT y)
2034 RECT clientRect, screenRect;
2035 POINT mousePos;
2037 mousePos.x = x;
2038 mousePos.y = y;
2040 GetClientRect(hwnd, &clientRect);
2042 if(PtInRect(&clientRect, mousePos))
2044 /* MousePos is in client, resume normal processing */
2045 if (msg == WM_LBUTTONDOWN)
2047 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2048 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2050 else if (pDescr->style & LBS_NOTIFY)
2051 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2052 return 0;
2054 else
2056 POINT screenMousePos;
2057 HWND hWndOldCapture;
2059 /* Check the Non-Client Area */
2060 screenMousePos = mousePos;
2061 hWndOldCapture = GetCapture();
2062 ReleaseCapture();
2063 GetWindowRect(hwnd, &screenRect);
2064 ClientToScreen(hwnd, &screenMousePos);
2066 if(!PtInRect(&screenRect, screenMousePos))
2068 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2069 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2070 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2071 return 0;
2073 else
2075 /* Check to see the NC is a scrollbar */
2076 INT nHitTestType=0;
2077 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2078 /* Check Vertical scroll bar */
2079 if (style & WS_VSCROLL)
2081 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2082 if (PtInRect( &clientRect, mousePos ))
2084 nHitTestType = HTVSCROLL;
2087 /* Check horizontal scroll bar */
2088 if (style & WS_HSCROLL)
2090 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2091 if (PtInRect( &clientRect, mousePos ))
2093 nHitTestType = HTHSCROLL;
2096 /* Windows sends this message when a scrollbar is clicked
2099 if(nHitTestType != 0)
2101 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2102 MAKELONG(screenMousePos.x, screenMousePos.y));
2104 /* Resume the Capture after scrolling is complete
2106 if(hWndOldCapture != 0)
2108 SetCapture(hWndOldCapture);
2112 return 0;
2115 /***********************************************************************
2116 * LISTBOX_HandleLButtonUp
2118 static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2120 if (LISTBOX_Timer != LB_TIMER_NONE)
2121 KillSystemTimer( hwnd, LB_TIMER_ID );
2122 LISTBOX_Timer = LB_TIMER_NONE;
2123 if (descr->captured)
2125 descr->captured = FALSE;
2126 if (GetCapture() == hwnd) ReleaseCapture();
2127 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2128 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2130 return 0;
2134 /***********************************************************************
2135 * LISTBOX_HandleTimer
2137 * Handle scrolling upon a timer event.
2138 * Return TRUE if scrolling should continue.
2140 static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2141 INT index, TIMER_DIRECTION dir )
2143 switch(dir)
2145 case LB_TIMER_UP:
2146 if (descr->top_item) index = descr->top_item - 1;
2147 else index = 0;
2148 break;
2149 case LB_TIMER_LEFT:
2150 if (descr->top_item) index -= descr->page_size;
2151 break;
2152 case LB_TIMER_DOWN:
2153 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2154 if (index == descr->focus_item) index++;
2155 if (index >= descr->nb_items) index = descr->nb_items - 1;
2156 break;
2157 case LB_TIMER_RIGHT:
2158 if (index + descr->page_size < descr->nb_items)
2159 index += descr->page_size;
2160 break;
2161 case LB_TIMER_NONE:
2162 break;
2164 if (index == descr->focus_item) return FALSE;
2165 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2166 return TRUE;
2170 /***********************************************************************
2171 * LISTBOX_HandleSystemTimer
2173 * WM_SYSTIMER handler.
2175 static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2177 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2179 KillSystemTimer( hwnd, LB_TIMER_ID );
2180 LISTBOX_Timer = LB_TIMER_NONE;
2182 return 0;
2186 /***********************************************************************
2187 * LISTBOX_HandleMouseMove
2189 * WM_MOUSEMOVE handler.
2191 static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2192 INT x, INT y )
2194 INT index;
2195 TIMER_DIRECTION dir = LB_TIMER_NONE;
2197 if (!descr->captured) return;
2199 if (descr->style & LBS_MULTICOLUMN)
2201 if (y < 0) y = 0;
2202 else if (y >= descr->item_height * descr->page_size)
2203 y = descr->item_height * descr->page_size - 1;
2205 if (x < 0)
2207 dir = LB_TIMER_LEFT;
2208 x = 0;
2210 else if (x >= descr->width)
2212 dir = LB_TIMER_RIGHT;
2213 x = descr->width - 1;
2216 else
2218 if (y < 0) dir = LB_TIMER_UP; /* above */
2219 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2222 index = LISTBOX_GetItemFromPoint( descr, x, y );
2223 if (index == -1) index = descr->focus_item;
2224 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2226 /* Start/stop the system timer */
2228 if (dir != LB_TIMER_NONE)
2229 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2230 else if (LISTBOX_Timer != LB_TIMER_NONE)
2231 KillSystemTimer( hwnd, LB_TIMER_ID );
2232 LISTBOX_Timer = dir;
2236 /***********************************************************************
2237 * LISTBOX_HandleKeyDown
2239 static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2241 INT caret = -1;
2242 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2243 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2244 bForceSelection = FALSE; /* only for single select list */
2246 if (descr->style & LBS_WANTKEYBOARDINPUT)
2248 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2249 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2250 (LPARAM)hwnd );
2251 if (caret == -2) return 0;
2253 if (caret == -1) switch(wParam)
2255 case VK_LEFT:
2256 if (descr->style & LBS_MULTICOLUMN)
2258 bForceSelection = FALSE;
2259 if (descr->focus_item >= descr->page_size)
2260 caret = descr->focus_item - descr->page_size;
2261 break;
2263 /* fall through */
2264 case VK_UP:
2265 caret = descr->focus_item - 1;
2266 if (caret < 0) caret = 0;
2267 break;
2268 case VK_RIGHT:
2269 if (descr->style & LBS_MULTICOLUMN)
2271 bForceSelection = FALSE;
2272 if (descr->focus_item + descr->page_size < descr->nb_items)
2273 caret = descr->focus_item + descr->page_size;
2274 break;
2276 /* fall through */
2277 case VK_DOWN:
2278 caret = descr->focus_item + 1;
2279 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2280 break;
2282 case VK_PRIOR:
2283 if (descr->style & LBS_MULTICOLUMN)
2285 INT page = descr->width / descr->column_width;
2286 if (page < 1) page = 1;
2287 caret = descr->focus_item - (page * descr->page_size) + 1;
2289 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2290 if (caret < 0) caret = 0;
2291 break;
2292 case VK_NEXT:
2293 if (descr->style & LBS_MULTICOLUMN)
2295 INT page = descr->width / descr->column_width;
2296 if (page < 1) page = 1;
2297 caret = descr->focus_item + (page * descr->page_size) - 1;
2299 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2300 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2301 break;
2302 case VK_HOME:
2303 caret = 0;
2304 break;
2305 case VK_END:
2306 caret = descr->nb_items - 1;
2307 break;
2308 case VK_SPACE:
2309 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2310 else if (descr->style & LBS_MULTIPLESEL)
2312 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2313 !descr->items[descr->focus_item].selected,
2314 (descr->style & LBS_NOTIFY) != 0 );
2316 break;
2317 default:
2318 bForceSelection = FALSE;
2320 if (bForceSelection) /* focused item is used instead of key */
2321 caret = descr->focus_item;
2322 if (caret >= 0)
2324 if ((descr->style & LBS_EXTENDEDSEL) &&
2325 !(GetKeyState( VK_SHIFT ) & 0x8000))
2326 descr->anchor_item = caret;
2327 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2328 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2329 if (descr->style & LBS_NOTIFY)
2331 if( descr->lphc )
2333 /* make sure that combo parent doesn't hide us */
2334 descr->lphc->wState |= CBF_NOROLLUP;
2336 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2339 return 0;
2343 /***********************************************************************
2344 * LISTBOX_HandleChar
2346 static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2348 INT caret = -1;
2349 WCHAR str[2];
2351 str[0] = charW;
2352 str[1] = '\0';
2354 if (descr->style & LBS_WANTKEYBOARDINPUT)
2356 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2357 MAKEWPARAM(charW, descr->focus_item),
2358 (LPARAM)hwnd );
2359 if (caret == -2) return 0;
2361 if (caret == -1)
2362 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2363 if (caret != -1)
2365 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2366 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2367 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2368 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2369 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2371 return 0;
2375 /***********************************************************************
2376 * LISTBOX_Create
2378 static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2380 LB_DESCR *descr;
2381 MEASUREITEMSTRUCT mis;
2382 RECT rect;
2384 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2385 return FALSE;
2387 GetClientRect( hwnd, &rect );
2388 descr->owner = GetParent( hwnd );
2389 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2390 descr->width = rect.right - rect.left;
2391 descr->height = rect.bottom - rect.top;
2392 descr->items = NULL;
2393 descr->nb_items = 0;
2394 descr->top_item = 0;
2395 descr->selected_item = -1;
2396 descr->focus_item = 0;
2397 descr->anchor_item = -1;
2398 descr->item_height = 1;
2399 descr->page_size = 1;
2400 descr->column_width = 150;
2401 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2402 descr->horz_pos = 0;
2403 descr->nb_tabs = 0;
2404 descr->tabs = NULL;
2405 descr->caret_on = lphc ? FALSE : TRUE;
2406 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2407 descr->in_focus = FALSE;
2408 descr->captured = FALSE;
2409 descr->font = 0;
2410 descr->locale = 0; /* FIXME */
2411 descr->lphc = lphc;
2413 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2415 /* Win95 document "List Box Differences" from MSDN:
2416 If a list box in a version 3.x application has either the
2417 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2418 horizontal and vertical scroll bars.
2420 descr->style |= WS_VSCROLL | WS_HSCROLL;
2423 if( lphc )
2425 TRACE_(combo)("[%p]: resetting owner %p -> %p\n", hwnd, descr->owner, lphc->self );
2426 descr->owner = lphc->self;
2429 SetWindowLongA( hwnd, 0, (LONG)descr );
2431 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2433 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2434 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2435 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2436 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2438 if (descr->style & LBS_OWNERDRAWFIXED)
2440 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2442 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2443 descr->item_height = lphc->fixedOwnerDrawHeight;
2445 else
2447 UINT id = GetWindowLongA( hwnd, GWL_ID );
2448 mis.CtlType = ODT_LISTBOX;
2449 mis.CtlID = id;
2450 mis.itemID = -1;
2451 mis.itemWidth = 0;
2452 mis.itemData = 0;
2453 mis.itemHeight = descr->item_height;
2454 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2455 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2459 TRACE("owner: %p, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2460 return TRUE;
2464 /***********************************************************************
2465 * LISTBOX_Destroy
2467 static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2469 LISTBOX_ResetContent( hwnd, descr );
2470 SetWindowLongA( hwnd, 0, 0 );
2471 HeapFree( GetProcessHeap(), 0, descr );
2472 return TRUE;
2476 /***********************************************************************
2477 * ListBoxWndProc_common
2479 static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2480 WPARAM wParam, LPARAM lParam, BOOL unicode )
2482 LRESULT ret;
2483 LB_DESCR *descr;
2485 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2487 if (msg == WM_CREATE)
2489 if (!LISTBOX_Create( hwnd, NULL ))
2490 return -1;
2491 TRACE("creating wnd=%p descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2492 return 0;
2494 /* Ignore all other messages before we get a WM_CREATE */
2495 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2496 DefWindowProcA( hwnd, msg, wParam, lParam );
2499 TRACE("[%p]: msg %s wp %08x lp %08lx\n",
2500 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2501 switch(msg)
2503 case LB_RESETCONTENT16:
2504 case LB_RESETCONTENT:
2505 LISTBOX_ResetContent( hwnd, descr );
2506 LISTBOX_UpdateScroll( hwnd, descr );
2507 InvalidateRect( hwnd, NULL, TRUE );
2508 return 0;
2510 case LB_ADDSTRING16:
2511 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2512 /* fall through */
2513 case LB_ADDSTRING:
2515 INT ret;
2516 LPWSTR textW;
2517 if(unicode || !HAS_STRINGS(descr))
2518 textW = (LPWSTR)lParam;
2519 else
2521 LPSTR textA = (LPSTR)lParam;
2522 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2523 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2524 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2526 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2527 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2528 if (!unicode && HAS_STRINGS(descr))
2529 HeapFree(GetProcessHeap(), 0, textW);
2530 return ret;
2533 case LB_INSERTSTRING16:
2534 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2535 wParam = (INT)(INT16)wParam;
2536 /* fall through */
2537 case LB_INSERTSTRING:
2539 INT ret;
2540 LPWSTR textW;
2541 if(unicode || !HAS_STRINGS(descr))
2542 textW = (LPWSTR)lParam;
2543 else
2545 LPSTR textA = (LPSTR)lParam;
2546 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2547 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2548 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2550 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2551 if(!unicode && HAS_STRINGS(descr))
2552 HeapFree(GetProcessHeap(), 0, textW);
2553 return ret;
2556 case LB_ADDFILE16:
2557 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2558 /* fall through */
2559 case LB_ADDFILE:
2561 INT ret;
2562 LPWSTR textW;
2563 if(unicode || !HAS_STRINGS(descr))
2564 textW = (LPWSTR)lParam;
2565 else
2567 LPSTR textA = (LPSTR)lParam;
2568 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2569 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2570 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2572 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2573 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2574 if(!unicode && HAS_STRINGS(descr))
2575 HeapFree(GetProcessHeap(), 0, textW);
2576 return ret;
2579 case LB_DELETESTRING16:
2580 case LB_DELETESTRING:
2581 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2582 return descr->nb_items;
2583 else
2584 return LB_ERR;
2586 case LB_GETITEMDATA16:
2587 case LB_GETITEMDATA:
2588 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2589 return LB_ERR;
2590 return descr->items[wParam].data;
2592 case LB_SETITEMDATA16:
2593 case LB_SETITEMDATA:
2594 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2595 return LB_ERR;
2596 descr->items[wParam].data = (DWORD)lParam;
2597 return LB_OKAY;
2599 case LB_GETCOUNT16:
2600 case LB_GETCOUNT:
2601 return descr->nb_items;
2603 case LB_GETTEXT16:
2604 lParam = (LPARAM)MapSL(lParam);
2605 /* fall through */
2606 case LB_GETTEXT:
2607 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2609 case LB_GETTEXTLEN16:
2610 /* fall through */
2611 case LB_GETTEXTLEN:
2612 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2613 return LB_ERR;
2614 if (!HAS_STRINGS(descr)) return sizeof(DWORD);
2615 if (unicode) return strlenW( descr->items[wParam].str );
2616 return WideCharToMultiByte( CP_ACP, 0, descr->items[wParam].str,
2617 strlenW(descr->items[wParam].str), NULL, 0, NULL, NULL );
2619 case LB_GETCURSEL16:
2620 case LB_GETCURSEL:
2621 if (descr->nb_items==0)
2622 return LB_ERR;
2623 if (!IS_MULTISELECT(descr))
2624 return descr->selected_item;
2625 /* else */
2626 if (descr->selected_item!=-1)
2627 return descr->selected_item;
2628 /* else */
2629 return descr->focus_item;
2630 /* otherwise, if the user tries to move the selection with the */
2631 /* arrow keys, we will give the application something to choke on */
2632 case LB_GETTOPINDEX16:
2633 case LB_GETTOPINDEX:
2634 return descr->top_item;
2636 case LB_GETITEMHEIGHT16:
2637 case LB_GETITEMHEIGHT:
2638 return LISTBOX_GetItemHeight( descr, wParam );
2640 case LB_SETITEMHEIGHT16:
2641 lParam = LOWORD(lParam);
2642 /* fall through */
2643 case LB_SETITEMHEIGHT:
2644 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2646 case LB_ITEMFROMPOINT:
2648 POINT pt;
2649 RECT rect;
2651 pt.x = LOWORD(lParam);
2652 pt.y = HIWORD(lParam);
2653 rect.left = 0;
2654 rect.top = 0;
2655 rect.right = descr->width;
2656 rect.bottom = descr->height;
2658 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2659 !PtInRect( &rect, pt ) );
2662 case LB_SETCARETINDEX16:
2663 case LB_SETCARETINDEX:
2664 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2665 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2666 return LB_ERR;
2667 else if (ISWIN31)
2668 return wParam;
2669 else
2670 return LB_OKAY;
2672 case LB_GETCARETINDEX16:
2673 case LB_GETCARETINDEX:
2674 return descr->focus_item;
2676 case LB_SETTOPINDEX16:
2677 case LB_SETTOPINDEX:
2678 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2680 case LB_SETCOLUMNWIDTH16:
2681 case LB_SETCOLUMNWIDTH:
2682 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2684 case LB_GETITEMRECT16:
2686 RECT rect;
2687 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2688 CONV_RECT32TO16( &rect, MapSL(lParam) );
2690 return ret;
2692 case LB_GETITEMRECT:
2693 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2695 case LB_FINDSTRING16:
2696 wParam = (INT)(INT16)wParam;
2697 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2698 /* fall through */
2699 case LB_FINDSTRING:
2701 INT ret;
2702 LPWSTR textW;
2703 if(unicode || !HAS_STRINGS(descr))
2704 textW = (LPWSTR)lParam;
2705 else
2707 LPSTR textA = (LPSTR)lParam;
2708 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2709 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2710 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2712 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2713 if(!unicode && HAS_STRINGS(descr))
2714 HeapFree(GetProcessHeap(), 0, textW);
2715 return ret;
2718 case LB_FINDSTRINGEXACT16:
2719 wParam = (INT)(INT16)wParam;
2720 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2721 /* fall through */
2722 case LB_FINDSTRINGEXACT:
2724 INT ret;
2725 LPWSTR textW;
2726 if(unicode || !HAS_STRINGS(descr))
2727 textW = (LPWSTR)lParam;
2728 else
2730 LPSTR textA = (LPSTR)lParam;
2731 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2732 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2733 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2735 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2736 if(!unicode && HAS_STRINGS(descr))
2737 HeapFree(GetProcessHeap(), 0, textW);
2738 return ret;
2741 case LB_SELECTSTRING16:
2742 wParam = (INT)(INT16)wParam;
2743 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2744 /* fall through */
2745 case LB_SELECTSTRING:
2747 INT index;
2748 LPWSTR textW;
2750 if(HAS_STRINGS(descr))
2751 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2752 debugstr_a((LPSTR)lParam));
2753 if(unicode || !HAS_STRINGS(descr))
2754 textW = (LPWSTR)lParam;
2755 else
2757 LPSTR textA = (LPSTR)lParam;
2758 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2759 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2760 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2762 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2763 if(!unicode && HAS_STRINGS(descr))
2764 HeapFree(GetProcessHeap(), 0, textW);
2765 if (index != LB_ERR)
2767 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2768 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2770 return index;
2773 case LB_GETSEL16:
2774 wParam = (INT)(INT16)wParam;
2775 /* fall through */
2776 case LB_GETSEL:
2777 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2778 return LB_ERR;
2779 return descr->items[wParam].selected;
2781 case LB_SETSEL16:
2782 lParam = (INT)(INT16)lParam;
2783 /* fall through */
2784 case LB_SETSEL:
2785 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2787 case LB_SETCURSEL16:
2788 wParam = (INT)(INT16)wParam;
2789 /* fall through */
2790 case LB_SETCURSEL:
2791 if (IS_MULTISELECT(descr)) return LB_ERR;
2792 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2793 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2795 case LB_GETSELCOUNT16:
2796 case LB_GETSELCOUNT:
2797 return LISTBOX_GetSelCount( descr );
2799 case LB_GETSELITEMS16:
2800 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2802 case LB_GETSELITEMS:
2803 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2805 case LB_SELITEMRANGE16:
2806 case LB_SELITEMRANGE:
2807 if (LOWORD(lParam) <= HIWORD(lParam))
2808 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2809 HIWORD(lParam), wParam );
2810 else
2811 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2812 LOWORD(lParam), wParam );
2814 case LB_SELITEMRANGEEX16:
2815 case LB_SELITEMRANGEEX:
2816 if ((INT)lParam >= (INT)wParam)
2817 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2818 else
2819 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2821 case LB_GETHORIZONTALEXTENT16:
2822 case LB_GETHORIZONTALEXTENT:
2823 return descr->horz_extent;
2825 case LB_SETHORIZONTALEXTENT16:
2826 case LB_SETHORIZONTALEXTENT:
2827 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2829 case LB_GETANCHORINDEX16:
2830 case LB_GETANCHORINDEX:
2831 return descr->anchor_item;
2833 case LB_SETANCHORINDEX16:
2834 wParam = (INT)(INT16)wParam;
2835 /* fall through */
2836 case LB_SETANCHORINDEX:
2837 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2838 return LB_ERR;
2839 descr->anchor_item = (INT)wParam;
2840 return LB_OKAY;
2842 case LB_DIR16:
2843 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2844 * be set automatically (this is different in Win32) */
2845 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2846 lParam = (LPARAM)MapSL(lParam);
2847 /* fall through */
2848 case LB_DIR:
2850 INT ret;
2851 LPWSTR textW;
2852 if(unicode)
2853 textW = (LPWSTR)lParam;
2854 else
2856 LPSTR textA = (LPSTR)lParam;
2857 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2858 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2859 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2861 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2862 if(!unicode)
2863 HeapFree(GetProcessHeap(), 0, textW);
2864 return ret;
2867 case LB_GETLOCALE:
2868 return descr->locale;
2870 case LB_SETLOCALE:
2871 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2872 return LB_OKAY;
2874 case LB_INITSTORAGE:
2875 return LISTBOX_InitStorage( hwnd, descr, wParam );
2877 case LB_SETCOUNT:
2878 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2880 case LB_SETTABSTOPS16:
2881 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2883 case LB_SETTABSTOPS:
2884 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2886 case LB_CARETON16:
2887 case LB_CARETON:
2888 if (descr->caret_on)
2889 return LB_OKAY;
2890 descr->caret_on = TRUE;
2891 if ((descr->focus_item != -1) && (descr->in_focus))
2892 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2893 return LB_OKAY;
2895 case LB_CARETOFF16:
2896 case LB_CARETOFF:
2897 if (!descr->caret_on)
2898 return LB_OKAY;
2899 descr->caret_on = FALSE;
2900 if ((descr->focus_item != -1) && (descr->in_focus))
2901 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2902 return LB_OKAY;
2904 case WM_DESTROY:
2905 return LISTBOX_Destroy( hwnd, descr );
2907 case WM_ENABLE:
2908 InvalidateRect( hwnd, NULL, TRUE );
2909 return 0;
2911 case WM_SETREDRAW:
2912 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2913 return 0;
2915 case WM_GETDLGCODE:
2916 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2918 case WM_PAINT:
2920 PAINTSTRUCT ps;
2921 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2922 ret = LISTBOX_Paint( hwnd, descr, hdc );
2923 if( !wParam ) EndPaint( hwnd, &ps );
2925 return ret;
2926 case WM_SIZE:
2927 LISTBOX_UpdateSize( hwnd, descr );
2928 return 0;
2929 case WM_GETFONT:
2930 return (LRESULT)descr->font;
2931 case WM_SETFONT:
2932 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2933 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2934 return 0;
2935 case WM_SETFOCUS:
2936 descr->in_focus = TRUE;
2937 descr->caret_on = TRUE;
2938 if (descr->focus_item != -1)
2939 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2940 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2941 return 0;
2942 case WM_KILLFOCUS:
2943 descr->in_focus = FALSE;
2944 if ((descr->focus_item != -1) && descr->caret_on)
2945 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2946 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2947 return 0;
2948 case WM_HSCROLL:
2949 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2950 case WM_VSCROLL:
2951 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2952 case WM_MOUSEWHEEL:
2953 if (wParam & (MK_SHIFT | MK_CONTROL))
2954 return DefWindowProcW( hwnd, msg, wParam, lParam );
2955 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2956 case WM_LBUTTONDOWN:
2957 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2958 (INT16)LOWORD(lParam),
2959 (INT16)HIWORD(lParam) );
2960 case WM_LBUTTONDBLCLK:
2961 if (descr->style & LBS_NOTIFY)
2962 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2963 return 0;
2964 case WM_MOUSEMOVE:
2965 if (GetCapture() == hwnd)
2966 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2967 (INT16)HIWORD(lParam) );
2968 return 0;
2969 case WM_LBUTTONUP:
2970 return LISTBOX_HandleLButtonUp( hwnd, descr );
2971 case WM_KEYDOWN:
2972 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2973 case WM_CHAR:
2975 WCHAR charW;
2976 if(unicode)
2977 charW = (WCHAR)wParam;
2978 else
2980 CHAR charA = (CHAR)wParam;
2981 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2983 return LISTBOX_HandleChar( hwnd, descr, charW );
2985 case WM_SYSTIMER:
2986 return LISTBOX_HandleSystemTimer( hwnd, descr );
2987 case WM_ERASEBKGND:
2988 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2990 RECT rect;
2991 HBRUSH hbrush = (HBRUSH)SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2992 wParam, (LPARAM)hwnd );
2993 TRACE("hbrush = %p\n", hbrush);
2994 if(!hbrush)
2995 hbrush = GetSysColorBrush(COLOR_WINDOW);
2996 if(hbrush)
2998 GetClientRect(hwnd, &rect);
2999 FillRect((HDC)wParam, &rect, hbrush);
3002 return 1;
3003 case WM_DROPFILES:
3004 if( !descr->lphc )
3005 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3006 SendMessageA( descr->owner, msg, wParam, lParam );
3007 break;
3009 default:
3010 if ((msg >= WM_USER) && (msg < 0xc000))
3011 WARN("[%p]: unknown msg %04x wp %08x lp %08lx\n",
3012 hwnd, msg, wParam, lParam );
3013 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3014 DefWindowProcA( hwnd, msg, wParam, lParam );
3016 return 0;
3019 /***********************************************************************
3020 * ListBoxWndProcA
3022 * This is just a wrapper for the real wndproc, it only does window locking
3023 * and unlocking.
3025 static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3027 if (!IsWindow(hwnd)) return 0;
3028 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3031 /***********************************************************************
3032 * ListBoxWndProcW
3034 static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3036 if (!IsWindow(hwnd)) return 0;
3037 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3040 /***********************************************************************
3041 * ComboLBWndProc_common
3043 * The real combo listbox wndproc
3045 static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3046 WPARAM wParam, LPARAM lParam, BOOL unicode )
3048 LRESULT lRet = 0;
3049 LB_DESCR *descr;
3050 LPHEADCOMBO lphc;
3052 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
3054 if (msg == WM_CREATE)
3056 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3057 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3058 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3059 return LISTBOX_Create( hwnd, lphc );
3061 /* Ignore all other messages before we get a WM_CREATE */
3062 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3063 DefWindowProcA( hwnd, msg, wParam, lParam );
3066 TRACE_(combo)("[%p]: msg %s wp %08x lp %08lx\n",
3067 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3069 if ((lphc = descr->lphc) != NULL)
3071 switch( msg )
3073 case WM_MOUSEMOVE:
3074 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3075 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3077 POINT mousePos;
3078 BOOL captured;
3079 RECT clientRect;
3081 mousePos.x = (INT16)LOWORD(lParam);
3082 mousePos.y = (INT16)HIWORD(lParam);
3085 * If we are in a dropdown combobox, we simulate that
3086 * the mouse is captured to show the tracking of the item.
3088 GetClientRect(hwnd, &clientRect);
3090 if (PtInRect( &clientRect, mousePos ))
3092 captured = descr->captured;
3093 descr->captured = TRUE;
3095 LISTBOX_HandleMouseMove( hwnd, descr,
3096 mousePos.x, mousePos.y);
3098 descr->captured = captured;
3101 else
3103 LISTBOX_HandleMouseMove( hwnd, descr,
3104 mousePos.x, mousePos.y);
3107 return 0;
3110 /* else we are in Win3.1 look, go with the default behavior. */
3111 break;
3113 case WM_LBUTTONUP:
3114 if (TWEAK_WineLook > WIN31_LOOK)
3116 POINT mousePos;
3117 RECT clientRect;
3120 * If the mouse button "up" is not in the listbox,
3121 * we make sure there is no selection by re-selecting the
3122 * item that was selected when the listbox was made visible.
3124 mousePos.x = (INT16)LOWORD(lParam);
3125 mousePos.y = (INT16)HIWORD(lParam);
3127 GetClientRect(hwnd, &clientRect);
3130 * When the user clicks outside the combobox and the focus
3131 * is lost, the owning combobox will send a fake buttonup with
3132 * 0xFFFFFFF as the mouse location, we must also revert the
3133 * selection to the original selection.
3135 if ( (lParam == (LPARAM)-1) ||
3136 (!PtInRect( &clientRect, mousePos )) )
3138 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3141 return LISTBOX_HandleLButtonUp( hwnd, descr );
3142 case WM_LBUTTONDBLCLK:
3143 case WM_LBUTTONDOWN:
3144 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3145 (INT16)LOWORD(lParam),
3146 (INT16)HIWORD(lParam) );
3147 case WM_NCACTIVATE:
3148 return FALSE;
3149 case WM_KEYDOWN:
3150 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3152 /* for some reason(?) Windows makes it possible to
3153 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3155 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3156 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3157 && (wParam == VK_DOWN || wParam == VK_UP)) )
3159 COMBO_FlipListbox( lphc, FALSE, FALSE );
3160 return 0;
3163 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3165 case LB_SETCURSEL16:
3166 case LB_SETCURSEL:
3167 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3168 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3169 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3170 return lRet;
3171 case WM_NCDESTROY:
3172 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3173 lphc->hWndLBox = 0;
3174 break;
3178 /* default handling: call listbox wnd proc */
3179 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3180 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3182 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3184 return lRet;
3187 /***********************************************************************
3188 * ComboLBWndProcA
3190 * NOTE: in Windows, winproc address of the ComboLBox is the same
3191 * as that of the Listbox.
3193 LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3195 if (!IsWindow(hwnd)) return 0;
3196 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3199 /***********************************************************************
3200 * ComboLBWndProcW
3202 LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3204 if (!IsWindow(hwnd)) return 0;
3205 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );