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