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