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