source: trunk/src/user32/controls/listbox.c

Last change on this file was 8543, checked in by sandervl, 23 years ago

seperate odin control dll with latest wine controls added (not activate & not finished)

File size: 103.8 KB
Line 
1/*
2 * Listbox controls
3 *
4 * Copyright 1996 Alexandre Julliard
5 *
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.
10 *
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.
15 *
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
19 */
20
21#include <string.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include "windef.h"
25#include "wingdi.h"
26#include "wine/winuser16.h"
27#include "wine/winbase16.h"
28#include "wine/unicode.h"
29#include "winuser.h"
30#include "winerror.h"
31#include "spy.h"
32#include "user.h"
33#include "controls.h"
34#include "wine/debug.h"
35
36WINE_DEFAULT_DEBUG_CHANNEL(listbox);
37WINE_DECLARE_DEBUG_CHANNEL(combo);
38
39/* Unimplemented yet:
40 * - LBS_USETABSTOPS
41 * - Locale handling
42 *
43 * Probably needs improvement:
44 * - LBS_NOSEL
45 */
46
47/* Items array granularity */
48#define LB_ARRAY_GRANULARITY 16
49
50/* Scrolling timeout in ms */
51#define LB_SCROLL_TIMEOUT 50
52
53/* Listbox system timer id */
54#define LB_TIMER_ID 2
55
56/* flag listbox changed while setredraw false - internal style */
57#define LBS_DISPLAYCHANGED 0x80000000
58
59/* Item structure */
60typedef struct
61{
62 LPWSTR str; /* Item text */
63 BOOL selected; /* Is item selected? */
64 UINT height; /* Item height (only for OWNERDRAWVARIABLE) */
65 DWORD data; /* User data */
66} LB_ITEMDATA;
67
68/* Listbox structure */
69typedef struct
70{
71 HWND owner; /* Owner window to send notifications to */
72 UINT style; /* Window style */
73 INT width; /* Window width */
74 INT height; /* Window height */
75 LB_ITEMDATA *items; /* Array of items */
76 INT nb_items; /* Number of items */
77 INT top_item; /* Top visible item */
78 INT selected_item; /* Selected item */
79 INT focus_item; /* Item that has the focus */
80 INT anchor_item; /* Anchor item for extended selection */
81 INT item_height; /* Default item height */
82 INT page_size; /* Items per listbox page */
83 INT column_width; /* Column width for multi-column listboxes */
84 INT horz_extent; /* Horizontal extent (0 if no hscroll) */
85 INT horz_pos; /* Horizontal position */
86 INT nb_tabs; /* Number of tabs in array */
87 INT *tabs; /* Array of tabs */
88 BOOL caret_on; /* Is caret on? */
89 BOOL captured; /* Is mouse captured? */
90 BOOL in_focus;
91 HFONT font; /* Current font */
92 LCID locale; /* Current locale for string comparisons */
93 LPHEADCOMBO lphc; /* ComboLBox */
94} LB_DESCR;
95
96
97#define IS_OWNERDRAW(descr) \
98 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
99
100#define HAS_STRINGS(descr) \
101 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
102
103
104#define IS_MULTISELECT(descr) \
105 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
106
107#define SEND_NOTIFICATION(hwnd,descr,code) \
108 (SendMessageW( (descr)->owner, WM_COMMAND, \
109 MAKEWPARAM( GetWindowLongA((hwnd),GWL_ID), (code)), (hwnd) ))
110
111#define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
112
113/* Current timer status */
114typedef enum
115{
116 LB_TIMER_NONE,
117 LB_TIMER_UP,
118 LB_TIMER_LEFT,
119 LB_TIMER_DOWN,
120 LB_TIMER_RIGHT
121} TIMER_DIRECTION;
122
123static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
124
125static LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
126static LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
127static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
128static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );
129
130static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect );
131
132/*********************************************************************
133 * listbox class descriptor
134 */
135const struct builtin_class_descr LISTBOX_builtin_class =
136{
137 "ListBox", /* name */
138 CS_GLOBALCLASS | CS_DBLCLKS /*| CS_PARENTDC*/, /* style */
139 ListBoxWndProcA, /* procA */
140 ListBoxWndProcW, /* procW */
141 sizeof(LB_DESCR *), /* extra */
142 IDC_ARROWA, /* cursor */
143 0 /* brush */
144};
145
146
147/*********************************************************************
148 * combolbox class descriptor
149 */
150const struct builtin_class_descr COMBOLBOX_builtin_class =
151{
152 "ComboLBox", /* name */
153 CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS, /* style */
154 ComboLBWndProcA, /* procA */
155 ComboLBWndProcW, /* procW */
156 sizeof(LB_DESCR *), /* extra */
157 IDC_ARROWA, /* cursor */
158 0 /* brush */
159};
160
161
162/* check whether app is a Win 3.1 app */
163inline static BOOL is_old_app( HWND hwnd )
164{
165 return (GetExpWinVer16( GetWindowLongA(hwnd,GWL_HINSTANCE) ) & 0xFF00 ) == 0x0300;
166}
167
168
169/***********************************************************************
170 * LISTBOX_Dump
171 */
172void LISTBOX_Dump( HWND hwnd )
173{
174 INT i;
175 LB_ITEMDATA *item;
176 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
177
178 TRACE( "Listbox:\n" );
179 TRACE( "hwnd=%04x descr=%08x items=%d top=%d\n",
180 hwnd, (UINT)descr, descr->nb_items,
181 descr->top_item );
182 for (i = 0, item = descr->items; i < descr->nb_items; i++, item++)
183 {
184 TRACE( "%4d: %-40s %d %08lx %3d\n",
185 i, debugstr_w(item->str), item->selected, item->data, item->height );
186 }
187}
188
189
190/***********************************************************************
191 * LISTBOX_GetCurrentPageSize
192 *
193 * Return the current page size
194 */
195static INT LISTBOX_GetCurrentPageSize( LB_DESCR *descr )
196{
197 INT i, height;
198 if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
199 for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
200 {
201 if ((height += descr->items[i].height) > descr->height) break;
202 }
203 if (i == descr->top_item) return 1;
204 else return i - descr->top_item;
205}
206
207
208/***********************************************************************
209 * LISTBOX_GetMaxTopIndex
210 *
211 * Return the maximum possible index for the top of the listbox.
212 */
213static INT LISTBOX_GetMaxTopIndex( LB_DESCR *descr )
214{
215 INT max, page;
216
217 if (descr->style & LBS_OWNERDRAWVARIABLE)
218 {
219 page = descr->height;
220 for (max = descr->nb_items - 1; max >= 0; max--)
221 if ((page -= descr->items[max].height) < 0) break;
222 if (max < descr->nb_items - 1) max++;
223 }
224 else if (descr->style & LBS_MULTICOLUMN)
225 {
226 if ((page = descr->width / descr->column_width) < 1) page = 1;
227 max = (descr->nb_items + descr->page_size - 1) / descr->page_size;
228 max = (max - page) * descr->page_size;
229 }
230 else
231 {
232 max = descr->nb_items - descr->page_size;
233 }
234 if (max < 0) max = 0;
235 return max;
236}
237
238
239/***********************************************************************
240 * LISTBOX_UpdateScroll
241 *
242 * Update the scrollbars. Should be called whenever the content
243 * of the listbox changes.
244 */
245static void LISTBOX_UpdateScroll( HWND hwnd, LB_DESCR *descr )
246{
247 SCROLLINFO info;
248
249 /* Check the listbox scroll bar flags individually before we call
250 SetScrollInfo otherwise when the listbox style is WS_HSCROLL and
251 no WS_VSCROLL, we end up with an uninitialized, visible horizontal
252 scroll bar when we do not need one.
253 if (!(descr->style & WS_VSCROLL)) return;
254 */
255
256 /* It is important that we check descr->style, and not wnd->dwStyle,
257 for WS_VSCROLL, as the former is exactly the one passed in
258 argument to CreateWindow.
259 In Windows (and from now on in Wine :) a listbox created
260 with such a style (no WS_SCROLL) does not update
261 the scrollbar with listbox-related data, thus letting
262 the programmer use it for his/her own purposes. */
263
264 if (descr->style & LBS_NOREDRAW) return;
265 info.cbSize = sizeof(info);
266
267 if (descr->style & LBS_MULTICOLUMN)
268 {
269 info.nMin = 0;
270 info.nMax = (descr->nb_items - 1) / descr->page_size;
271 info.nPos = descr->top_item / descr->page_size;
272 info.nPage = descr->width / descr->column_width;
273 if (info.nPage < 1) info.nPage = 1;
274 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
275 if (descr->style & LBS_DISABLENOSCROLL)
276 info.fMask |= SIF_DISABLENOSCROLL;
277 if (descr->style & WS_HSCROLL)
278 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
279 info.nMax = 0;
280 info.fMask = SIF_RANGE;
281 if (descr->style & WS_VSCROLL)
282 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
283 }
284 else
285 {
286 info.nMin = 0;
287 info.nMax = descr->nb_items - 1;
288 info.nPos = descr->top_item;
289 info.nPage = LISTBOX_GetCurrentPageSize( descr );
290 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
291 if (descr->style & LBS_DISABLENOSCROLL)
292 info.fMask |= SIF_DISABLENOSCROLL;
293 if (descr->style & WS_VSCROLL)
294 SetScrollInfo( hwnd, SB_VERT, &info, TRUE );
295
296 if (descr->horz_extent)
297 {
298 info.nMin = 0;
299 info.nMax = descr->horz_extent - 1;
300 info.nPos = descr->horz_pos;
301 info.nPage = descr->width;
302 info.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
303 if (descr->style & LBS_DISABLENOSCROLL)
304 info.fMask |= SIF_DISABLENOSCROLL;
305 if (descr->style & WS_HSCROLL)
306 SetScrollInfo( hwnd, SB_HORZ, &info, TRUE );
307 }
308 }
309}
310
311
312/***********************************************************************
313 * LISTBOX_SetTopItem
314 *
315 * Set the top item of the listbox, scrolling up or down if necessary.
316 */
317static LRESULT LISTBOX_SetTopItem( HWND hwnd, LB_DESCR *descr, INT index,
318 BOOL scroll )
319{
320 INT max = LISTBOX_GetMaxTopIndex( descr );
321 if (index > max) index = max;
322 if (index < 0) index = 0;
323 if (descr->style & LBS_MULTICOLUMN) index -= index % descr->page_size;
324 if (descr->top_item == index) return LB_OKAY;
325 if (descr->style & LBS_MULTICOLUMN)
326 {
327 INT diff = (descr->top_item - index) / descr->page_size * descr->column_width;
328 if (scroll && (abs(diff) < descr->width))
329 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
330 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
331
332 else
333 scroll = FALSE;
334 }
335 else if (scroll)
336 {
337 INT diff;
338 if (descr->style & LBS_OWNERDRAWVARIABLE)
339 {
340 INT i;
341 diff = 0;
342 if (index > descr->top_item)
343 {
344 for (i = index - 1; i >= descr->top_item; i--)
345 diff -= descr->items[i].height;
346 }
347 else
348 {
349 for (i = index; i < descr->top_item; i++)
350 diff += descr->items[i].height;
351 }
352 }
353 else
354 diff = (descr->top_item - index) * descr->item_height;
355
356 if (abs(diff) < descr->height)
357 ScrollWindowEx( hwnd, 0, diff, NULL, NULL, 0, NULL,
358 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
359 else
360 scroll = FALSE;
361 }
362 if (!scroll) InvalidateRect( hwnd, NULL, TRUE );
363 descr->top_item = index;
364 LISTBOX_UpdateScroll( hwnd, descr );
365 return LB_OKAY;
366}
367
368
369/***********************************************************************
370 * LISTBOX_UpdatePage
371 *
372 * Update the page size. Should be called when the size of
373 * the client area or the item height changes.
374 */
375static void LISTBOX_UpdatePage( HWND hwnd, LB_DESCR *descr )
376{
377 INT page_size;
378
379 if ((descr->item_height == 0) || (page_size = descr->height / descr->item_height) < 1)
380 page_size = 1;
381 if (page_size == descr->page_size) return;
382 descr->page_size = page_size;
383 if (descr->style & LBS_MULTICOLUMN)
384 InvalidateRect( hwnd, NULL, TRUE );
385 LISTBOX_SetTopItem( hwnd, descr, descr->top_item, FALSE );
386}
387
388
389/***********************************************************************
390 * LISTBOX_UpdateSize
391 *
392 * Update the size of the listbox. Should be called when the size of
393 * the client area changes.
394 */
395static void LISTBOX_UpdateSize( HWND hwnd, LB_DESCR *descr )
396{
397 RECT rect;
398
399 GetClientRect( hwnd, &rect );
400 descr->width = rect.right - rect.left;
401 descr->height = rect.bottom - rect.top;
402 if (!(descr->style & LBS_NOINTEGRALHEIGHT) && !(descr->style & LBS_OWNERDRAWVARIABLE))
403 {
404 INT remaining;
405 RECT rect;
406
407 GetWindowRect( hwnd, &rect );
408 if(descr->item_height != 0)
409 remaining = descr->height % descr->item_height;
410 else
411 remaining = 0;
412 if ((descr->height > descr->item_height) && remaining)
413 {
414 if (is_old_app(hwnd))
415 { /* give a margin for error to 16 bits programs - if we need
416 less than the height of the nonclient area, round to the
417 *next* number of items */
418 int ncheight = rect.bottom - rect.top - descr->height;
419 if ((descr->item_height - remaining) <= ncheight)
420 remaining = remaining - descr->item_height;
421 }
422 TRACE("[%04x]: changing height %d -> %d\n",
423 hwnd, descr->height, descr->height - remaining );
424 SetWindowPos( hwnd, 0, 0, 0, rect.right - rect.left,
425 rect.bottom - rect.top - remaining,
426 SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE );
427 return;
428 }
429 }
430 TRACE("[%04x]: new size = %d,%d\n", hwnd, descr->width, descr->height );
431 LISTBOX_UpdatePage( hwnd, descr );
432 LISTBOX_UpdateScroll( hwnd, descr );
433
434 /* Invalidate the focused item so it will be repainted correctly */
435 if (LISTBOX_GetItemRect( descr, descr->focus_item, &rect ) == 1)
436 {
437 InvalidateRect( hwnd, &rect, FALSE );
438 }
439}
440
441
442/***********************************************************************
443 * LISTBOX_GetItemRect
444 *
445 * Get the rectangle enclosing an item, in listbox client coordinates.
446 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
447 */
448static LRESULT LISTBOX_GetItemRect( LB_DESCR *descr, INT index, RECT *rect )
449{
450 /* Index <= 0 is legal even on empty listboxes */
451 if (index && (index >= descr->nb_items)) return -1;
452 SetRect( rect, 0, 0, descr->width, descr->height );
453 if (descr->style & LBS_MULTICOLUMN)
454 {
455 INT col = (index / descr->page_size) -
456 (descr->top_item / descr->page_size);
457 rect->left += col * descr->column_width;
458 rect->right = rect->left + descr->column_width;
459 rect->top += (index % descr->page_size) * descr->item_height;
460 rect->bottom = rect->top + descr->item_height;
461 }
462 else if (descr->style & LBS_OWNERDRAWVARIABLE)
463 {
464 INT i;
465 rect->right += descr->horz_pos;
466 if ((index >= 0) && (index < descr->nb_items))
467 {
468 if (index < descr->top_item)
469 {
470 for (i = descr->top_item-1; i >= index; i--)
471 rect->top -= descr->items[i].height;
472 }
473 else
474 {
475 for (i = descr->top_item; i < index; i++)
476 rect->top += descr->items[i].height;
477 }
478 rect->bottom = rect->top + descr->items[index].height;
479
480 }
481 }
482 else
483 {
484 rect->top += (index - descr->top_item) * descr->item_height;
485 rect->bottom = rect->top + descr->item_height;
486 rect->right += descr->horz_pos;
487 }
488
489 return ((rect->left < descr->width) && (rect->right > 0) &&
490 (rect->top < descr->height) && (rect->bottom > 0));
491}
492
493
494/***********************************************************************
495 * LISTBOX_GetItemFromPoint
496 *
497 * Return the item nearest from point (x,y) (in client coordinates).
498 */
499static INT LISTBOX_GetItemFromPoint( LB_DESCR *descr, INT x, INT y )
500{
501 INT index = descr->top_item;
502
503 if (!descr->nb_items) return -1; /* No items */
504 if (descr->style & LBS_OWNERDRAWVARIABLE)
505 {
506 INT pos = 0;
507 if (y >= 0)
508 {
509 while (index < descr->nb_items)
510 {
511 if ((pos += descr->items[index].height) > y) break;
512 index++;
513 }
514 }
515 else
516 {
517 while (index > 0)
518 {
519 index--;
520 if ((pos -= descr->items[index].height) <= y) break;
521 }
522 }
523 }
524 else if (descr->style & LBS_MULTICOLUMN)
525 {
526 if (y >= descr->item_height * descr->page_size) return -1;
527 if (y >= 0) index += y / descr->item_height;
528 if (x >= 0) index += (x / descr->column_width) * descr->page_size;
529 else index -= (((x + 1) / descr->column_width) - 1) * descr->page_size;
530 }
531 else
532 {
533 index += (y / descr->item_height);
534 }
535 if (index < 0) return 0;
536 if (index >= descr->nb_items) return -1;
537 return index;
538}
539
540
541/***********************************************************************
542 * LISTBOX_PaintItem
543 *
544 * Paint an item.
545 */
546static void LISTBOX_PaintItem( HWND hwnd, LB_DESCR *descr, HDC hdc,
547 const RECT *rect, INT index, UINT action, BOOL ignoreFocus )
548{
549 LB_ITEMDATA *item = NULL;
550 if (index < descr->nb_items) item = &descr->items[index];
551
552 if (IS_OWNERDRAW(descr))
553 {
554 DRAWITEMSTRUCT dis;
555 RECT r;
556 HRGN hrgn;
557 UINT id = GetWindowLongA( hwnd, GWL_ID );
558
559 if (!item)
560 {
561 if (action == ODA_FOCUS)
562 DrawFocusRect( hdc, rect );
563 else
564 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index,descr->nb_items);
565 return;
566 }
567
568 /* some programs mess with the clipping region when
569 drawing the item, *and* restore the previous region
570 after they are done, so a region has better to exist
571 else everything ends clipped */
572 GetClientRect(hwnd, &r);
573 hrgn = CreateRectRgnIndirect(&r);
574 SelectClipRgn( hdc, hrgn);
575 DeleteObject( hrgn );
576
577 dis.CtlType = ODT_LISTBOX;
578 dis.CtlID = id;
579 dis.hwndItem = hwnd;
580 dis.itemAction = action;
581 dis.hDC = hdc;
582 dis.itemID = index;
583 dis.itemState = 0;
584 if (item && item->selected) dis.itemState |= ODS_SELECTED;
585 if (!ignoreFocus && (descr->focus_item == index) &&
586 (descr->caret_on) &&
587 (descr->in_focus)) dis.itemState |= ODS_FOCUS;
588 if (!IsWindowEnabled(hwnd)) dis.itemState |= ODS_DISABLED;
589 dis.itemData = item ? item->data : 0;
590 dis.rcItem = *rect;
591 TRACE("[%04x]: drawitem %d (%s) action=%02x state=%02x rect=%d,%d-%d,%d\n",
592 hwnd, index, item ? debugstr_w(item->str) : "", action,
593 dis.itemState, rect->left, rect->top, rect->right, rect->bottom );
594 SendMessageW(descr->owner, WM_DRAWITEM, id, (LPARAM)&dis);
595 }
596 else
597 {
598 COLORREF oldText = 0, oldBk = 0;
599
600 if (action == ODA_FOCUS)
601 {
602 DrawFocusRect( hdc, rect );
603 return;
604 }
605 if (item && item->selected)
606 {
607 oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
608 oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
609 }
610
611 TRACE("[%04x]: painting %d (%s) action=%02x rect=%d,%d-%d,%d\n",
612 hwnd, index, item ? debugstr_w(item->str) : "", action,
613 rect->left, rect->top, rect->right, rect->bottom );
614 if (!item)
615 ExtTextOutW( hdc, rect->left + 1, rect->top,
616 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
617 else if (!(descr->style & LBS_USETABSTOPS))
618 ExtTextOutW( hdc, rect->left + 1, rect->top,
619 ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
620 strlenW(item->str), NULL );
621 else
622 {
623 /* Output empty string to paint background in the full width. */
624 ExtTextOutW( hdc, rect->left + 1, rect->top,
625 ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
626 TabbedTextOutW( hdc, rect->left + 1 , rect->top,
627 item->str, strlenW(item->str),
628 descr->nb_tabs, descr->tabs, 0);
629 }
630 if (item && item->selected)
631 {
632 SetBkColor( hdc, oldBk );
633 SetTextColor( hdc, oldText );
634 }
635 if (!ignoreFocus && (descr->focus_item == index) &&
636 (descr->caret_on) &&
637 (descr->in_focus)) DrawFocusRect( hdc, rect );
638 }
639}
640
641
642/***********************************************************************
643 * LISTBOX_SetRedraw
644 *
645 * Change the redraw flag.
646 */
647static void LISTBOX_SetRedraw( HWND hwnd, LB_DESCR *descr, BOOL on )
648{
649 if (on)
650 {
651 if (!(descr->style & LBS_NOREDRAW)) return;
652 descr->style &= ~LBS_NOREDRAW;
653 if (descr->style & LBS_DISPLAYCHANGED)
654 { /* page was changed while setredraw false, refresh automatically */
655 InvalidateRect(hwnd, NULL, TRUE);
656 if ((descr->top_item + descr->page_size) > descr->nb_items)
657 { /* reset top of page if less than number of items/page */
658 descr->top_item = descr->nb_items - descr->page_size;
659 if (descr->top_item < 0) descr->top_item = 0;
660 }
661 descr->style &= ~LBS_DISPLAYCHANGED;
662 }
663 LISTBOX_UpdateScroll( hwnd, descr );
664 }
665 else descr->style |= LBS_NOREDRAW;
666}
667
668
669/***********************************************************************
670 * LISTBOX_RepaintItem
671 *
672 * Repaint a single item synchronously.
673 */
674static void LISTBOX_RepaintItem( HWND hwnd, LB_DESCR *descr, INT index,
675 UINT action )
676{
677 HDC hdc;
678 RECT rect;
679 HFONT oldFont = 0;
680 HBRUSH hbrush, oldBrush = 0;
681
682 /* Do not repaint the item if the item is not visible */
683 if (!IsWindowVisible(hwnd)) return;
684 if (descr->style & LBS_NOREDRAW)
685 {
686 descr->style |= LBS_DISPLAYCHANGED;
687 return;
688 }
689 if (LISTBOX_GetItemRect( descr, index, &rect ) != 1) return;
690 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE ))) return;
691 if (descr->font) oldFont = SelectObject( hdc, descr->font );
692 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
693 hdc, (LPARAM)hwnd );
694 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
695 if (!IsWindowEnabled(hwnd))
696 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
697 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
698 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, index, action, FALSE );
699 if (oldFont) SelectObject( hdc, oldFont );
700 if (oldBrush) SelectObject( hdc, oldBrush );
701 ReleaseDC( hwnd, hdc );
702}
703
704
705/***********************************************************************
706 * LISTBOX_InitStorage
707 */
708static LRESULT LISTBOX_InitStorage( HWND hwnd, LB_DESCR *descr, INT nb_items )
709{
710 LB_ITEMDATA *item;
711
712 nb_items += LB_ARRAY_GRANULARITY - 1;
713 nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
714 if (descr->items)
715 nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
716 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
717 nb_items * sizeof(LB_ITEMDATA) )))
718 {
719 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
720 return LB_ERRSPACE;
721 }
722 descr->items = item;
723 return LB_OKAY;
724}
725
726
727/***********************************************************************
728 * LISTBOX_SetTabStops
729 */
730static BOOL LISTBOX_SetTabStops( HWND hwnd, LB_DESCR *descr, INT count,
731 LPINT tabs, BOOL short_ints )
732{
733 if (!(descr->style & LBS_USETABSTOPS)) return TRUE;
734 if (descr->tabs) HeapFree( GetProcessHeap(), 0, descr->tabs );
735 if (!(descr->nb_tabs = count))
736 {
737 descr->tabs = NULL;
738 return TRUE;
739 }
740 /* FIXME: count = 1 */
741 if (!(descr->tabs = (INT *)HeapAlloc( GetProcessHeap(), 0,
742 descr->nb_tabs * sizeof(INT) )))
743 return FALSE;
744 if (short_ints)
745 {
746 INT i;
747 LPINT16 p = (LPINT16)tabs;
748
749 TRACE("[%04x]: settabstops ", hwnd );
750 for (i = 0; i < descr->nb_tabs; i++) {
751 descr->tabs[i] = *p++<<1; /* FIXME */
752 if (TRACE_ON(listbox)) DPRINTF("%hd ", descr->tabs[i]);
753 }
754 if (TRACE_ON(listbox)) DPRINTF("\n");
755 }
756 else memcpy( descr->tabs, tabs, descr->nb_tabs * sizeof(INT) );
757 /* FIXME: repaint the window? */
758 return TRUE;
759}
760
761
762/***********************************************************************
763 * LISTBOX_GetText
764 */
765static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPARAM lParam, BOOL unicode )
766{
767 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
768 if (HAS_STRINGS(descr))
769 {
770 if (!lParam)
771 return strlenW(descr->items[index].str);
772
773 TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
774
775 if(unicode)
776 {
777 LPWSTR buffer = (LPWSTR)lParam;
778 strcpyW( buffer, descr->items[index].str );
779 return strlenW(buffer);
780 }
781 else
782 {
783 LPSTR buffer = (LPSTR)lParam;
784 return WideCharToMultiByte(CP_ACP, 0, descr->items[index].str, -1, buffer, 0x7FFFFFFF, NULL, NULL) - 1;
785 }
786 } else {
787 if (lParam)
788 *((LPDWORD)lParam)=*(LPDWORD)(&descr->items[index].data);
789 return sizeof(DWORD);
790 }
791}
792
793
794/***********************************************************************
795 * LISTBOX_FindStringPos
796 *
797 * Find the nearest string located before a given string in sort order.
798 * If 'exact' is TRUE, return an error if we don't get an exact match.
799 */
800static INT LISTBOX_FindStringPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str,
801 BOOL exact )
802{
803 INT index, min, max, res = -1;
804
805 if (!(descr->style & LBS_SORT)) return -1; /* Add it at the end */
806 min = 0;
807 max = descr->nb_items;
808 while (min != max)
809 {
810 index = (min + max) / 2;
811 if (HAS_STRINGS(descr))
812 res = lstrcmpiW( descr->items[index].str, str );
813 else
814 {
815 COMPAREITEMSTRUCT cis;
816 UINT id = GetWindowLongA( hwnd, GWL_ID );
817
818 cis.CtlType = ODT_LISTBOX;
819 cis.CtlID = id;
820 cis.hwndItem = hwnd;
821 cis.itemID1 = index;
822 cis.itemData1 = descr->items[index].data;
823 cis.itemID2 = -1;
824 cis.itemData2 = (DWORD)str;
825 cis.dwLocaleId = descr->locale;
826 res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
827 }
828 if (!res) return index;
829 if (res > 0) max = index;
830 else min = index + 1;
831 }
832 return exact ? -1 : max;
833}
834
835
836/***********************************************************************
837 * LISTBOX_FindFileStrPos
838 *
839 * Find the nearest string located before a given string in directory
840 * sort order (i.e. first files, then directories, then drives).
841 */
842static INT LISTBOX_FindFileStrPos( HWND hwnd, LB_DESCR *descr, LPCWSTR str )
843{
844 INT min, max, res = -1;
845
846 if (!HAS_STRINGS(descr))
847 return LISTBOX_FindStringPos( hwnd, descr, str, FALSE );
848 min = 0;
849 max = descr->nb_items;
850 while (min != max)
851 {
852 INT index = (min + max) / 2;
853 LPCWSTR p = descr->items[index].str;
854 if (*p == '[') /* drive or directory */
855 {
856 if (*str != '[') res = -1;
857 else if (p[1] == '-') /* drive */
858 {
859 if (str[1] == '-') res = str[2] - p[2];
860 else res = -1;
861 }
862 else /* directory */
863 {
864 if (str[1] == '-') res = 1;
865 else res = lstrcmpiW( str, p );
866 }
867 }
868 else /* filename */
869 {
870 if (*str == '[') res = 1;
871 else res = lstrcmpiW( str, p );
872 }
873 if (!res) return index;
874 if (res < 0) max = index;
875 else min = index + 1;
876 }
877 return max;
878}
879
880
881/***********************************************************************
882 * LISTBOX_FindString
883 *
884 * Find the item beginning with a given string.
885 */
886static INT LISTBOX_FindString( HWND hwnd, LB_DESCR *descr, INT start,
887 LPCWSTR str, BOOL exact )
888{
889 INT i;
890 LB_ITEMDATA *item;
891
892 if (start >= descr->nb_items) start = -1;
893 item = descr->items + start + 1;
894 if (HAS_STRINGS(descr))
895 {
896 if (!str || ! str[0] ) return LB_ERR;
897 if (exact)
898 {
899 for (i = start + 1; i < descr->nb_items; i++, item++)
900 if (!lstrcmpiW( str, item->str )) return i;
901 for (i = 0, item = descr->items; i <= start; i++, item++)
902 if (!lstrcmpiW( str, item->str )) return i;
903 }
904 else
905 {
906 /* Special case for drives and directories: ignore prefix */
907#define CHECK_DRIVE(item) \
908 if ((item)->str[0] == '[') \
909 { \
910 if (!strncmpiW( str, (item)->str+1, len )) return i; \
911 if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
912 return i; \
913 }
914
915 INT len = strlenW(str);
916 for (i = start + 1; i < descr->nb_items; i++, item++)
917 {
918 if (!strncmpiW( str, item->str, len )) return i;
919 CHECK_DRIVE(item);
920 }
921 for (i = 0, item = descr->items; i <= start; i++, item++)
922 {
923 if (!strncmpiW( str, item->str, len )) return i;
924 CHECK_DRIVE(item);
925 }
926#undef CHECK_DRIVE
927 }
928 }
929 else
930 {
931 if (exact && (descr->style & LBS_SORT))
932 /* If sorted, use a WM_COMPAREITEM binary search */
933 return LISTBOX_FindStringPos( hwnd, descr, str, TRUE );
934
935 /* Otherwise use a linear search */
936 for (i = start + 1; i < descr->nb_items; i++, item++)
937 if (item->data == (DWORD)str) return i;
938 for (i = 0, item = descr->items; i <= start; i++, item++)
939 if (item->data == (DWORD)str) return i;
940 }
941 return LB_ERR;
942}
943
944
945/***********************************************************************
946 * LISTBOX_GetSelCount
947 */
948static LRESULT LISTBOX_GetSelCount( LB_DESCR *descr )
949{
950 INT i, count;
951 LB_ITEMDATA *item = descr->items;
952
953 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
954 for (i = count = 0; i < descr->nb_items; i++, item++)
955 if (item->selected) count++;
956 return count;
957}
958
959
960/***********************************************************************
961 * LISTBOX_GetSelItems16
962 */
963static LRESULT LISTBOX_GetSelItems16( LB_DESCR *descr, INT16 max, LPINT16 array )
964{
965 INT i, count;
966 LB_ITEMDATA *item = descr->items;
967
968 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
969 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
970 if (item->selected) array[count++] = (INT16)i;
971 return count;
972}
973
974
975/***********************************************************************
976 * LISTBOX_GetSelItems
977 */
978static LRESULT LISTBOX_GetSelItems( LB_DESCR *descr, INT max, LPINT array )
979{
980 INT i, count;
981 LB_ITEMDATA *item = descr->items;
982
983 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
984 for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
985 if (item->selected) array[count++] = i;
986 return count;
987}
988
989
990/***********************************************************************
991 * LISTBOX_Paint
992 */
993static LRESULT LISTBOX_Paint( HWND hwnd, LB_DESCR *descr, HDC hdc )
994{
995 INT i, col_pos = descr->page_size - 1;
996 RECT rect;
997 RECT focusRect = {-1, -1, -1, -1};
998 HFONT oldFont = 0;
999 HBRUSH hbrush, oldBrush = 0;
1000
1001 if (descr->style & LBS_NOREDRAW) return 0;
1002
1003 SetRect( &rect, 0, 0, descr->width, descr->height );
1004 if (descr->style & LBS_MULTICOLUMN)
1005 rect.right = rect.left + descr->column_width;
1006 else if (descr->horz_pos)
1007 {
1008 SetWindowOrgEx( hdc, descr->horz_pos, 0, NULL );
1009 rect.right += descr->horz_pos;
1010 }
1011
1012 if (descr->font) oldFont = SelectObject( hdc, descr->font );
1013 hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
1014 hdc, (LPARAM)hwnd );
1015 if (hbrush) oldBrush = SelectObject( hdc, hbrush );
1016 if (!IsWindowEnabled(hwnd)) SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1017
1018 if (!descr->nb_items && (descr->focus_item != -1) && descr->caret_on &&
1019 (descr->in_focus))
1020 {
1021 /* Special case for empty listbox: paint focus rect */
1022 rect.bottom = rect.top + descr->item_height;
1023 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, descr->focus_item,
1024 ODA_FOCUS, FALSE );
1025 rect.top = rect.bottom;
1026 }
1027
1028 /* Paint all the item, regarding the selection
1029 Focus state will be painted after */
1030
1031 for (i = descr->top_item; i < descr->nb_items; i++)
1032 {
1033 if (!(descr->style & LBS_OWNERDRAWVARIABLE))
1034 rect.bottom = rect.top + descr->item_height;
1035 else
1036 rect.bottom = rect.top + descr->items[i].height;
1037
1038 if (i == descr->focus_item)
1039 {
1040 /* keep the focus rect, to paint the focus item after */
1041 focusRect.left = rect.left;
1042 focusRect.right = rect.right;
1043 focusRect.top = rect.top;
1044 focusRect.bottom = rect.bottom;
1045 }
1046 LISTBOX_PaintItem( hwnd, descr, hdc, &rect, i, ODA_DRAWENTIRE, TRUE );
1047 rect.top = rect.bottom;
1048
1049 if ((descr->style & LBS_MULTICOLUMN) && !col_pos)
1050 {
1051 if (!IS_OWNERDRAW(descr))
1052 {
1053 /* Clear the bottom of the column */
1054 if (rect.top < descr->height)
1055 {
1056 rect.bottom = descr->height;
1057 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1058 &rect, NULL, 0, NULL );
1059 }
1060 }
1061
1062 /* Go to the next column */
1063 rect.left += descr->column_width;
1064 rect.right += descr->column_width;
1065 rect.top = 0;
1066 col_pos = descr->page_size - 1;
1067 }
1068 else
1069 {
1070 col_pos--;
1071 if (rect.top >= descr->height) break;
1072 }
1073 }
1074
1075 /* Paint the focus item now */
1076 if (focusRect.top != focusRect.bottom && descr->caret_on)
1077 LISTBOX_PaintItem( hwnd, descr, hdc, &focusRect, descr->focus_item, ODA_FOCUS, FALSE );
1078
1079 if (!IS_OWNERDRAW(descr))
1080 {
1081 /* Clear the remainder of the client area */
1082 if (rect.top < descr->height)
1083 {
1084 rect.bottom = descr->height;
1085 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1086 &rect, NULL, 0, NULL );
1087 }
1088 if (rect.right < descr->width)
1089 {
1090 rect.left = rect.right;
1091 rect.right = descr->width;
1092 rect.top = 0;
1093 rect.bottom = descr->height;
1094 ExtTextOutW( hdc, 0, 0, ETO_OPAQUE | ETO_CLIPPED,
1095 &rect, NULL, 0, NULL );
1096 }
1097 }
1098 if (oldFont) SelectObject( hdc, oldFont );
1099 if (oldBrush) SelectObject( hdc, oldBrush );
1100 return 0;
1101}
1102
1103
1104/***********************************************************************
1105 * LISTBOX_InvalidateItems
1106 *
1107 * Invalidate all items from a given item. If the specified item is not
1108 * visible, nothing happens.
1109 */
1110static void LISTBOX_InvalidateItems( HWND hwnd, LB_DESCR *descr, INT index )
1111{
1112 RECT rect;
1113
1114 if (LISTBOX_GetItemRect( descr, index, &rect ) == 1)
1115 {
1116 if (descr->style & LBS_NOREDRAW)
1117 {
1118 descr->style |= LBS_DISPLAYCHANGED;
1119 return;
1120 }
1121 rect.bottom = descr->height;
1122 InvalidateRect( hwnd, &rect, TRUE );
1123 if (descr->style & LBS_MULTICOLUMN)
1124 {
1125 /* Repaint the other columns */
1126 rect.left = rect.right;
1127 rect.right = descr->width;
1128 rect.top = 0;
1129 InvalidateRect( hwnd, &rect, TRUE );
1130 }
1131 }
1132}
1133
1134
1135/***********************************************************************
1136 * LISTBOX_GetItemHeight
1137 */
1138static LRESULT LISTBOX_GetItemHeight( LB_DESCR *descr, INT index )
1139{
1140 if (descr->style & LBS_OWNERDRAWVARIABLE)
1141 {
1142 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1143 return descr->items[index].height;
1144 }
1145 else return descr->item_height;
1146}
1147
1148
1149/***********************************************************************
1150 * LISTBOX_SetItemHeight
1151 */
1152static LRESULT LISTBOX_SetItemHeight( HWND hwnd, LB_DESCR *descr, INT index,
1153 INT height, BOOL repaint )
1154{
1155 if (!height) height = 1;
1156
1157 if (descr->style & LBS_OWNERDRAWVARIABLE)
1158 {
1159 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1160 TRACE("[%04x]: item %d height = %d\n", hwnd, index, height );
1161 descr->items[index].height = height;
1162 LISTBOX_UpdateScroll( hwnd, descr );
1163 if (repaint)
1164 LISTBOX_InvalidateItems( hwnd, descr, index );
1165 }
1166 else if (height != descr->item_height)
1167 {
1168 TRACE("[%04x]: new height = %d\n", hwnd, height );
1169 descr->item_height = height;
1170 LISTBOX_UpdatePage( hwnd, descr );
1171 LISTBOX_UpdateScroll( hwnd, descr );
1172 if (repaint)
1173 InvalidateRect( hwnd, 0, TRUE );
1174 }
1175 return LB_OKAY;
1176}
1177
1178
1179/***********************************************************************
1180 * LISTBOX_SetHorizontalPos
1181 */
1182static void LISTBOX_SetHorizontalPos( HWND hwnd, LB_DESCR *descr, INT pos )
1183{
1184 INT diff;
1185
1186 if (pos > descr->horz_extent - descr->width)
1187 pos = descr->horz_extent - descr->width;
1188 if (pos < 0) pos = 0;
1189 if (!(diff = descr->horz_pos - pos)) return;
1190 TRACE("[%04x]: new horz pos = %d\n", hwnd, pos );
1191 descr->horz_pos = pos;
1192 LISTBOX_UpdateScroll( hwnd, descr );
1193 if (abs(diff) < descr->width)
1194 ScrollWindowEx( hwnd, diff, 0, NULL, NULL, 0, NULL,
1195 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
1196 else
1197 InvalidateRect( hwnd, NULL, TRUE );
1198}
1199
1200
1201/***********************************************************************
1202 * LISTBOX_SetHorizontalExtent
1203 */
1204static LRESULT LISTBOX_SetHorizontalExtent( HWND hwnd, LB_DESCR *descr,
1205 INT extent )
1206{
1207 if (!descr->horz_extent || (descr->style & LBS_MULTICOLUMN))
1208 return LB_OKAY;
1209 if (extent <= 0) extent = 1;
1210 if (extent == descr->horz_extent) return LB_OKAY;
1211 TRACE("[%04x]: new horz extent = %d\n", hwnd, extent );
1212 descr->horz_extent = extent;
1213 if (descr->horz_pos > extent - descr->width)
1214 LISTBOX_SetHorizontalPos( hwnd, descr, extent - descr->width );
1215 else
1216 LISTBOX_UpdateScroll( hwnd, descr );
1217 return LB_OKAY;
1218}
1219
1220
1221/***********************************************************************
1222 * LISTBOX_SetColumnWidth
1223 */
1224static LRESULT LISTBOX_SetColumnWidth( HWND hwnd, LB_DESCR *descr, INT width)
1225{
1226 if (width == descr->column_width) return LB_OKAY;
1227 TRACE("[%04x]: new column width = %d\n", hwnd, width );
1228 descr->column_width = width;
1229 LISTBOX_UpdatePage( hwnd, descr );
1230 return LB_OKAY;
1231}
1232
1233
1234/***********************************************************************
1235 * LISTBOX_SetFont
1236 *
1237 * Returns the item height.
1238 */
1239static INT LISTBOX_SetFont( HWND hwnd, LB_DESCR *descr, HFONT font )
1240{
1241 HDC hdc;
1242 HFONT oldFont = 0;
1243 TEXTMETRICW tm;
1244
1245 descr->font = font;
1246
1247 if (!(hdc = GetDCEx( hwnd, 0, DCX_CACHE )))
1248 {
1249 ERR("unable to get DC.\n" );
1250 return 16;
1251 }
1252 if (font) oldFont = SelectObject( hdc, font );
1253 GetTextMetricsW( hdc, &tm );
1254 if (oldFont) SelectObject( hdc, oldFont );
1255 ReleaseDC( hwnd, hdc );
1256 if (!IS_OWNERDRAW(descr))
1257 LISTBOX_SetItemHeight( hwnd, descr, 0, tm.tmHeight, FALSE );
1258 return tm.tmHeight ;
1259}
1260
1261
1262/***********************************************************************
1263 * LISTBOX_MakeItemVisible
1264 *
1265 * Make sure that a given item is partially or fully visible.
1266 */
1267static void LISTBOX_MakeItemVisible( HWND hwnd, LB_DESCR *descr, INT index,
1268 BOOL fully )
1269{
1270 INT top;
1271
1272 if (index <= descr->top_item) top = index;
1273 else if (descr->style & LBS_MULTICOLUMN)
1274 {
1275 INT cols = descr->width;
1276 if (!fully) cols += descr->column_width - 1;
1277 if (cols >= descr->column_width) cols /= descr->column_width;
1278 else cols = 1;
1279 if (index < descr->top_item + (descr->page_size * cols)) return;
1280 top = index - descr->page_size * (cols - 1);
1281 }
1282 else if (descr->style & LBS_OWNERDRAWVARIABLE)
1283 {
1284 INT height = fully ? descr->items[index].height : 1;
1285 for (top = index; top > descr->top_item; top--)
1286 if ((height += descr->items[top-1].height) > descr->height) break;
1287 }
1288 else
1289 {
1290 if (index < descr->top_item + descr->page_size) return;
1291 if (!fully && (index == descr->top_item + descr->page_size) &&
1292 (descr->height > (descr->page_size * descr->item_height))) return;
1293 top = index - descr->page_size + 1;
1294 }
1295 LISTBOX_SetTopItem( hwnd, descr, top, TRUE );
1296}
1297
1298/***********************************************************************
1299 * LISTBOX_SetCaretIndex
1300 *
1301 * NOTES
1302 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1303 *
1304 */
1305static LRESULT LISTBOX_SetCaretIndex( HWND hwnd, LB_DESCR *descr, INT index,
1306 BOOL fully_visible )
1307{
1308 INT oldfocus = descr->focus_item;
1309
1310 if (descr->style & LBS_NOSEL) return LB_ERR;
1311 if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1312 if (index == oldfocus) return LB_OKAY;
1313 descr->focus_item = index;
1314 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1315 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1316
1317 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1318 if (descr->caret_on && (descr->in_focus))
1319 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1320
1321 return LB_OKAY;
1322}
1323
1324
1325/***********************************************************************
1326 * LISTBOX_SelectItemRange
1327 *
1328 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1329 */
1330static LRESULT LISTBOX_SelectItemRange( HWND hwnd, LB_DESCR *descr, INT first,
1331 INT last, BOOL on )
1332{
1333 INT i;
1334
1335 /* A few sanity checks */
1336
1337 if (descr->style & LBS_NOSEL) return LB_ERR;
1338 if ((last == -1) && (descr->nb_items == 0)) return LB_OKAY;
1339 if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
1340 if (last == -1) last = descr->nb_items - 1;
1341 if ((first < 0) || (first >= descr->nb_items)) return LB_ERR;
1342 if ((last < 0) || (last >= descr->nb_items)) return LB_ERR;
1343 /* selected_item reflects last selected/unselected item on multiple sel */
1344 descr->selected_item = last;
1345
1346 if (on) /* Turn selection on */
1347 {
1348 for (i = first; i <= last; i++)
1349 {
1350 if (descr->items[i].selected) continue;
1351 descr->items[i].selected = TRUE;
1352 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1353 }
1354 LISTBOX_SetCaretIndex( hwnd, descr, last, TRUE );
1355 }
1356 else /* Turn selection off */
1357 {
1358 for (i = first; i <= last; i++)
1359 {
1360 if (!descr->items[i].selected) continue;
1361 descr->items[i].selected = FALSE;
1362 LISTBOX_RepaintItem( hwnd, descr, i, ODA_SELECT );
1363 }
1364 }
1365 return LB_OKAY;
1366}
1367
1368/***********************************************************************
1369 * LISTBOX_SetSelection
1370 */
1371static LRESULT LISTBOX_SetSelection( HWND hwnd, LB_DESCR *descr, INT index,
1372 BOOL on, BOOL send_notify )
1373{
1374 TRACE( "index=%d notify=%s\n", index, send_notify ? "YES" : "NO" );
1375
1376 if (descr->style & LBS_NOSEL) return LB_ERR;
1377 if ((index < -1) || (index >= descr->nb_items)) return LB_ERR;
1378 if (descr->style & LBS_MULTIPLESEL)
1379 {
1380 if (index == -1) /* Select all items */
1381 return LISTBOX_SelectItemRange( hwnd, descr, 0, -1, on );
1382 else /* Only one item */
1383 return LISTBOX_SelectItemRange( hwnd, descr, index, index, on );
1384 }
1385 else
1386 {
1387 INT oldsel = descr->selected_item;
1388 if (index == oldsel) return LB_OKAY;
1389 if (oldsel != -1) descr->items[oldsel].selected = FALSE;
1390 if (index != -1) descr->items[index].selected = TRUE;
1391 descr->selected_item = index;
1392 if (oldsel != -1) LISTBOX_RepaintItem( hwnd, descr, oldsel, ODA_SELECT );
1393 if (index != -1) LISTBOX_RepaintItem( hwnd, descr, index, ODA_SELECT );
1394 if (send_notify && descr->nb_items) SEND_NOTIFICATION( hwnd, descr,
1395 (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
1396 else
1397 if( descr->lphc ) /* set selection change flag for parent combo */
1398 descr->lphc->wState |= CBF_SELCHANGE;
1399 }
1400 return LB_OKAY;
1401}
1402
1403
1404/***********************************************************************
1405 * LISTBOX_MoveCaret
1406 *
1407 * Change the caret position and extend the selection to the new caret.
1408 */
1409static void LISTBOX_MoveCaret( HWND hwnd, LB_DESCR *descr, INT index,
1410 BOOL fully_visible )
1411{
1412 INT oldfocus = descr->focus_item;
1413
1414 if ((index < 0) || (index >= descr->nb_items))
1415 return;
1416
1417 /* Important, repaint needs to be done in this order if
1418 you want to mimic Windows behavior:
1419 1. Remove the focus and paint the item
1420 2. Remove the selection and paint the item(s)
1421 3. Set the selection and repaint the item(s)
1422 4. Set the focus to 'index' and repaint the item */
1423
1424 /* 1. remove the focus and repaint the item */
1425 descr->focus_item = -1;
1426 if ((oldfocus != -1) && descr->caret_on && (descr->in_focus))
1427 LISTBOX_RepaintItem( hwnd, descr, oldfocus, ODA_FOCUS );
1428
1429 /* 2. then turn off the previous selection */
1430 /* 3. repaint the new selected item */
1431 if (descr->style & LBS_EXTENDEDSEL)
1432 {
1433 if (descr->anchor_item != -1)
1434 {
1435 INT first = min( index, descr->anchor_item );
1436 INT last = max( index, descr->anchor_item );
1437 if (first > 0)
1438 LISTBOX_SelectItemRange( hwnd, descr, 0, first - 1, FALSE );
1439 LISTBOX_SelectItemRange( hwnd, descr, last + 1, -1, FALSE );
1440 LISTBOX_SelectItemRange( hwnd, descr, first, last, TRUE );
1441 }
1442 }
1443 else if (!(descr->style & LBS_MULTIPLESEL))
1444 {
1445 /* Set selection to new caret item */
1446 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
1447 }
1448
1449 /* 4. repaint the new item with the focus */
1450 descr->focus_item = index;
1451 LISTBOX_MakeItemVisible( hwnd, descr, index, fully_visible );
1452 if (descr->caret_on && (descr->in_focus))
1453 LISTBOX_RepaintItem( hwnd, descr, index, ODA_FOCUS );
1454}
1455
1456
1457/***********************************************************************
1458 * LISTBOX_InsertItem
1459 */
1460static LRESULT LISTBOX_InsertItem( HWND hwnd, LB_DESCR *descr, INT index,
1461 LPWSTR str, DWORD data )
1462{
1463 LB_ITEMDATA *item;
1464 INT max_items;
1465 INT oldfocus = descr->focus_item;
1466
1467 if (index == -1) index = descr->nb_items;
1468 else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
1469 if (!descr->items) max_items = 0;
1470 else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
1471 if (descr->nb_items == max_items)
1472 {
1473 /* We need to grow the array */
1474 max_items += LB_ARRAY_GRANULARITY;
1475 if (!(item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1476 max_items * sizeof(LB_ITEMDATA) )))
1477 {
1478 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1479 return LB_ERRSPACE;
1480 }
1481 descr->items = item;
1482 }
1483
1484 /* Insert the item structure */
1485
1486 item = &descr->items[index];
1487 if (index < descr->nb_items)
1488 RtlMoveMemory( item + 1, item,
1489 (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
1490 item->str = str;
1491 item->data = data;
1492 item->height = 0;
1493 item->selected = FALSE;
1494 descr->nb_items++;
1495
1496 /* Get item height */
1497
1498 if (descr->style & LBS_OWNERDRAWVARIABLE)
1499 {
1500 MEASUREITEMSTRUCT mis;
1501 UINT id = GetWindowLongA( hwnd, GWL_ID );
1502
1503 mis.CtlType = ODT_LISTBOX;
1504 mis.CtlID = id;
1505 mis.itemID = index;
1506 mis.itemData = descr->items[index].data;
1507 mis.itemHeight = descr->item_height;
1508 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
1509 item->height = mis.itemHeight ? mis.itemHeight : 1;
1510 TRACE("[%04x]: measure item %d (%s) = %d\n",
1511 hwnd, index, str ? debugstr_w(str) : "", item->height );
1512 }
1513
1514 /* Repaint the items */
1515
1516 LISTBOX_UpdateScroll( hwnd, descr );
1517 LISTBOX_InvalidateItems( hwnd, descr, index );
1518
1519 /* Move selection and focused item */
1520 /* If listbox was empty, set focus to the first item */
1521 if (descr->nb_items == 1)
1522 LISTBOX_SetCaretIndex( hwnd, descr, 0, FALSE );
1523 /* single select don't change selection index in win31 */
1524 else if ((ISWIN31) && !(IS_MULTISELECT(descr)))
1525 {
1526 descr->selected_item++;
1527 LISTBOX_SetSelection( hwnd, descr, descr->selected_item-1, TRUE, FALSE );
1528 }
1529 else
1530 {
1531 if (index <= descr->selected_item)
1532 {
1533 descr->selected_item++;
1534 descr->focus_item = oldfocus; /* focus not changed */
1535 }
1536 }
1537 return LB_OKAY;
1538}
1539
1540
1541/***********************************************************************
1542 * LISTBOX_InsertString
1543 */
1544static LRESULT LISTBOX_InsertString( HWND hwnd, LB_DESCR *descr, INT index,
1545 LPCWSTR str )
1546{
1547 LPWSTR new_str = NULL;
1548 DWORD data = 0;
1549 LRESULT ret;
1550
1551 if (HAS_STRINGS(descr))
1552 {
1553 static const WCHAR empty_stringW[] = { 0 };
1554 if (!str) str = empty_stringW;
1555 if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
1556 {
1557 SEND_NOTIFICATION( hwnd, descr, LBN_ERRSPACE );
1558 return LB_ERRSPACE;
1559 }
1560 strcpyW(new_str, str);
1561 }
1562 else data = (DWORD)str;
1563
1564 if (index == -1) index = descr->nb_items;
1565 if ((ret = LISTBOX_InsertItem( hwnd, descr, index, new_str, data )) != 0)
1566 {
1567 if (new_str) HeapFree( GetProcessHeap(), 0, new_str );
1568 return ret;
1569 }
1570
1571 TRACE("[%04x]: added item %d %s\n",
1572 hwnd, index, HAS_STRINGS(descr) ? debugstr_w(new_str) : "" );
1573 return index;
1574}
1575
1576
1577/***********************************************************************
1578 * LISTBOX_DeleteItem
1579 *
1580 * Delete the content of an item. 'index' must be a valid index.
1581 */
1582static void LISTBOX_DeleteItem( HWND hwnd, LB_DESCR *descr, INT index )
1583{
1584 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1585 * while Win95 sends it for all items with user data.
1586 * It's probably better to send it too often than not
1587 * often enough, so this is what we do here.
1588 */
1589 if (IS_OWNERDRAW(descr) || descr->items[index].data)
1590 {
1591 DELETEITEMSTRUCT dis;
1592 UINT id = GetWindowLongA( hwnd, GWL_ID );
1593
1594 dis.CtlType = ODT_LISTBOX;
1595 dis.CtlID = id;
1596 dis.itemID = index;
1597 dis.hwndItem = hwnd;
1598 dis.itemData = descr->items[index].data;
1599 SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
1600 }
1601 if (HAS_STRINGS(descr) && descr->items[index].str)
1602 HeapFree( GetProcessHeap(), 0, descr->items[index].str );
1603}
1604
1605
1606/***********************************************************************
1607 * LISTBOX_RemoveItem
1608 *
1609 * Remove an item from the listbox and delete its content.
1610 */
1611static LRESULT LISTBOX_RemoveItem( HWND hwnd, LB_DESCR *descr, INT index )
1612{
1613 LB_ITEMDATA *item;
1614 INT max_items;
1615
1616 if ((index == -1) && (descr->nb_items > 0)) index = descr->nb_items - 1;
1617 else if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
1618
1619 /* We need to invalidate the original rect instead of the updated one. */
1620 LISTBOX_InvalidateItems( hwnd, descr, index );
1621
1622 LISTBOX_DeleteItem( hwnd, descr, index );
1623
1624 /* Remove the item */
1625
1626 item = &descr->items[index];
1627 if (index < descr->nb_items-1)
1628 RtlMoveMemory( item, item + 1,
1629 (descr->nb_items - index - 1) * sizeof(LB_ITEMDATA) );
1630 descr->nb_items--;
1631 if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
1632
1633 /* Shrink the item array if possible */
1634
1635 max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
1636 if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
1637 {
1638 max_items -= LB_ARRAY_GRANULARITY;
1639 item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
1640 max_items * sizeof(LB_ITEMDATA) );
1641 if (item) descr->items = item;
1642 }
1643 /* Repaint the items */
1644
1645 LISTBOX_UpdateScroll( hwnd, descr );
1646 /* if we removed the scrollbar, reset the top of the list
1647 (correct for owner-drawn ???) */
1648 if (descr->nb_items == descr->page_size)
1649 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1650
1651 /* Move selection and focused item */
1652 if (!IS_MULTISELECT(descr))
1653 {
1654 if (index == descr->selected_item)
1655 descr->selected_item = -1;
1656 else if (index < descr->selected_item)
1657 {
1658 descr->selected_item--;
1659 if (ISWIN31) /* win 31 do not change the selected item number */
1660 LISTBOX_SetSelection( hwnd, descr, descr->selected_item + 1, TRUE, FALSE);
1661 }
1662 }
1663
1664 if (descr->focus_item >= descr->nb_items)
1665 {
1666 descr->focus_item = descr->nb_items - 1;
1667 if (descr->focus_item < 0) descr->focus_item = 0;
1668 }
1669 return LB_OKAY;
1670}
1671
1672
1673/***********************************************************************
1674 * LISTBOX_ResetContent
1675 */
1676static void LISTBOX_ResetContent( HWND hwnd, LB_DESCR *descr )
1677{
1678 INT i;
1679
1680 for (i = 0; i < descr->nb_items; i++) LISTBOX_DeleteItem( hwnd, descr, i );
1681 if (descr->items) HeapFree( GetProcessHeap(), 0, descr->items );
1682 descr->nb_items = 0;
1683 descr->top_item = 0;
1684 descr->selected_item = -1;
1685 descr->focus_item = 0;
1686 descr->anchor_item = -1;
1687 descr->items = NULL;
1688}
1689
1690
1691/***********************************************************************
1692 * LISTBOX_SetCount
1693 */
1694static LRESULT LISTBOX_SetCount( HWND hwnd, LB_DESCR *descr, INT count )
1695{
1696 LRESULT ret;
1697
1698 if (HAS_STRINGS(descr)) return LB_ERR;
1699 /* FIXME: this is far from optimal... */
1700 if (count > descr->nb_items)
1701 {
1702 while (count > descr->nb_items)
1703 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, 0 )) < 0)
1704 return ret;
1705 }
1706 else if (count < descr->nb_items)
1707 {
1708 while (count < descr->nb_items)
1709 if ((ret = LISTBOX_RemoveItem( hwnd, descr, -1 )) < 0)
1710 return ret;
1711 }
1712 return LB_OKAY;
1713}
1714
1715
1716/***********************************************************************
1717 * LISTBOX_Directory
1718 */
1719static LRESULT LISTBOX_Directory( HWND hwnd, LB_DESCR *descr, UINT attrib,
1720 LPCWSTR filespec, BOOL long_names )
1721{
1722 HANDLE handle;
1723 LRESULT ret = LB_OKAY;
1724 WIN32_FIND_DATAW entry;
1725 int pos;
1726
1727 /* don't scan directory if we just want drives exclusively */
1728 if (attrib != (DDL_DRIVES | DDL_EXCLUSIVE)) {
1729 /* scan directory */
1730 if ((handle = FindFirstFileW(filespec, &entry)) == INVALID_HANDLE_VALUE)
1731 {
1732 if (GetLastError() != ERROR_NO_MORE_FILES) return LB_ERR;
1733 }
1734 else
1735 {
1736 do
1737 {
1738 WCHAR buffer[270];
1739 if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1740 {
1741 static const WCHAR bracketW[] = { ']',0 };
1742 static const WCHAR dotW[] = { '.',0 };
1743 if (!(attrib & DDL_DIRECTORY) ||
1744 !strcmpW( entry.cAlternateFileName, dotW )) continue;
1745 buffer[0] = '[';
1746 if (long_names) strcpyW( buffer + 1, entry.cFileName );
1747 else strcpyW( buffer + 1, entry.cAlternateFileName );
1748 strcatW(buffer, bracketW);
1749 }
1750 else /* not a directory */
1751 {
1752#define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1753 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1754
1755 if ((attrib & DDL_EXCLUSIVE) &&
1756 ((attrib & ATTRIBS) != (entry.dwFileAttributes & ATTRIBS)))
1757 continue;
1758#undef ATTRIBS
1759 if (long_names) strcpyW( buffer, entry.cFileName );
1760 else strcpyW( buffer, entry.cAlternateFileName );
1761 }
1762 if (!long_names) CharLowerW( buffer );
1763 pos = LISTBOX_FindFileStrPos( hwnd, descr, buffer );
1764 if ((ret = LISTBOX_InsertString( hwnd, descr, pos, buffer )) < 0)
1765 break;
1766 } while (FindNextFileW( handle, &entry ));
1767 FindClose( handle );
1768 }
1769 }
1770
1771 /* scan drives */
1772 if ((ret >= 0) && (attrib & DDL_DRIVES))
1773 {
1774 WCHAR buffer[] = {'[','-','a','-',']',0};
1775 WCHAR root[] = {'A',':','\\',0};
1776 int drive;
1777 for (drive = 0; drive < 26; drive++, buffer[2]++, root[0]++)
1778 {
1779 if (GetDriveTypeW(root) <= DRIVE_NO_ROOT_DIR) continue;
1780 if ((ret = LISTBOX_InsertString( hwnd, descr, -1, buffer )) < 0)
1781 break;
1782 }
1783 }
1784 return ret;
1785}
1786
1787
1788/***********************************************************************
1789 * LISTBOX_HandleVScroll
1790 */
1791static LRESULT LISTBOX_HandleVScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1792{
1793 SCROLLINFO info;
1794
1795 if (descr->style & LBS_MULTICOLUMN) return 0;
1796 switch(LOWORD(wParam))
1797 {
1798 case SB_LINEUP:
1799 LISTBOX_SetTopItem( hwnd, descr, descr->top_item - 1, TRUE );
1800 break;
1801 case SB_LINEDOWN:
1802 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + 1, TRUE );
1803 break;
1804 case SB_PAGEUP:
1805 LISTBOX_SetTopItem( hwnd, descr, descr->top_item -
1806 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1807 break;
1808 case SB_PAGEDOWN:
1809 LISTBOX_SetTopItem( hwnd, descr, descr->top_item +
1810 LISTBOX_GetCurrentPageSize( descr ), TRUE );
1811 break;
1812 case SB_THUMBPOSITION:
1813 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam), TRUE );
1814 break;
1815 case SB_THUMBTRACK:
1816 info.cbSize = sizeof(info);
1817 info.fMask = SIF_TRACKPOS;
1818 GetScrollInfo( hwnd, SB_VERT, &info );
1819 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos, TRUE );
1820 break;
1821 case SB_TOP:
1822 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1823 break;
1824 case SB_BOTTOM:
1825 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1826 break;
1827 }
1828 return 0;
1829}
1830
1831
1832/***********************************************************************
1833 * LISTBOX_HandleHScroll
1834 */
1835static LRESULT LISTBOX_HandleHScroll( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1836{
1837 SCROLLINFO info;
1838 INT page;
1839
1840 if (descr->style & LBS_MULTICOLUMN)
1841 {
1842 switch(LOWORD(wParam))
1843 {
1844 case SB_LINELEFT:
1845 LISTBOX_SetTopItem( hwnd, descr, descr->top_item-descr->page_size,
1846 TRUE );
1847 break;
1848 case SB_LINERIGHT:
1849 LISTBOX_SetTopItem( hwnd, descr, descr->top_item+descr->page_size,
1850 TRUE );
1851 break;
1852 case SB_PAGELEFT:
1853 page = descr->width / descr->column_width;
1854 if (page < 1) page = 1;
1855 LISTBOX_SetTopItem( hwnd, descr,
1856 descr->top_item - page * descr->page_size, TRUE );
1857 break;
1858 case SB_PAGERIGHT:
1859 page = descr->width / descr->column_width;
1860 if (page < 1) page = 1;
1861 LISTBOX_SetTopItem( hwnd, descr,
1862 descr->top_item + page * descr->page_size, TRUE );
1863 break;
1864 case SB_THUMBPOSITION:
1865 LISTBOX_SetTopItem( hwnd, descr, HIWORD(wParam)*descr->page_size,
1866 TRUE );
1867 break;
1868 case SB_THUMBTRACK:
1869 info.cbSize = sizeof(info);
1870 info.fMask = SIF_TRACKPOS;
1871 GetScrollInfo( hwnd, SB_VERT, &info );
1872 LISTBOX_SetTopItem( hwnd, descr, info.nTrackPos*descr->page_size,
1873 TRUE );
1874 break;
1875 case SB_LEFT:
1876 LISTBOX_SetTopItem( hwnd, descr, 0, TRUE );
1877 break;
1878 case SB_RIGHT:
1879 LISTBOX_SetTopItem( hwnd, descr, descr->nb_items, TRUE );
1880 break;
1881 }
1882 }
1883 else if (descr->horz_extent)
1884 {
1885 switch(LOWORD(wParam))
1886 {
1887 case SB_LINELEFT:
1888 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos - 1 );
1889 break;
1890 case SB_LINERIGHT:
1891 LISTBOX_SetHorizontalPos( hwnd, descr, descr->horz_pos + 1 );
1892 break;
1893 case SB_PAGELEFT:
1894 LISTBOX_SetHorizontalPos( hwnd, descr,
1895 descr->horz_pos - descr->width );
1896 break;
1897 case SB_PAGERIGHT:
1898 LISTBOX_SetHorizontalPos( hwnd, descr,
1899 descr->horz_pos + descr->width );
1900 break;
1901 case SB_THUMBPOSITION:
1902 LISTBOX_SetHorizontalPos( hwnd, descr, HIWORD(wParam) );
1903 break;
1904 case SB_THUMBTRACK:
1905 info.cbSize = sizeof(info);
1906 info.fMask = SIF_TRACKPOS;
1907 GetScrollInfo( hwnd, SB_HORZ, &info );
1908 LISTBOX_SetHorizontalPos( hwnd, descr, info.nTrackPos );
1909 break;
1910 case SB_LEFT:
1911 LISTBOX_SetHorizontalPos( hwnd, descr, 0 );
1912 break;
1913 case SB_RIGHT:
1914 LISTBOX_SetHorizontalPos( hwnd, descr,
1915 descr->horz_extent - descr->width );
1916 break;
1917 }
1918 }
1919 return 0;
1920}
1921
1922static LRESULT LISTBOX_HandleMouseWheel(HWND hwnd, LB_DESCR *descr, WPARAM wParam )
1923{
1924 short gcWheelDelta = 0;
1925 UINT pulScrollLines = 3;
1926
1927 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
1928
1929 gcWheelDelta -= (short) HIWORD(wParam);
1930
1931 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
1932 {
1933 int cLineScroll = (int) min((UINT) descr->page_size, pulScrollLines);
1934 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
1935 LISTBOX_SetTopItem( hwnd, descr, descr->top_item + cLineScroll, TRUE );
1936 }
1937 return 0;
1938}
1939
1940/***********************************************************************
1941 * LISTBOX_HandleLButtonDown
1942 */
1943static LRESULT LISTBOX_HandleLButtonDown( HWND hwnd, LB_DESCR *descr,
1944 WPARAM wParam, INT x, INT y )
1945{
1946 INT index = LISTBOX_GetItemFromPoint( descr, x, y );
1947 TRACE("[%04x]: lbuttondown %d,%d item %d\n", hwnd, x, y, index );
1948 if (!descr->caret_on && (descr->in_focus)) return 0;
1949
1950 if (!descr->in_focus)
1951 {
1952 if( !descr->lphc ) SetFocus( hwnd );
1953 else SetFocus( (descr->lphc->hWndEdit) ? descr->lphc->hWndEdit : descr->lphc->self );
1954 }
1955
1956 if (index == -1) return 0;
1957
1958 if (descr->style & LBS_EXTENDEDSEL)
1959 {
1960 /* we should perhaps make sure that all items are deselected
1961 FIXME: needed for !LBS_EXTENDEDSEL, too ?
1962 if (!(wParam & (MK_SHIFT|MK_CONTROL)))
1963 LISTBOX_SetSelection( hwnd, descr, -1, FALSE, FALSE);
1964 */
1965
1966 if (!(wParam & MK_SHIFT)) descr->anchor_item = index;
1967 if (wParam & MK_CONTROL)
1968 {
1969 LISTBOX_SetCaretIndex( hwnd, descr, index, FALSE );
1970 LISTBOX_SetSelection( hwnd, descr, index,
1971 !descr->items[index].selected,
1972 (descr->style & LBS_NOTIFY) != 0);
1973 }
1974 else LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1975 }
1976 else
1977 {
1978 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
1979 LISTBOX_SetSelection( hwnd, descr, index,
1980 (!(descr->style & LBS_MULTIPLESEL) ||
1981 !descr->items[index].selected),
1982 (descr->style & LBS_NOTIFY) != 0 );
1983 }
1984
1985 descr->captured = TRUE;
1986 SetCapture( hwnd );
1987
1988 if (!descr->lphc)
1989 {
1990 if (descr->style & LBS_NOTIFY )
1991 SendMessageW( descr->owner, WM_LBTRACKPOINT, index,
1992 MAKELPARAM( x, y ) );
1993 if (GetWindowLongA( hwnd, GWL_EXSTYLE ) & WS_EX_DRAGDETECT)
1994 {
1995 POINT pt;
1996
1997 pt.x = x;
1998 pt.y = y;
1999
2000 if (DragDetect( hwnd, pt ))
2001 SendMessageW( descr->owner, WM_BEGINDRAG, 0, 0 );
2002 }
2003 }
2004 return 0;
2005}
2006
2007
2008/*************************************************************************
2009 * LISTBOX_HandleLButtonDownCombo [Internal]
2010 *
2011 * Process LButtonDown message for the ComboListBox
2012 *
2013nn * PARAMS
2014 * pWnd [I] The windows internal structure
2015 * pDescr [I] The ListBox internal structure
2016 * wParam [I] Key Flag (WM_LBUTTONDOWN doc for more info)
2017 * x [I] X Mouse Coordinate
2018 * y [I] Y Mouse Coordinate
2019 *
2020 * RETURNS
2021 * 0 since we are processing the WM_LBUTTONDOWN Message
2022 *
2023 * NOTES
2024 * This function is only to be used when a ListBox is a ComboListBox
2025 */
2026
2027static LRESULT LISTBOX_HandleLButtonDownCombo( HWND hwnd, LB_DESCR *pDescr,
2028 UINT msg, WPARAM wParam, INT x, INT y)
2029{
2030 RECT clientRect, screenRect;
2031 POINT mousePos;
2032
2033 mousePos.x = x;
2034 mousePos.y = y;
2035
2036 GetClientRect(hwnd, &clientRect);
2037
2038 if(PtInRect(&clientRect, mousePos))
2039 {
2040 /* MousePos is in client, resume normal processing */
2041 if (msg == WM_LBUTTONDOWN)
2042 {
2043 pDescr->lphc->droppedIndex = pDescr->nb_items ? pDescr->selected_item : -1;
2044 return LISTBOX_HandleLButtonDown( hwnd, pDescr, wParam, x, y);
2045 }
2046 else if (pDescr->style & LBS_NOTIFY)
2047 SEND_NOTIFICATION( hwnd, pDescr, LBN_DBLCLK );
2048 return 0;
2049 }
2050 else
2051 {
2052 POINT screenMousePos;
2053 HWND hWndOldCapture;
2054
2055 /* Check the Non-Client Area */
2056 screenMousePos = mousePos;
2057 hWndOldCapture = GetCapture();
2058 ReleaseCapture();
2059 GetWindowRect(hwnd, &screenRect);
2060 ClientToScreen(hwnd, &screenMousePos);
2061
2062 if(!PtInRect(&screenRect, screenMousePos))
2063 {
2064 LISTBOX_SetCaretIndex( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE );
2065 LISTBOX_SetSelection( hwnd, pDescr, pDescr->lphc->droppedIndex, FALSE, FALSE );
2066 COMBO_FlipListbox( pDescr->lphc, FALSE, FALSE );
2067 return 0;
2068 }
2069 else
2070 {
2071 /* Check to see the NC is a scrollbar */
2072 INT nHitTestType=0;
2073 LONG style = GetWindowLongA( hwnd, GWL_STYLE );
2074 /* Check Vertical scroll bar */
2075 if (style & WS_VSCROLL)
2076 {
2077 clientRect.right += GetSystemMetrics(SM_CXVSCROLL);
2078 if (PtInRect( &clientRect, mousePos ))
2079 {
2080 nHitTestType = HTVSCROLL;
2081 }
2082 }
2083 /* Check horizontal scroll bar */
2084 if (style & WS_HSCROLL)
2085 {
2086 clientRect.bottom += GetSystemMetrics(SM_CYHSCROLL);
2087 if (PtInRect( &clientRect, mousePos ))
2088 {
2089 nHitTestType = HTHSCROLL;
2090 }
2091 }
2092 /* Windows sends this message when a scrollbar is clicked
2093 */
2094
2095 if(nHitTestType != 0)
2096 {
2097 SendMessageW(hwnd, WM_NCLBUTTONDOWN, nHitTestType,
2098 MAKELONG(screenMousePos.x, screenMousePos.y));
2099 }
2100 /* Resume the Capture after scrolling is complete
2101 */
2102 if(hWndOldCapture != 0)
2103 {
2104 SetCapture(hWndOldCapture);
2105 }
2106 }
2107 }
2108 return 0;
2109}
2110
2111/***********************************************************************
2112 * LISTBOX_HandleLButtonUp
2113 */
2114static LRESULT LISTBOX_HandleLButtonUp( HWND hwnd, LB_DESCR *descr )
2115{
2116 if (LISTBOX_Timer != LB_TIMER_NONE)
2117 KillSystemTimer( hwnd, LB_TIMER_ID );
2118 LISTBOX_Timer = LB_TIMER_NONE;
2119 if (descr->captured)
2120 {
2121 descr->captured = FALSE;
2122 if (GetCapture() == hwnd) ReleaseCapture();
2123 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2124 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2125 }
2126 return 0;
2127}
2128
2129
2130/***********************************************************************
2131 * LISTBOX_HandleTimer
2132 *
2133 * Handle scrolling upon a timer event.
2134 * Return TRUE if scrolling should continue.
2135 */
2136static LRESULT LISTBOX_HandleTimer( HWND hwnd, LB_DESCR *descr,
2137 INT index, TIMER_DIRECTION dir )
2138{
2139 switch(dir)
2140 {
2141 case LB_TIMER_UP:
2142 if (descr->top_item) index = descr->top_item - 1;
2143 else index = 0;
2144 break;
2145 case LB_TIMER_LEFT:
2146 if (descr->top_item) index -= descr->page_size;
2147 break;
2148 case LB_TIMER_DOWN:
2149 index = descr->top_item + LISTBOX_GetCurrentPageSize( descr );
2150 if (index == descr->focus_item) index++;
2151 if (index >= descr->nb_items) index = descr->nb_items - 1;
2152 break;
2153 case LB_TIMER_RIGHT:
2154 if (index + descr->page_size < descr->nb_items)
2155 index += descr->page_size;
2156 break;
2157 case LB_TIMER_NONE:
2158 break;
2159 }
2160 if (index == descr->focus_item) return FALSE;
2161 LISTBOX_MoveCaret( hwnd, descr, index, FALSE );
2162 return TRUE;
2163}
2164
2165
2166/***********************************************************************
2167 * LISTBOX_HandleSystemTimer
2168 *
2169 * WM_SYSTIMER handler.
2170 */
2171static LRESULT LISTBOX_HandleSystemTimer( HWND hwnd, LB_DESCR *descr )
2172{
2173 if (!LISTBOX_HandleTimer( hwnd, descr, descr->focus_item, LISTBOX_Timer ))
2174 {
2175 KillSystemTimer( hwnd, LB_TIMER_ID );
2176 LISTBOX_Timer = LB_TIMER_NONE;
2177 }
2178 return 0;
2179}
2180
2181
2182/***********************************************************************
2183 * LISTBOX_HandleMouseMove
2184 *
2185 * WM_MOUSEMOVE handler.
2186 */
2187static void LISTBOX_HandleMouseMove( HWND hwnd, LB_DESCR *descr,
2188 INT x, INT y )
2189{
2190 INT index;
2191 TIMER_DIRECTION dir = LB_TIMER_NONE;
2192
2193 if (!descr->captured) return;
2194
2195 if (descr->style & LBS_MULTICOLUMN)
2196 {
2197 if (y < 0) y = 0;
2198 else if (y >= descr->item_height * descr->page_size)
2199 y = descr->item_height * descr->page_size - 1;
2200
2201 if (x < 0)
2202 {
2203 dir = LB_TIMER_LEFT;
2204 x = 0;
2205 }
2206 else if (x >= descr->width)
2207 {
2208 dir = LB_TIMER_RIGHT;
2209 x = descr->width - 1;
2210 }
2211 }
2212 else
2213 {
2214 if (y < 0) dir = LB_TIMER_UP; /* above */
2215 else if (y >= descr->height) dir = LB_TIMER_DOWN; /* below */
2216 }
2217
2218 index = LISTBOX_GetItemFromPoint( descr, x, y );
2219 if (index == -1) index = descr->focus_item;
2220 if (!LISTBOX_HandleTimer( hwnd, descr, index, dir )) dir = LB_TIMER_NONE;
2221
2222 /* Start/stop the system timer */
2223
2224 if (dir != LB_TIMER_NONE)
2225 SetSystemTimer( hwnd, LB_TIMER_ID, LB_SCROLL_TIMEOUT, NULL);
2226 else if (LISTBOX_Timer != LB_TIMER_NONE)
2227 KillSystemTimer( hwnd, LB_TIMER_ID );
2228 LISTBOX_Timer = dir;
2229}
2230
2231
2232/***********************************************************************
2233 * LISTBOX_HandleKeyDown
2234 */
2235static LRESULT LISTBOX_HandleKeyDown( HWND hwnd, LB_DESCR *descr, WPARAM wParam )
2236{
2237 INT caret = -1;
2238 BOOL bForceSelection = TRUE; /* select item pointed to by focus_item */
2239 if ((IS_MULTISELECT(descr)) || (descr->selected_item == descr->focus_item))
2240 bForceSelection = FALSE; /* only for single select list */
2241
2242 if (descr->style & LBS_WANTKEYBOARDINPUT)
2243 {
2244 caret = SendMessageW( descr->owner, WM_VKEYTOITEM,
2245 MAKEWPARAM(LOWORD(wParam), descr->focus_item),
2246 hwnd );
2247 if (caret == -2) return 0;
2248 }
2249 if (caret == -1) switch(wParam)
2250 {
2251 case VK_LEFT:
2252 if (descr->style & LBS_MULTICOLUMN)
2253 {
2254 bForceSelection = FALSE;
2255 if (descr->focus_item >= descr->page_size)
2256 caret = descr->focus_item - descr->page_size;
2257 break;
2258 }
2259 /* fall through */
2260 case VK_UP:
2261 caret = descr->focus_item - 1;
2262 if (caret < 0) caret = 0;
2263 break;
2264 case VK_RIGHT:
2265 if (descr->style & LBS_MULTICOLUMN)
2266 {
2267 bForceSelection = FALSE;
2268 if (descr->focus_item + descr->page_size < descr->nb_items)
2269 caret = descr->focus_item + descr->page_size;
2270 break;
2271 }
2272 /* fall through */
2273 case VK_DOWN:
2274 caret = descr->focus_item + 1;
2275 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2276 break;
2277
2278 case VK_PRIOR:
2279 if (descr->style & LBS_MULTICOLUMN)
2280 {
2281 INT page = descr->width / descr->column_width;
2282 if (page < 1) page = 1;
2283 caret = descr->focus_item - (page * descr->page_size) + 1;
2284 }
2285 else caret = descr->focus_item-LISTBOX_GetCurrentPageSize(descr) + 1;
2286 if (caret < 0) caret = 0;
2287 break;
2288 case VK_NEXT:
2289 if (descr->style & LBS_MULTICOLUMN)
2290 {
2291 INT page = descr->width / descr->column_width;
2292 if (page < 1) page = 1;
2293 caret = descr->focus_item + (page * descr->page_size) - 1;
2294 }
2295 else caret = descr->focus_item + LISTBOX_GetCurrentPageSize(descr) - 1;
2296 if (caret >= descr->nb_items) caret = descr->nb_items - 1;
2297 break;
2298 case VK_HOME:
2299 caret = 0;
2300 break;
2301 case VK_END:
2302 caret = descr->nb_items - 1;
2303 break;
2304 case VK_SPACE:
2305 if (descr->style & LBS_EXTENDEDSEL) caret = descr->focus_item;
2306 else if (descr->style & LBS_MULTIPLESEL)
2307 {
2308 LISTBOX_SetSelection( hwnd, descr, descr->focus_item,
2309 !descr->items[descr->focus_item].selected,
2310 (descr->style & LBS_NOTIFY) != 0 );
2311 }
2312 break;
2313 default:
2314 bForceSelection = FALSE;
2315 }
2316 if (bForceSelection) /* focused item is used instead of key */
2317 caret = descr->focus_item;
2318 if (caret >= 0)
2319 {
2320 if ((descr->style & LBS_EXTENDEDSEL) &&
2321 !(GetKeyState( VK_SHIFT ) & 0x8000))
2322 descr->anchor_item = caret;
2323 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2324 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2325 if (descr->style & LBS_NOTIFY)
2326 {
2327 if( descr->lphc )
2328 {
2329 /* make sure that combo parent doesn't hide us */
2330 descr->lphc->wState |= CBF_NOROLLUP;
2331 }
2332 if (descr->nb_items) SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2333 }
2334 }
2335 return 0;
2336}
2337
2338
2339/***********************************************************************
2340 * LISTBOX_HandleChar
2341 */
2342static LRESULT LISTBOX_HandleChar( HWND hwnd, LB_DESCR *descr, WCHAR charW )
2343{
2344 INT caret = -1;
2345 WCHAR str[2];
2346
2347 str[0] = charW;
2348 str[1] = '\0';
2349
2350 if (descr->style & LBS_WANTKEYBOARDINPUT)
2351 {
2352 caret = SendMessageW( descr->owner, WM_CHARTOITEM,
2353 MAKEWPARAM(charW, descr->focus_item),
2354 hwnd );
2355 if (caret == -2) return 0;
2356 }
2357 if (caret == -1)
2358 caret = LISTBOX_FindString( hwnd, descr, descr->focus_item, str, FALSE);
2359 if (caret != -1)
2360 {
2361 if ((!IS_MULTISELECT(descr)) && descr->selected_item == -1)
2362 LISTBOX_SetSelection( hwnd, descr, caret, TRUE, FALSE);
2363 LISTBOX_MoveCaret( hwnd, descr, caret, TRUE );
2364 if ((descr->style & LBS_NOTIFY) && descr->nb_items)
2365 SEND_NOTIFICATION( hwnd, descr, LBN_SELCHANGE );
2366 }
2367 return 0;
2368}
2369
2370
2371/***********************************************************************
2372 * LISTBOX_Create
2373 */
2374static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
2375{
2376 LB_DESCR *descr;
2377 MEASUREITEMSTRUCT mis;
2378 RECT rect;
2379
2380 if (!(descr = HeapAlloc( GetProcessHeap(), 0, sizeof(*descr) )))
2381 return FALSE;
2382
2383 GetClientRect( hwnd, &rect );
2384 descr->owner = GetParent( hwnd );
2385 descr->style = GetWindowLongA( hwnd, GWL_STYLE );
2386 descr->width = rect.right - rect.left;
2387 descr->height = rect.bottom - rect.top;
2388 descr->items = NULL;
2389 descr->nb_items = 0;
2390 descr->top_item = 0;
2391 descr->selected_item = -1;
2392 descr->focus_item = 0;
2393 descr->anchor_item = -1;
2394 descr->item_height = 1;
2395 descr->page_size = 1;
2396 descr->column_width = 150;
2397 descr->horz_extent = (descr->style & WS_HSCROLL) ? 1 : 0;
2398 descr->horz_pos = 0;
2399 descr->nb_tabs = 0;
2400 descr->tabs = NULL;
2401 descr->caret_on = lphc ? FALSE : TRUE;
2402 if (descr->style & LBS_NOSEL) descr->caret_on = FALSE;
2403 descr->in_focus = FALSE;
2404 descr->captured = FALSE;
2405 descr->font = 0;
2406 descr->locale = 0; /* FIXME */
2407 descr->lphc = lphc;
2408
2409 if (is_old_app(hwnd) && ( descr->style & ( WS_VSCROLL | WS_HSCROLL ) ) )
2410 {
2411 /* Win95 document "List Box Differences" from MSDN:
2412 If a list box in a version 3.x application has either the
2413 WS_HSCROLL or WS_VSCROLL style, the list box receives both
2414 horizontal and vertical scroll bars.
2415 */
2416 descr->style |= WS_VSCROLL | WS_HSCROLL;
2417 }
2418
2419 if( lphc )
2420 {
2421 TRACE_(combo)("[%04x]: resetting owner %04x -> %04x\n",
2422 hwnd, descr->owner, lphc->self );
2423 descr->owner = lphc->self;
2424 }
2425
2426 SetWindowLongA( hwnd, 0, (LONG)descr );
2427
2428/* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2429 */
2430 if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
2431 if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
2432 if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
2433 descr->item_height = LISTBOX_SetFont( hwnd, descr, 0 );
2434
2435 if (descr->style & LBS_OWNERDRAWFIXED)
2436 {
2437 if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
2438 {
2439 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2440 descr->item_height = lphc->fixedOwnerDrawHeight;
2441 }
2442 else
2443 {
2444 UINT id = GetWindowLongA( hwnd, GWL_ID );
2445 mis.CtlType = ODT_LISTBOX;
2446 mis.CtlID = id;
2447 mis.itemID = -1;
2448 mis.itemWidth = 0;
2449 mis.itemData = 0;
2450 mis.itemHeight = descr->item_height;
2451 SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
2452 descr->item_height = mis.itemHeight ? mis.itemHeight : 1;
2453 }
2454 }
2455
2456 TRACE("owner: %04x, style: %08x, width: %d, height: %d\n", descr->owner, descr->style, descr->width, descr->height);
2457 return TRUE;
2458}
2459
2460
2461/***********************************************************************
2462 * LISTBOX_Destroy
2463 */
2464static BOOL LISTBOX_Destroy( HWND hwnd, LB_DESCR *descr )
2465{
2466 LISTBOX_ResetContent( hwnd, descr );
2467 SetWindowLongA( hwnd, 0, 0 );
2468 HeapFree( GetProcessHeap(), 0, descr );
2469 return TRUE;
2470}
2471
2472
2473/***********************************************************************
2474 * ListBoxWndProc_common
2475 */
2476static LRESULT WINAPI ListBoxWndProc_common( HWND hwnd, UINT msg,
2477 WPARAM wParam, LPARAM lParam, BOOL unicode )
2478{
2479 LRESULT ret;
2480 LB_DESCR *descr;
2481
2482 if (!(descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 )))
2483 {
2484 if (msg == WM_CREATE)
2485 {
2486 if (!LISTBOX_Create( hwnd, NULL ))
2487 return -1;
2488 TRACE("creating wnd=%04x descr=%lx\n", hwnd, GetWindowLongA( hwnd, 0 ) );
2489 return 0;
2490 }
2491 /* Ignore all other messages before we get a WM_CREATE */
2492 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
2493 DefWindowProcA( hwnd, msg, wParam, lParam );
2494 }
2495
2496 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2497 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
2498 switch(msg)
2499 {
2500 case LB_RESETCONTENT16:
2501 case LB_RESETCONTENT:
2502 LISTBOX_ResetContent( hwnd, descr );
2503 LISTBOX_UpdateScroll( hwnd, descr );
2504 InvalidateRect( hwnd, NULL, TRUE );
2505 return 0;
2506
2507 case LB_ADDSTRING16:
2508 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2509 /* fall through */
2510 case LB_ADDSTRING:
2511 {
2512 INT ret;
2513 LPWSTR textW;
2514 if(unicode || !HAS_STRINGS(descr))
2515 textW = (LPWSTR)lParam;
2516 else
2517 {
2518 LPSTR textA = (LPSTR)lParam;
2519 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2520 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2521 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2522 }
2523 wParam = LISTBOX_FindStringPos( hwnd, descr, textW, FALSE );
2524 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2525 if (!unicode && HAS_STRINGS(descr))
2526 HeapFree(GetProcessHeap(), 0, textW);
2527 return ret;
2528 }
2529
2530 case LB_INSERTSTRING16:
2531 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2532 wParam = (INT)(INT16)wParam;
2533 /* fall through */
2534 case LB_INSERTSTRING:
2535 {
2536 INT ret;
2537 LPWSTR textW;
2538 if(unicode || !HAS_STRINGS(descr))
2539 textW = (LPWSTR)lParam;
2540 else
2541 {
2542 LPSTR textA = (LPSTR)lParam;
2543 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2544 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2545 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2546 }
2547 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2548 if(!unicode && HAS_STRINGS(descr))
2549 HeapFree(GetProcessHeap(), 0, textW);
2550 return ret;
2551 }
2552
2553 case LB_ADDFILE16:
2554 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2555 /* fall through */
2556 case LB_ADDFILE:
2557 {
2558 INT ret;
2559 LPWSTR textW;
2560 if(unicode || !HAS_STRINGS(descr))
2561 textW = (LPWSTR)lParam;
2562 else
2563 {
2564 LPSTR textA = (LPSTR)lParam;
2565 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2566 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2567 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2568 }
2569 wParam = LISTBOX_FindFileStrPos( hwnd, descr, textW );
2570 ret = LISTBOX_InsertString( hwnd, descr, wParam, textW );
2571 if(!unicode && HAS_STRINGS(descr))
2572 HeapFree(GetProcessHeap(), 0, textW);
2573 return ret;
2574 }
2575
2576 case LB_DELETESTRING16:
2577 case LB_DELETESTRING:
2578 if (LISTBOX_RemoveItem( hwnd, descr, wParam) != LB_ERR)
2579 return descr->nb_items;
2580 else
2581 return LB_ERR;
2582
2583 case LB_GETITEMDATA16:
2584 case LB_GETITEMDATA:
2585 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2586 return LB_ERR;
2587 return descr->items[wParam].data;
2588
2589 case LB_SETITEMDATA16:
2590 case LB_SETITEMDATA:
2591 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2592 return LB_ERR;
2593 descr->items[wParam].data = (DWORD)lParam;
2594 return LB_OKAY;
2595
2596 case LB_GETCOUNT16:
2597 case LB_GETCOUNT:
2598 return descr->nb_items;
2599
2600 case LB_GETTEXT16:
2601 lParam = (LPARAM)MapSL(lParam);
2602 /* fall through */
2603 case LB_GETTEXT:
2604 return LISTBOX_GetText( descr, wParam, lParam, unicode );
2605
2606 case LB_GETTEXTLEN16:
2607 /* fall through */
2608 case LB_GETTEXTLEN:
2609 if ((INT)wParam >= descr->nb_items || (INT)wParam < 0)
2610 return LB_ERR;
2611 return (HAS_STRINGS(descr) ? strlenW(descr->items[wParam].str)
2612 : sizeof(DWORD));
2613
2614 case LB_GETCURSEL16:
2615 case LB_GETCURSEL:
2616 if (descr->nb_items==0)
2617 return LB_ERR;
2618 if (!IS_MULTISELECT(descr))
2619 return descr->selected_item;
2620 /* else */
2621 if (descr->selected_item!=-1)
2622 return descr->selected_item;
2623 /* else */
2624 return descr->focus_item;
2625 /* otherwise, if the user tries to move the selection with the */
2626 /* arrow keys, we will give the application something to choke on */
2627 case LB_GETTOPINDEX16:
2628 case LB_GETTOPINDEX:
2629 return descr->top_item;
2630
2631 case LB_GETITEMHEIGHT16:
2632 case LB_GETITEMHEIGHT:
2633 return LISTBOX_GetItemHeight( descr, wParam );
2634
2635 case LB_SETITEMHEIGHT16:
2636 lParam = LOWORD(lParam);
2637 /* fall through */
2638 case LB_SETITEMHEIGHT:
2639 return LISTBOX_SetItemHeight( hwnd, descr, wParam, lParam, TRUE );
2640
2641 case LB_ITEMFROMPOINT:
2642 {
2643 POINT pt;
2644 RECT rect;
2645
2646 pt.x = LOWORD(lParam);
2647 pt.y = HIWORD(lParam);
2648 rect.left = 0;
2649 rect.top = 0;
2650 rect.right = descr->width;
2651 rect.bottom = descr->height;
2652
2653 return MAKELONG( LISTBOX_GetItemFromPoint(descr, pt.x, pt.y),
2654 !PtInRect( &rect, pt ) );
2655 }
2656
2657 case LB_SETCARETINDEX16:
2658 case LB_SETCARETINDEX:
2659 if ((!IS_MULTISELECT(descr)) && (descr->selected_item != -1)) return LB_ERR;
2660 if (LISTBOX_SetCaretIndex( hwnd, descr, wParam, !lParam ) == LB_ERR)
2661 return LB_ERR;
2662 else if (ISWIN31)
2663 return wParam;
2664 else
2665 return LB_OKAY;
2666
2667 case LB_GETCARETINDEX16:
2668 case LB_GETCARETINDEX:
2669 return descr->focus_item;
2670
2671 case LB_SETTOPINDEX16:
2672 case LB_SETTOPINDEX:
2673 return LISTBOX_SetTopItem( hwnd, descr, wParam, TRUE );
2674
2675 case LB_SETCOLUMNWIDTH16:
2676 case LB_SETCOLUMNWIDTH:
2677 return LISTBOX_SetColumnWidth( hwnd, descr, wParam );
2678
2679 case LB_GETITEMRECT16:
2680 {
2681 RECT rect;
2682 ret = LISTBOX_GetItemRect( descr, (INT16)wParam, &rect );
2683 CONV_RECT32TO16( &rect, MapSL(lParam) );
2684 }
2685 return ret;
2686
2687 case LB_GETITEMRECT:
2688 return LISTBOX_GetItemRect( descr, wParam, (RECT *)lParam );
2689
2690 case LB_FINDSTRING16:
2691 wParam = (INT)(INT16)wParam;
2692 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2693 /* fall through */
2694 case LB_FINDSTRING:
2695 {
2696 INT ret;
2697 LPWSTR textW;
2698 if(unicode || !HAS_STRINGS(descr))
2699 textW = (LPWSTR)lParam;
2700 else
2701 {
2702 LPSTR textA = (LPSTR)lParam;
2703 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2704 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2705 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2706 }
2707 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2708 if(!unicode && HAS_STRINGS(descr))
2709 HeapFree(GetProcessHeap(), 0, textW);
2710 return ret;
2711 }
2712
2713 case LB_FINDSTRINGEXACT16:
2714 wParam = (INT)(INT16)wParam;
2715 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2716 /* fall through */
2717 case LB_FINDSTRINGEXACT:
2718 {
2719 INT ret;
2720 LPWSTR textW;
2721 if(unicode || !HAS_STRINGS(descr))
2722 textW = (LPWSTR)lParam;
2723 else
2724 {
2725 LPSTR textA = (LPSTR)lParam;
2726 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2727 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2728 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2729 }
2730 ret = LISTBOX_FindString( hwnd, descr, wParam, textW, TRUE );
2731 if(!unicode && HAS_STRINGS(descr))
2732 HeapFree(GetProcessHeap(), 0, textW);
2733 return ret;
2734 }
2735
2736 case LB_SELECTSTRING16:
2737 wParam = (INT)(INT16)wParam;
2738 if (HAS_STRINGS(descr)) lParam = (LPARAM)MapSL(lParam);
2739 /* fall through */
2740 case LB_SELECTSTRING:
2741 {
2742 INT index;
2743 LPWSTR textW;
2744
2745 if(HAS_STRINGS(descr))
2746 TRACE("LB_SELECTSTRING: %s\n", unicode ? debugstr_w((LPWSTR)lParam) :
2747 debugstr_a((LPSTR)lParam));
2748 if(unicode || !HAS_STRINGS(descr))
2749 textW = (LPWSTR)lParam;
2750 else
2751 {
2752 LPSTR textA = (LPSTR)lParam;
2753 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2754 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2755 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2756 }
2757 index = LISTBOX_FindString( hwnd, descr, wParam, textW, FALSE );
2758 if(!unicode && HAS_STRINGS(descr))
2759 HeapFree(GetProcessHeap(), 0, textW);
2760 if (index != LB_ERR)
2761 {
2762 LISTBOX_SetCaretIndex( hwnd, descr, index, TRUE );
2763 LISTBOX_SetSelection( hwnd, descr, index, TRUE, FALSE );
2764 }
2765 return index;
2766 }
2767
2768 case LB_GETSEL16:
2769 wParam = (INT)(INT16)wParam;
2770 /* fall through */
2771 case LB_GETSEL:
2772 if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
2773 return LB_ERR;
2774 return descr->items[wParam].selected;
2775
2776 case LB_SETSEL16:
2777 lParam = (INT)(INT16)lParam;
2778 /* fall through */
2779 case LB_SETSEL:
2780 return LISTBOX_SetSelection( hwnd, descr, lParam, wParam, FALSE );
2781
2782 case LB_SETCURSEL16:
2783 wParam = (INT)(INT16)wParam;
2784 /* fall through */
2785 case LB_SETCURSEL:
2786 if (IS_MULTISELECT(descr)) return LB_ERR;
2787 LISTBOX_SetCaretIndex( hwnd, descr, wParam, TRUE );
2788 return LISTBOX_SetSelection( hwnd, descr, wParam, TRUE, FALSE );
2789
2790 case LB_GETSELCOUNT16:
2791 case LB_GETSELCOUNT:
2792 return LISTBOX_GetSelCount( descr );
2793
2794 case LB_GETSELITEMS16:
2795 return LISTBOX_GetSelItems16( descr, wParam, (LPINT16)MapSL(lParam) );
2796
2797 case LB_GETSELITEMS:
2798 return LISTBOX_GetSelItems( descr, wParam, (LPINT)lParam );
2799
2800 case LB_SELITEMRANGE16:
2801 case LB_SELITEMRANGE:
2802 if (LOWORD(lParam) <= HIWORD(lParam))
2803 return LISTBOX_SelectItemRange( hwnd, descr, LOWORD(lParam),
2804 HIWORD(lParam), wParam );
2805 else
2806 return LISTBOX_SelectItemRange( hwnd, descr, HIWORD(lParam),
2807 LOWORD(lParam), wParam );
2808
2809 case LB_SELITEMRANGEEX16:
2810 case LB_SELITEMRANGEEX:
2811 if ((INT)lParam >= (INT)wParam)
2812 return LISTBOX_SelectItemRange( hwnd, descr, wParam, lParam, TRUE );
2813 else
2814 return LISTBOX_SelectItemRange( hwnd, descr, lParam, wParam, FALSE);
2815
2816 case LB_GETHORIZONTALEXTENT16:
2817 case LB_GETHORIZONTALEXTENT:
2818 return descr->horz_extent;
2819
2820 case LB_SETHORIZONTALEXTENT16:
2821 case LB_SETHORIZONTALEXTENT:
2822 return LISTBOX_SetHorizontalExtent( hwnd, descr, wParam );
2823
2824 case LB_GETANCHORINDEX16:
2825 case LB_GETANCHORINDEX:
2826 return descr->anchor_item;
2827
2828 case LB_SETANCHORINDEX16:
2829 wParam = (INT)(INT16)wParam;
2830 /* fall through */
2831 case LB_SETANCHORINDEX:
2832 if (((INT)wParam < -1) || ((INT)wParam >= descr->nb_items))
2833 return LB_ERR;
2834 descr->anchor_item = (INT)wParam;
2835 return LB_OKAY;
2836
2837 case LB_DIR16:
2838 /* according to Win16 docs, DDL_DRIVES should make DDL_EXCLUSIVE
2839 * be set automatically (this is different in Win32) */
2840 if (wParam & DDL_DRIVES) wParam |= DDL_EXCLUSIVE;
2841 lParam = (LPARAM)MapSL(lParam);
2842 /* fall through */
2843 case LB_DIR:
2844 {
2845 INT ret;
2846 LPWSTR textW;
2847 if(unicode)
2848 textW = (LPWSTR)lParam;
2849 else
2850 {
2851 LPSTR textA = (LPSTR)lParam;
2852 INT countW = MultiByteToWideChar(CP_ACP, 0, textA, -1, NULL, 0);
2853 if((textW = HeapAlloc(GetProcessHeap(), 0, countW * sizeof(WCHAR))))
2854 MultiByteToWideChar(CP_ACP, 0, textA, -1, textW, countW);
2855 }
2856 ret = LISTBOX_Directory( hwnd, descr, wParam, textW, msg == LB_DIR );
2857 if(!unicode)
2858 HeapFree(GetProcessHeap(), 0, textW);
2859 return ret;
2860 }
2861
2862 case LB_GETLOCALE:
2863 return descr->locale;
2864
2865 case LB_SETLOCALE:
2866 descr->locale = (LCID)wParam; /* FIXME: should check for valid lcid */
2867 return LB_OKAY;
2868
2869 case LB_INITSTORAGE:
2870 return LISTBOX_InitStorage( hwnd, descr, wParam );
2871
2872 case LB_SETCOUNT:
2873 return LISTBOX_SetCount( hwnd, descr, (INT)wParam );
2874
2875 case LB_SETTABSTOPS16:
2876 return LISTBOX_SetTabStops( hwnd, descr, (INT)(INT16)wParam, MapSL(lParam), TRUE );
2877
2878 case LB_SETTABSTOPS:
2879 return LISTBOX_SetTabStops( hwnd, descr, wParam, (LPINT)lParam, FALSE );
2880
2881 case LB_CARETON16:
2882 case LB_CARETON:
2883 if (descr->caret_on)
2884 return LB_OKAY;
2885 descr->caret_on = TRUE;
2886 if ((descr->focus_item != -1) && (descr->in_focus))
2887 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2888 return LB_OKAY;
2889
2890 case LB_CARETOFF16:
2891 case LB_CARETOFF:
2892 if (!descr->caret_on)
2893 return LB_OKAY;
2894 descr->caret_on = FALSE;
2895 if ((descr->focus_item != -1) && (descr->in_focus))
2896 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2897 return LB_OKAY;
2898
2899 case WM_DESTROY:
2900 return LISTBOX_Destroy( hwnd, descr );
2901
2902 case WM_ENABLE:
2903 InvalidateRect( hwnd, NULL, TRUE );
2904 return 0;
2905
2906 case WM_SETREDRAW:
2907 LISTBOX_SetRedraw( hwnd, descr, wParam != 0 );
2908 return 0;
2909
2910 case WM_GETDLGCODE:
2911 return DLGC_WANTARROWS | DLGC_WANTCHARS;
2912
2913 case WM_PAINT:
2914 {
2915 PAINTSTRUCT ps;
2916 HDC hdc = ( wParam ) ? ((HDC)wParam) : BeginPaint( hwnd, &ps );
2917 ret = LISTBOX_Paint( hwnd, descr, hdc );
2918 if( !wParam ) EndPaint( hwnd, &ps );
2919 }
2920 return ret;
2921 case WM_SIZE:
2922 LISTBOX_UpdateSize( hwnd, descr );
2923 return 0;
2924 case WM_GETFONT:
2925 return descr->font;
2926 case WM_SETFONT:
2927 LISTBOX_SetFont( hwnd, descr, (HFONT)wParam );
2928 if (lParam) InvalidateRect( hwnd, 0, TRUE );
2929 return 0;
2930 case WM_SETFOCUS:
2931 descr->in_focus = TRUE;
2932 descr->caret_on = TRUE;
2933 if (descr->focus_item != -1)
2934 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2935 SEND_NOTIFICATION( hwnd, descr, LBN_SETFOCUS );
2936 return 0;
2937 case WM_KILLFOCUS:
2938 descr->in_focus = FALSE;
2939 if ((descr->focus_item != -1) && descr->caret_on)
2940 LISTBOX_RepaintItem( hwnd, descr, descr->focus_item, ODA_FOCUS );
2941 SEND_NOTIFICATION( hwnd, descr, LBN_KILLFOCUS );
2942 return 0;
2943 case WM_HSCROLL:
2944 return LISTBOX_HandleHScroll( hwnd, descr, wParam );
2945 case WM_VSCROLL:
2946 return LISTBOX_HandleVScroll( hwnd, descr, wParam );
2947 case WM_MOUSEWHEEL:
2948 if (wParam & (MK_SHIFT | MK_CONTROL))
2949 return DefWindowProcW( hwnd, msg, wParam, lParam );
2950 return LISTBOX_HandleMouseWheel( hwnd, descr, wParam );
2951 case WM_LBUTTONDOWN:
2952 return LISTBOX_HandleLButtonDown( hwnd, descr, wParam,
2953 (INT16)LOWORD(lParam),
2954 (INT16)HIWORD(lParam) );
2955 case WM_LBUTTONDBLCLK:
2956 if (descr->style & LBS_NOTIFY)
2957 SEND_NOTIFICATION( hwnd, descr, LBN_DBLCLK );
2958 return 0;
2959 case WM_MOUSEMOVE:
2960 if (GetCapture() == hwnd)
2961 LISTBOX_HandleMouseMove( hwnd, descr, (INT16)LOWORD(lParam),
2962 (INT16)HIWORD(lParam) );
2963 return 0;
2964 case WM_LBUTTONUP:
2965 return LISTBOX_HandleLButtonUp( hwnd, descr );
2966 case WM_KEYDOWN:
2967 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
2968 case WM_CHAR:
2969 {
2970 WCHAR charW;
2971 if(unicode)
2972 charW = (WCHAR)wParam;
2973 else
2974 {
2975 CHAR charA = (CHAR)wParam;
2976 MultiByteToWideChar(CP_ACP, 0, &charA, 1, &charW, 1);
2977 }
2978 return LISTBOX_HandleChar( hwnd, descr, charW );
2979 }
2980 case WM_SYSTIMER:
2981 return LISTBOX_HandleSystemTimer( hwnd, descr );
2982 case WM_ERASEBKGND:
2983 if ((IS_OWNERDRAW(descr)) && !(descr->style & LBS_DISPLAYCHANGED))
2984 {
2985 RECT rect;
2986 HBRUSH hbrush = SendMessageW( descr->owner, WM_CTLCOLORLISTBOX,
2987 wParam, (LPARAM)hwnd );
2988 TRACE("hbrush = %04x\n", hbrush);
2989 if(!hbrush)
2990 hbrush = GetSysColorBrush(COLOR_WINDOW);
2991 if(hbrush)
2992 {
2993 GetClientRect(hwnd, &rect);
2994 FillRect((HDC)wParam, &rect, hbrush);
2995 }
2996 }
2997 return 1;
2998 case WM_DROPFILES:
2999 if( !descr->lphc )
3000 return unicode ? SendMessageW( descr->owner, msg, wParam, lParam ) :
3001 SendMessageA( descr->owner, msg, wParam, lParam );
3002 break;
3003
3004#ifndef __WIN32OS2__
3005 case WM_DROPOBJECT:
3006 case WM_QUERYDROPOBJECT:
3007 case WM_DRAGSELECT:
3008 case WM_DRAGMOVE:
3009 if( !descr->lphc )
3010 {
3011 LPDRAGINFO16 dragInfo = MapSL( lParam );
3012 dragInfo->l = LISTBOX_GetItemFromPoint( descr, dragInfo->pt.x,
3013 dragInfo->pt.y );
3014 return SendMessage16( descr->owner, msg, wParam, lParam );
3015 }
3016 break;
3017#endif
3018
3019 default:
3020 if ((msg >= WM_USER) && (msg < 0xc000))
3021 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
3022 hwnd, msg, wParam, lParam );
3023 return unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3024 DefWindowProcA( hwnd, msg, wParam, lParam );
3025 }
3026 return 0;
3027}
3028
3029/***********************************************************************
3030 * ListBoxWndProcA
3031 *
3032 * This is just a wrapper for the real wndproc, it only does window locking
3033 * and unlocking.
3034 */
3035static LRESULT WINAPI ListBoxWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3036{
3037 if (!IsWindow(hwnd)) return 0;
3038 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3039}
3040
3041/***********************************************************************
3042 * ListBoxWndProcW
3043 */
3044static LRESULT WINAPI ListBoxWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3045{
3046 if (!IsWindow(hwnd)) return 0;
3047 return ListBoxWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3048}
3049
3050/***********************************************************************
3051 * ComboLBWndProc_common
3052 *
3053 * The real combo listbox wndproc
3054 */
3055static LRESULT WINAPI ComboLBWndProc_common( HWND hwnd, UINT msg,
3056 WPARAM wParam, LPARAM lParam, BOOL unicode )
3057{
3058 LRESULT lRet = 0;
3059 LB_DESCR *descr = (LB_DESCR *)GetWindowLongA( hwnd, 0 );
3060
3061 TRACE_(combo)("[%04x]: msg %s wp %08x lp %08lx\n",
3062 hwnd, SPY_GetMsgName(msg, hwnd), wParam, lParam );
3063
3064 if( descr || msg == WM_CREATE )
3065 {
3066 LPHEADCOMBO lphc = (descr) ? descr->lphc : NULL;
3067
3068 switch( msg )
3069 {
3070 case WM_CREATE:
3071 {
3072 CREATESTRUCTA *lpcs = (CREATESTRUCTA *)lParam;
3073 TRACE_(combo)("\tpassed parent handle = %p\n",lpcs->lpCreateParams);
3074 lphc = (LPHEADCOMBO)(lpcs->lpCreateParams);
3075 return LISTBOX_Create( hwnd, lphc );
3076 }
3077 case WM_MOUSEMOVE:
3078 if ( (TWEAK_WineLook > WIN31_LOOK) &&
3079 (CB_GETTYPE(lphc) != CBS_SIMPLE) )
3080 {
3081 POINT mousePos;
3082 BOOL captured;
3083 RECT clientRect;
3084
3085 mousePos.x = (INT16)LOWORD(lParam);
3086 mousePos.y = (INT16)HIWORD(lParam);
3087
3088 /*
3089 * If we are in a dropdown combobox, we simulate that
3090 * the mouse is captured to show the tracking of the item.
3091 */
3092 GetClientRect(hwnd, &clientRect);
3093
3094 if (PtInRect( &clientRect, mousePos ))
3095 {
3096 captured = descr->captured;
3097 descr->captured = TRUE;
3098
3099 LISTBOX_HandleMouseMove( hwnd, descr,
3100 mousePos.x, mousePos.y);
3101
3102 descr->captured = captured;
3103
3104 }
3105 else
3106 {
3107 LISTBOX_HandleMouseMove( hwnd, descr,
3108 mousePos.x, mousePos.y);
3109 }
3110
3111 return 0;
3112
3113 }
3114 else
3115 {
3116 /*
3117 * If we are in Win3.1 look, go with the default behavior.
3118 */
3119 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3120 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3121 }
3122 case WM_LBUTTONUP:
3123 if (TWEAK_WineLook > WIN31_LOOK)
3124 {
3125 POINT mousePos;
3126 RECT clientRect;
3127
3128 /*
3129 * If the mouse button "up" is not in the listbox,
3130 * we make sure there is no selection by re-selecting the
3131 * item that was selected when the listbox was made visible.
3132 */
3133 mousePos.x = (INT16)LOWORD(lParam);
3134 mousePos.y = (INT16)HIWORD(lParam);
3135
3136 GetClientRect(hwnd, &clientRect);
3137
3138 /*
3139 * When the user clicks outside the combobox and the focus
3140 * is lost, the owning combobox will send a fake buttonup with
3141 * 0xFFFFFFF as the mouse location, we must also revert the
3142 * selection to the original selection.
3143 */
3144 if ( (lParam == (LPARAM)-1) ||
3145 (!PtInRect( &clientRect, mousePos )) )
3146 {
3147 LISTBOX_MoveCaret( hwnd, descr, lphc->droppedIndex, FALSE );
3148 }
3149 }
3150 return LISTBOX_HandleLButtonUp( hwnd, descr );
3151 case WM_LBUTTONDBLCLK:
3152 case WM_LBUTTONDOWN:
3153 return LISTBOX_HandleLButtonDownCombo(hwnd, descr, msg, wParam,
3154 (INT16)LOWORD(lParam),
3155 (INT16)HIWORD(lParam) );
3156 case WM_NCACTIVATE:
3157 return FALSE;
3158 case WM_KEYDOWN:
3159 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3160 {
3161 /* for some reason(?) Windows makes it possible to
3162 * show/hide ComboLBox by sending it WM_KEYDOWNs */
3163
3164 if( (!(lphc->wState & CBF_EUI) && wParam == VK_F4) ||
3165 ( (lphc->wState & CBF_EUI) && !(lphc->wState & CBF_DROPPED)
3166 && (wParam == VK_DOWN || wParam == VK_UP)) )
3167 {
3168 COMBO_FlipListbox( lphc, FALSE, FALSE );
3169 return 0;
3170 }
3171 }
3172 return LISTBOX_HandleKeyDown( hwnd, descr, wParam );
3173
3174 case LB_SETCURSEL16:
3175 case LB_SETCURSEL:
3176 lRet = unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3177 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3178 lRet =(lRet == LB_ERR) ? lRet : descr->selected_item;
3179 return lRet;
3180 case WM_NCDESTROY:
3181 if( CB_GETTYPE(lphc) != CBS_SIMPLE )
3182 lphc->hWndLBox = 0;
3183 /* fall through */
3184
3185 default:
3186 return unicode ? ListBoxWndProcW( hwnd, msg, wParam, lParam ) :
3187 ListBoxWndProcA( hwnd, msg, wParam, lParam );
3188 }
3189 }
3190 lRet = unicode ? DefWindowProcW( hwnd, msg, wParam, lParam ) :
3191 DefWindowProcA( hwnd, msg, wParam, lParam );
3192
3193 TRACE_(combo)("\t default on msg [%04x]\n", (UINT16)msg );
3194
3195 return lRet;
3196}
3197
3198/***********************************************************************
3199 * ComboLBWndProcA
3200 *
3201 * NOTE: in Windows, winproc address of the ComboLBox is the same
3202 * as that of the Listbox.
3203 */
3204LRESULT WINAPI ComboLBWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3205{
3206 if (!IsWindow(hwnd)) return 0;
3207 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, FALSE );
3208}
3209
3210/***********************************************************************
3211 * ComboLBWndProcW
3212 */
3213LRESULT WINAPI ComboLBWndProcW( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
3214{
3215 if (!IsWindow(hwnd)) return 0;
3216 return ComboLBWndProc_common( hwnd, msg, wParam, lParam, TRUE );
3217}
Note: See TracBrowser for help on using the repository browser.