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