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