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