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