source: trunk/src/user32/listbox.c

Last change on this file was 21356, checked in by rlwalsh, 16 years ago

eliminate VACPP warning & info msgs - see Ticket #1

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