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