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