4 * Copyright 1996 Alexandre Julliard
7 #include <ntos/minmax.h>
13 #include <user32/win.h>
14 #include <user32/combo.h>
15 #include <user32/scroll.h>
16 #include <user32/debug.h>
17 #include <user32/nc.h>
19 #define MAX_DOS_DRIVES 26
21 #define abs(x) ((x) < 0 ? -(x) : (x))
23 #define WM_LBTRACKPOINT 0x0131
24 #define WS_EX_DRAGDETECT 0x00000002L
27 #define WM_DROPOBJECT 0x022A
28 #define WM_QUERYDROPOBJECT 0x022B
29 #define WM_BEGINDRAG 0x022C
30 #define WM_DRAGLOOP 0x022D
31 #define WM_DRAGSELECT 0x022E
32 #define WM_DRAGMOVE 0x022F
42 /* Items array granularity */
43 #define LB_ARRAY_GRANULARITY 16
45 /* Scrolling timeout in ms */
46 #define LB_SCROLL_TIMEOUT 50
48 /* Listbox system timer id */
54 LPSTR str
; /* Item text */
55 BOOL selected
; /* Is item selected? */
56 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
57 DWORD data
; /* User data */
60 /* Listbox structure */
63 HANDLE heap
; /* Heap for this listbox */
64 HWND owner
; /* Owner window to send notifications to */
65 UINT style
; /* Window style */
66 INT width
; /* Window width */
67 INT height
; /* Window height */
68 LB_ITEMDATA
*items
; /* Array of items */
69 INT nb_items
; /* Number of items */
70 INT top_item
; /* Top visible item */
71 INT selected_item
; /* Selected item */
72 INT focus_item
; /* Item that has the focus */
73 INT anchor_item
; /* Anchor item for extended selection */
74 INT item_height
; /* Default item height */
75 INT page_size
; /* Items per listbox page */
76 INT column_width
; /* Column width for multi-column listboxes */
77 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
78 INT horz_pos
; /* Horizontal position */
79 INT nb_tabs
; /* Number of tabs in array */
80 INT
*tabs
; /* Array of tabs */
81 BOOL caret_on
; /* Is caret on? */
82 HFONT font
; /* Current font */
83 LCID locale
; /* Current locale for string comparisons */
84 LPHEADCOMBO lphc
; /* ComboLBox */
88 #define IS_OWNERDRAW(descr) \
89 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
91 #define HAS_STRINGS(descr) \
92 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
94 #define SEND_NOTIFICATION(wnd,descr,code) \
95 (SendMessageA( (descr)->owner, WM_COMMAND, \
96 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (LPARAM)(wnd)->hwndSelf ))
98 /* Current timer status */
108 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
111 /***********************************************************************
114 void LISTBOX_DPRINT( WND
*wnd
)
118 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
120 DPRINT( "Listbox:\n" );
121 DPRINT( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
122 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
124 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
126 DPRINT( "%4d: %-40s %d %08lx %3d\n",
127 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
132 /***********************************************************************
133 * LISTBOX_GetCurrentPageSize
135 * Return the current page size
137 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
140 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
141 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
143 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
145 if (i
== descr
->top_item
) return 1;
146 else return i
- descr
->top_item
;
150 /***********************************************************************
151 * LISTBOX_GetMaxTopIndex
153 * Return the maximum possible index for the top of the listbox.
155 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
159 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
161 page
= descr
->height
;
162 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
163 if ((page
-= descr
->items
[max
].height
) < 0) break;
164 if (max
< descr
->nb_items
- 1) max
++;
166 else if (descr
->style
& LBS_MULTICOLUMN
)
168 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
169 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
170 max
= (max
- page
) * descr
->page_size
;
174 max
= descr
->nb_items
- descr
->page_size
;
176 if (max
< 0) max
= 0;
181 /***********************************************************************
182 * LISTBOX_UpdateScroll
184 * Update the scrollbars. Should be called whenever the content
185 * of the listbox changes.
187 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
191 if (!(descr
->style
& WS_VSCROLL
)) return;
192 /* It is important that we check descr->style, and not wnd->dwStyle,
193 for WS_VSCROLL, as the former is exactly the one passed in
194 argument to CreateWindow.
195 In Windows (and from now on in Wine :) a listbox created
196 with such a style (no WS_SCROLL) does not update
197 the scrollbar with listbox-related data, thus letting
198 the programmer use it for his/her own purposes. */
200 if (descr
->style
& LBS_NOREDRAW
) return;
201 info
.cbSize
= sizeof(info
);
203 if (descr
->style
& LBS_MULTICOLUMN
)
206 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
207 info
.nPos
= descr
->top_item
/ descr
->page_size
;
208 info
.nPage
= descr
->width
/ descr
->column_width
;
209 if (info
.nPage
< 1) info
.nPage
= 1;
210 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
211 if (descr
->style
& LBS_DISABLENOSCROLL
)
212 info
.fMask
|= SIF_DISABLENOSCROLL
;
213 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
215 info
.fMask
= SIF_RANGE
;
216 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
221 info
.nMax
= descr
->nb_items
- 1;
222 info
.nPos
= descr
->top_item
;
223 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
224 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
225 if (descr
->style
& LBS_DISABLENOSCROLL
)
226 info
.fMask
|= SIF_DISABLENOSCROLL
;
227 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
229 if (descr
->horz_extent
)
232 info
.nMax
= descr
->horz_extent
- 1;
233 info
.nPos
= descr
->horz_pos
;
234 info
.nPage
= descr
->width
;
235 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
236 if (descr
->style
& LBS_DISABLENOSCROLL
)
237 info
.fMask
|= SIF_DISABLENOSCROLL
;
238 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
244 /***********************************************************************
247 * Set the top item of the listbox, scrolling up or down if necessary.
249 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
252 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
253 if (index
> max
) index
= max
;
254 if (index
< 0) index
= 0;
255 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
256 if (descr
->top_item
== index
) return LB_OKAY
;
257 if (descr
->style
& LBS_MULTICOLUMN
)
259 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
260 if (scroll
&& (abs(diff
) < descr
->width
))
261 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
262 SW_INVALIDATE
| SW_ERASE
);
269 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
273 if (index
> descr
->top_item
)
275 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
276 diff
-= descr
->items
[i
].height
;
280 for (i
= index
; i
< descr
->top_item
; i
++)
281 diff
+= descr
->items
[i
].height
;
285 diff
= (descr
->top_item
- index
) * descr
->item_height
;
287 if (abs(diff
) < descr
->height
)
288 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
289 SW_INVALIDATE
| SW_ERASE
);
293 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
294 descr
->top_item
= index
;
295 LISTBOX_UpdateScroll( wnd
, descr
);
300 /***********************************************************************
303 * Update the page size. Should be called when the size of
304 * the client area or the item height changes.
306 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
310 if ((page_size
= descr
->height
/ descr
->item_height
) < 1) page_size
= 1;
311 if (page_size
== descr
->page_size
) return;
312 descr
->page_size
= page_size
;
313 if (descr
->style
& LBS_MULTICOLUMN
)
314 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
315 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
319 /***********************************************************************
322 * Update the size of the listbox. Should be called when the size of
323 * the client area changes.
325 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
329 GetClientRect( wnd
->hwndSelf
, &rect
);
330 descr
->width
= rect
.right
- rect
.left
;
331 descr
->height
= rect
.bottom
- rect
.top
;
332 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
334 if ((descr
->height
> descr
->item_height
) &&
335 (descr
->height
% descr
->item_height
))
337 DPRINT( "[%04x]: changing height %d -> %d\n",
338 wnd
->hwndSelf
, descr
->height
,
339 descr
->height
- descr
->height
%descr
->item_height
);
340 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
341 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
342 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
343 (descr
->height
% descr
->item_height
),
344 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
348 DPRINT( "[%04x]: new size = %d,%d\n",
349 wnd
->hwndSelf
, descr
->width
, descr
->height
);
350 LISTBOX_UpdatePage( wnd
, descr
);
351 LISTBOX_UpdateScroll( wnd
, descr
);
355 /***********************************************************************
356 * LISTBOX_GetItemRect
358 * Get the rectangle enclosing an item, in listbox client coordinates.
359 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
361 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
364 /* Index <= 0 is legal even on empty listboxes */
365 if (index
&& (index
>= descr
->nb_items
)) return -1;
366 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
367 if (descr
->style
& LBS_MULTICOLUMN
)
369 INT col
= (index
/ descr
->page_size
) -
370 (descr
->top_item
/ descr
->page_size
);
371 rect
->left
+= col
* descr
->column_width
;
372 rect
->right
= rect
->left
+ descr
->column_width
;
373 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
374 rect
->bottom
= rect
->top
+ descr
->item_height
;
376 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
379 rect
->right
+= descr
->horz_pos
;
380 if ((index
>= 0) && (index
< descr
->nb_items
))
382 if (index
< descr
->top_item
)
384 for (i
= descr
->top_item
-1; i
>= index
; i
--)
385 rect
->top
-= descr
->items
[i
].height
;
389 for (i
= descr
->top_item
; i
< index
; i
++)
390 rect
->top
+= descr
->items
[i
].height
;
392 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
398 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
399 rect
->bottom
= rect
->top
+ descr
->item_height
;
400 rect
->right
+= descr
->horz_pos
;
403 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
404 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
408 /***********************************************************************
409 * LISTBOX_GetItemFromPoint
411 * Return the item nearest from point (x,y) (in client coordinates).
413 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
416 INT index
= descr
->top_item
;
418 if (!descr
->nb_items
) return -1; /* No items */
419 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
424 while (index
< descr
->nb_items
)
426 if ((pos
+= descr
->items
[index
].height
) > y
) break;
435 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
439 else if (descr
->style
& LBS_MULTICOLUMN
)
441 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
442 if (y
>= 0) index
+= y
/ descr
->item_height
;
443 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
444 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
448 index
+= (y
/ descr
->item_height
);
450 if (index
< 0) return 0;
451 if (index
>= descr
->nb_items
) return -1;
456 /***********************************************************************
461 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
462 const RECT
*rect
, INT index
, UINT action
)
464 LB_ITEMDATA
*item
= NULL
;
465 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
467 if (IS_OWNERDRAW(descr
))
470 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
472 dis
.CtlType
= ODT_LISTBOX
;
474 dis
.hwndItem
= wnd
->hwndSelf
;
475 dis
.itemAction
= action
;
479 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
480 if ((descr
->focus_item
== index
) &&
482 (GetFocus() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
483 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
484 dis
.itemData
= item
? item
->data
: 0;
486 DPRINT( "[%04x]: drawitem %d (%s) action=%02x "
487 "state=%02x rect=%d,%d-%d,%d\n",
488 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
489 dis
.itemState
, rect
->left
, rect
->top
,
490 rect
->right
, rect
->bottom
);
491 SendMessageA(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
495 COLORREF oldText
= 0, oldBk
= 0;
497 if (action
== ODA_FOCUS
)
499 DrawFocusRect( hdc
, rect
);
502 if (item
&& item
->selected
)
504 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
505 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
508 DPRINT( "[%04x]: painting %d (%s) action=%02x "
509 "rect=%d,%d-%d,%d\n",
510 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
511 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
513 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
514 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
515 else if (!(descr
->style
& LBS_USETABSTOPS
))
516 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
517 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
518 strlen(item
->str
), NULL
);
521 /* Output empty string to paint background in the full width. */
522 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
523 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
524 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
525 item
->str
, strlen(item
->str
),
526 descr
->nb_tabs
, descr
->tabs
, 0);
528 if (item
&& item
->selected
)
530 SetBkColor( hdc
, oldBk
);
531 SetTextColor( hdc
, oldText
);
533 if ((descr
->focus_item
== index
) &&
535 (GetFocus() == wnd
->hwndSelf
)) DrawFocusRect( hdc
, rect
);
540 /***********************************************************************
543 * Change the redraw flag.
545 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
549 if (!(descr
->style
& LBS_NOREDRAW
)) return;
550 descr
->style
&= ~LBS_NOREDRAW
;
551 LISTBOX_UpdateScroll( wnd
, descr
);
553 else descr
->style
|= LBS_NOREDRAW
;
557 /***********************************************************************
558 * LISTBOX_RepaintItem
560 * Repaint a single item synchronously.
562 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
568 HBRUSH hbrush
, oldBrush
= 0;
570 if (descr
->style
& LBS_NOREDRAW
) return;
571 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
572 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
573 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
574 hbrush
= (HBRUSH
)SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
575 (WPARAM
)hdc
, (LPARAM
)wnd
->hwndSelf
);
576 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
577 if (wnd
->dwStyle
& WS_DISABLED
)
578 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
579 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
580 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
581 if (oldFont
) SelectObject( hdc
, oldFont
);
582 if (oldBrush
) SelectObject( hdc
, oldBrush
);
583 ReleaseDC( wnd
->hwndSelf
, hdc
);
587 /***********************************************************************
588 * LISTBOX_InitStorage
590 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
595 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
596 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
598 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
599 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
600 nb_items
* sizeof(LB_ITEMDATA
) )))
602 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
610 /***********************************************************************
611 * LISTBOX_SetTabStops
613 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
614 LPINT tabs
, BOOL short_ints
)
616 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
617 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
618 if (!(descr
->nb_tabs
= count
))
623 /* FIXME: count = 1 */
624 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
625 descr
->nb_tabs
* sizeof(INT
) )))
630 INT
* p
= (INT
*)tabs
;
631 // dbg_decl_str(listbox, 256);
633 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
634 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
635 // if(TRACE_ON(listbox))
636 // dsprintf(listbox, "%hd ", descr->tabs[i]);
638 DPRINT( "[%04x]: settabstops %s\n",
639 wnd
->hwndSelf
, dbg_str(listbox
));
641 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
642 /* FIXME: repaint the window? */
647 /***********************************************************************
650 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
653 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
654 if (HAS_STRINGS(descr
))
657 return strlen(descr
->items
[index
].str
);
658 lstrcpyA( buffer
, descr
->items
[index
].str
);
659 return strlen(buffer
);
662 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
663 return sizeof(DWORD
);
668 /***********************************************************************
669 * LISTBOX_FindStringPos
671 * Find the nearest string located before a given string in sort order.
672 * If 'exact' is TRUE, return an error if we don't get an exact match.
674 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
677 INT index
, min
, max
, res
= -1;
679 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
681 max
= descr
->nb_items
;
684 index
= (min
+ max
) / 2;
685 if (HAS_STRINGS(descr
))
686 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
689 COMPAREITEMSTRUCT cis
;
690 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
692 cis
.CtlType
= ODT_LISTBOX
;
694 cis
.hwndItem
= wnd
->hwndSelf
;
696 cis
.itemData1
= descr
->items
[index
].data
;
698 cis
.itemData2
= (DWORD
)str
;
699 //cis.dwLocaleId = descr->locale;
700 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
703 if (!res
) return index
;
704 if (res
> 0) max
= index
;
705 else min
= index
+ 1;
707 return exact
? -1 : max
;
711 /***********************************************************************
712 * LISTBOX_FindFileStrPos
714 * Find the nearest string located before a given string in directory
715 * sort order (i.e. first files, then directories, then drives).
717 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
719 INT min
, max
, res
= -1;
721 if (!HAS_STRINGS(descr
))
722 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
724 max
= descr
->nb_items
;
727 INT index
= (min
+ max
) / 2;
728 const char *p
= descr
->items
[index
].str
;
729 if (*p
== '[') /* drive or directory */
731 if (*str
!= '[') res
= -1;
732 else if (p
[1] == '-') /* drive */
734 if (str
[1] == '-') res
= str
[2] - p
[2];
739 if (str
[1] == '-') res
= 1;
740 else res
= lstrcmpiA( str
, p
);
745 if (*str
== '[') res
= 1;
746 else res
= lstrcmpiA( str
, p
);
748 if (!res
) return index
;
749 if (res
< 0) max
= index
;
750 else min
= index
+ 1;
756 /***********************************************************************
759 * Find the item beginning with a given string.
761 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
762 LPCSTR str
, BOOL exact
)
767 if (start
>= descr
->nb_items
) start
= -1;
768 item
= descr
->items
+ start
+ 1;
769 if (HAS_STRINGS(descr
))
771 if (!str
) return LB_ERR
;
774 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
775 if (!lstrcmpiA( str
, item
->str
)) return i
;
776 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
777 if (!lstrcmpiA( str
, item
->str
)) return i
;
781 /* Special case for drives and directories: ignore prefix */
782 #define CHECK_DRIVE(item) \
783 if ((item)->str[0] == '[') \
785 if (!lstrcmpiA( str, (item)->str+1 )) return i; \
786 if (((item)->str[1] == '-') && !lstrcmpiA(str,(item)->str+2)) \
790 INT len
= lstrlenA(str
);
791 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
793 if (!lstrcmpiA( str
, item
->str
)) return i
;
796 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
798 if (!lstrcmpiA( str
, item
->str
)) return i
;
806 if (exact
&& (descr
->style
& LBS_SORT
))
807 /* If sorted, use a WM_COMPAREITEM binary search */
808 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
810 /* Otherwise use a linear search */
811 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
812 if (item
->data
== (DWORD
)str
) return i
;
813 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
814 if (item
->data
== (DWORD
)str
) return i
;
820 /***********************************************************************
821 * LISTBOX_GetSelCount
823 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
826 LB_ITEMDATA
*item
= descr
->items
;
828 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
829 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
830 if (item
->selected
) count
++;
837 /***********************************************************************
838 * LISTBOX_GetSelItems
840 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
844 LB_ITEMDATA
*item
= descr
->items
;
846 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
847 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
848 if (item
->selected
) array
[count
++] = i
;
853 /***********************************************************************
856 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
858 INT i
, col_pos
= descr
->page_size
- 1;
861 HBRUSH hbrush
, oldBrush
= 0;
863 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
864 if (descr
->style
& LBS_NOREDRAW
) return 0;
865 if (descr
->style
& LBS_MULTICOLUMN
)
866 rect
.right
= rect
.left
+ descr
->column_width
;
867 else if (descr
->horz_pos
)
869 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
870 rect
.right
+= descr
->horz_pos
;
873 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
874 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
875 (WPARAM
)hdc
, (LPARAM
)wnd
->hwndSelf
);
876 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
877 if (wnd
->dwStyle
& WS_DISABLED
)
878 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
880 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
881 (GetFocus() == wnd
->hwndSelf
))
883 /* Special case for empty listbox: paint focus rect */
884 rect
.bottom
= rect
.top
+ descr
->item_height
;
885 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
887 rect
.top
= rect
.bottom
;
890 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
892 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
893 rect
.bottom
= rect
.top
+ descr
->item_height
;
895 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
897 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
898 rect
.top
= rect
.bottom
;
900 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
902 if (!IS_OWNERDRAW(descr
))
904 /* Clear the bottom of the column */
905 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
906 if (rect
.top
< descr
->height
)
908 rect
.bottom
= descr
->height
;
909 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
910 &rect
, NULL
, 0, NULL
);
914 /* Go to the next column */
915 rect
.left
+= descr
->column_width
;
916 rect
.right
+= descr
->column_width
;
918 col_pos
= descr
->page_size
- 1;
923 if (rect
.top
>= descr
->height
) break;
927 if (!IS_OWNERDRAW(descr
))
929 /* Clear the remainder of the client area */
930 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
931 if (rect
.top
< descr
->height
)
933 rect
.bottom
= descr
->height
;
934 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
935 &rect
, NULL
, 0, NULL
);
937 if (rect
.right
< descr
->width
)
939 rect
.left
= rect
.right
;
940 rect
.right
= descr
->width
;
942 rect
.bottom
= descr
->height
;
943 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
944 &rect
, NULL
, 0, NULL
);
947 if (oldFont
) SelectObject( hdc
, oldFont
);
948 if (oldBrush
) SelectObject( hdc
, oldBrush
);
953 /***********************************************************************
954 * LISTBOX_InvalidateItems
956 * Invalidate all items from a given item. If the specified item is not
957 * visible, nothing happens.
959 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
963 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
965 rect
.bottom
= descr
->height
;
966 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
967 if (descr
->style
& LBS_MULTICOLUMN
)
969 /* Repaint the other columns */
970 rect
.left
= rect
.right
;
971 rect
.right
= descr
->width
;
973 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
979 /***********************************************************************
980 * LISTBOX_GetItemHeight
982 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
984 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
986 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
987 return descr
->items
[index
].height
;
989 else return descr
->item_height
;
993 /***********************************************************************
994 * LISTBOX_SetItemHeight
996 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
999 if (!height
) height
= 1;
1001 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1003 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1004 DPRINT( "[%04x]: item %d height = %d\n",
1005 wnd
->hwndSelf
, index
, height
);
1006 descr
->items
[index
].height
= height
;
1007 LISTBOX_UpdateScroll( wnd
, descr
);
1008 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1010 else if (height
!= descr
->item_height
)
1012 DPRINT( "[%04x]: new height = %d\n",
1013 wnd
->hwndSelf
, height
);
1014 descr
->item_height
= height
;
1015 LISTBOX_UpdatePage( wnd
, descr
);
1016 LISTBOX_UpdateScroll( wnd
, descr
);
1017 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1023 /***********************************************************************
1024 * LISTBOX_SetHorizontalPos
1026 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1030 if (pos
> descr
->horz_extent
- descr
->width
)
1031 pos
= descr
->horz_extent
- descr
->width
;
1032 if (pos
< 0) pos
= 0;
1033 if (!(diff
= descr
->horz_pos
- pos
)) return;
1034 DPRINT( "[%04x]: new horz pos = %d\n",
1035 wnd
->hwndSelf
, pos
);
1036 descr
->horz_pos
= pos
;
1037 LISTBOX_UpdateScroll( wnd
, descr
);
1038 if (abs(diff
) < descr
->width
)
1039 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1040 SW_INVALIDATE
| SW_ERASE
);
1042 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1046 /***********************************************************************
1047 * LISTBOX_SetHorizontalExtent
1049 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1052 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1054 if (extent
<= 0) extent
= 1;
1055 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1056 DPRINT( "[%04x]: new horz extent = %d\n",
1057 wnd
->hwndSelf
, extent
);
1058 descr
->horz_extent
= extent
;
1059 if (descr
->horz_pos
> extent
- descr
->width
)
1060 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1062 LISTBOX_UpdateScroll( wnd
, descr
);
1067 /***********************************************************************
1068 * LISTBOX_SetColumnWidth
1070 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1072 width
+= 2; /* For left and right margin */
1073 if (width
== descr
->column_width
) return LB_OKAY
;
1074 DPRINT( "[%04x]: new column width = %d\n",
1075 wnd
->hwndSelf
, width
);
1076 descr
->column_width
= width
;
1077 LISTBOX_UpdatePage( wnd
, descr
);
1082 /***********************************************************************
1085 * Returns the item height.
1087 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1095 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1097 DPRINT("unable to get DC.\n" );
1100 if (font
) oldFont
= SelectObject( hdc
, font
);
1101 GetTextMetricsA( hdc
, &tm
);
1102 if (oldFont
) SelectObject( hdc
, oldFont
);
1103 ReleaseDC( wnd
->hwndSelf
, hdc
);
1104 if (!IS_OWNERDRAW(descr
))
1105 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1106 return tm
.tmHeight
;
1110 /***********************************************************************
1111 * LISTBOX_MakeItemVisible
1113 * Make sure that a given item is partially or fully visible.
1115 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1120 if (index
<= descr
->top_item
) top
= index
;
1121 else if (descr
->style
& LBS_MULTICOLUMN
)
1123 INT cols
= descr
->width
;
1124 if (!fully
) cols
+= descr
->column_width
- 1;
1125 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1127 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1128 top
= index
- descr
->page_size
* (cols
- 1);
1130 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1132 INT height
= fully
? descr
->items
[index
].height
: 1;
1133 for (top
= index
; top
> descr
->top_item
; top
--)
1134 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1138 if (index
< descr
->top_item
+ descr
->page_size
) return;
1139 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1140 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1141 top
= index
- descr
->page_size
+ 1;
1143 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1147 /***********************************************************************
1148 * LISTBOX_SelectItemRange
1150 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1152 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1157 /* A few sanity checks */
1159 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1160 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1161 if (last
== -1) last
= descr
->nb_items
- 1;
1162 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1163 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1164 /* selected_item reflects last selected/unselected item on multiple sel */
1165 descr
->selected_item
= last
;
1167 if (on
) /* Turn selection on */
1169 for (i
= first
; i
<= last
; i
++)
1171 if (descr
->items
[i
].selected
) continue;
1172 descr
->items
[i
].selected
= TRUE
;
1173 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1176 else /* Turn selection off */
1178 for (i
= first
; i
<= last
; i
++)
1180 if (!descr
->items
[i
].selected
) continue;
1181 descr
->items
[i
].selected
= FALSE
;
1182 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1189 /***********************************************************************
1190 * LISTBOX_SetCaretIndex
1193 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1196 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1197 BOOL fully_visible
)
1199 INT oldfocus
= descr
->focus_item
;
1201 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1202 if (index
== oldfocus
) return LB_OKAY
;
1203 descr
->focus_item
= index
;
1204 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1205 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1207 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1208 if (descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1209 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1215 /***********************************************************************
1216 * LISTBOX_SetSelection
1218 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1219 BOOL on
, BOOL send_notify
)
1221 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1222 if (descr
->style
& LBS_MULTIPLESEL
)
1224 if (index
== -1) /* Select all items */
1225 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1226 else /* Only one item */
1227 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1231 INT oldsel
= descr
->selected_item
;
1232 if (index
== oldsel
) return LB_OKAY
;
1233 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1234 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1235 descr
->selected_item
= index
;
1236 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1237 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1238 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1239 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1241 if( descr
->lphc
) /* set selection change flag for parent combo */
1242 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1248 /***********************************************************************
1251 * Change the caret position and extend the selection to the new caret.
1253 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1254 BOOL fully_visible
)
1256 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1257 if (descr
->style
& LBS_EXTENDEDSEL
)
1259 if (descr
->anchor_item
!= -1)
1261 INT first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1262 INT last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1264 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1265 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1266 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1269 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1271 /* Set selection to new caret item */
1272 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1277 /***********************************************************************
1278 * LISTBOX_InsertItem
1280 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1281 LPSTR str
, DWORD data
)
1286 if (index
== -1) index
= descr
->nb_items
;
1287 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1288 if (!descr
->items
) max_items
= 0;
1289 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1290 if (descr
->nb_items
== max_items
)
1292 /* We need to grow the array */
1293 max_items
+= LB_ARRAY_GRANULARITY
;
1294 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1295 max_items
* sizeof(LB_ITEMDATA
) )))
1297 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1300 descr
->items
= item
;
1303 /* Insert the item structure */
1305 item
= &descr
->items
[index
];
1306 if (index
< descr
->nb_items
)
1307 MoveMemory( item
+ 1, item
, (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1311 item
->selected
= FALSE
;
1314 /* Get item height */
1316 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1318 MEASUREITEMSTRUCT mis
;
1319 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1321 mis
.CtlType
= ODT_LISTBOX
;
1324 mis
.itemData
= descr
->items
[index
].data
;
1325 mis
.itemHeight
= descr
->item_height
;
1326 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1327 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1328 DPRINT( "[%04x]: measure item %d (%s) = %d\n",
1329 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1332 /* Repaint the items */
1334 LISTBOX_UpdateScroll( wnd
, descr
);
1335 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1337 /* Move selection and focused item */
1339 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1340 if (index
<= descr
->focus_item
)
1342 descr
->focus_item
++;
1343 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1346 /* If listbox was empty, set focus to the first item */
1348 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1353 /***********************************************************************
1354 * LISTBOX_InsertString
1356 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1359 LPSTR new_str
= NULL
;
1363 if (HAS_STRINGS(descr
))
1365 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1367 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1371 else data
= (DWORD
)str
;
1373 if (index
== -1) index
= descr
->nb_items
;
1374 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1376 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1380 DPRINT( "[%04x]: added item %d '%s'\n",
1381 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1386 /***********************************************************************
1387 * LISTBOX_DeleteItem
1389 * Delete the content of an item. 'index' must be a valid index.
1391 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1393 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1394 * while Win95 sends it for all items with user data.
1395 * It's probably better to send it too often than not
1396 * often enough, so this is what we do here.
1398 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1400 DELETEITEMSTRUCT dis
;
1401 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1403 dis
.CtlType
= ODT_LISTBOX
;
1406 dis
.hwndItem
= wnd
->hwndSelf
;
1407 dis
.itemData
= descr
->items
[index
].data
;
1408 SendMessageA( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1410 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1411 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1415 /***********************************************************************
1416 * LISTBOX_RemoveItem
1418 * Remove an item from the listbox and delete its content.
1420 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1425 if (index
== -1) index
= descr
->nb_items
- 1;
1426 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1427 LISTBOX_DeleteItem( wnd
, descr
, index
);
1429 /* Remove the item */
1431 item
= &descr
->items
[index
];
1432 if (index
< descr
->nb_items
-1)
1433 MoveMemory( item
, item
+ 1,
1434 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1436 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1438 /* Shrink the item array if possible */
1440 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1441 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1443 max_items
-= LB_ARRAY_GRANULARITY
;
1444 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1445 max_items
* sizeof(LB_ITEMDATA
) );
1446 if (item
) descr
->items
= item
;
1449 /* Repaint the items */
1451 LISTBOX_UpdateScroll( wnd
, descr
);
1452 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1454 /* Move selection and focused item */
1456 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1457 if (index
<= descr
->focus_item
)
1459 descr
->focus_item
--;
1460 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1466 /***********************************************************************
1467 * LISTBOX_ResetContent
1469 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1473 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1474 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1475 descr
->nb_items
= 0;
1476 descr
->top_item
= 0;
1477 descr
->selected_item
= -1;
1478 descr
->focus_item
= 0;
1479 descr
->anchor_item
= -1;
1480 descr
->items
= NULL
;
1481 LISTBOX_UpdateScroll( wnd
, descr
);
1482 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1486 /***********************************************************************
1489 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1493 if (HAS_STRINGS(descr
)) return LB_ERR
;
1494 /* FIXME: this is far from optimal... */
1495 if (count
> descr
->nb_items
)
1497 while (count
> descr
->nb_items
)
1498 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1501 else if (count
< descr
->nb_items
)
1503 while (count
< descr
->nb_items
)
1504 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1511 /***********************************************************************
1514 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1515 LPCSTR filespec
, BOOL long_names
)
1518 LRESULT ret
= LB_OKAY
;
1519 WIN32_FIND_DATA entry
;
1522 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1524 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1531 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1533 if (!(attrib
& DDL_DIRECTORY
) ||
1534 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1535 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1536 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1538 else /* not a directory */
1540 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1541 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1543 if ((attrib
& DDL_EXCLUSIVE
) &&
1544 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1547 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1548 else strcpy( buffer
, entry
.cAlternateFileName
);
1550 if (!long_names
) CharLowerA( buffer
);
1551 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1552 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1554 } while (FindNextFileA( handle
, &entry
));
1555 FindClose( handle
);
1558 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1560 char buffer
[] = "[-a-]";
1562 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1564 //if (!DRIVE_IsValid(drive)) continue;
1565 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1573 /***********************************************************************
1574 * LISTBOX_HandleVScroll
1576 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1577 WPARAM wParam
, LPARAM lParam
)
1581 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1582 switch(LOWORD(wParam
))
1585 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1588 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1591 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1592 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1595 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1596 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1598 case SB_THUMBPOSITION
:
1599 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1602 info
.cbSize
= sizeof(info
);
1603 info
.fMask
= SIF_TRACKPOS
;
1604 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1605 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1608 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1611 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1618 /***********************************************************************
1619 * LISTBOX_HandleHScroll
1621 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1622 WPARAM wParam
, LPARAM lParam
)
1627 if (descr
->style
& LBS_MULTICOLUMN
)
1629 switch(LOWORD(wParam
))
1632 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1636 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1640 page
= descr
->width
/ descr
->column_width
;
1641 if (page
< 1) page
= 1;
1642 LISTBOX_SetTopItem( wnd
, descr
,
1643 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1646 page
= descr
->width
/ descr
->column_width
;
1647 if (page
< 1) page
= 1;
1648 LISTBOX_SetTopItem( wnd
, descr
,
1649 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1651 case SB_THUMBPOSITION
:
1652 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1656 info
.cbSize
= sizeof(info
);
1657 info
.fMask
= SIF_TRACKPOS
;
1658 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1659 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1663 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1666 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1670 else if (descr
->horz_extent
)
1672 switch(LOWORD(wParam
))
1675 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1678 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1681 LISTBOX_SetHorizontalPos( wnd
, descr
,
1682 descr
->horz_pos
- descr
->width
);
1685 LISTBOX_SetHorizontalPos( wnd
, descr
,
1686 descr
->horz_pos
+ descr
->width
);
1688 case SB_THUMBPOSITION
:
1689 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1692 info
.cbSize
= sizeof(info
);
1693 info
.fMask
= SIF_TRACKPOS
;
1694 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1695 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1698 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1701 LISTBOX_SetHorizontalPos( wnd
, descr
,
1702 descr
->horz_extent
- descr
->width
);
1710 /***********************************************************************
1711 * LISTBOX_HandleLButtonDown
1713 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1714 WPARAM wParam
, INT x
, INT y
)
1716 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1717 DPRINT( "[%04x]: lbuttondown %d,%d item %d\n",
1718 wnd
->hwndSelf
, x
, y
, index
);
1719 if (!descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
)) return 0;
1722 if (descr
->style
& LBS_EXTENDEDSEL
)
1724 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1725 if (wParam
& MK_CONTROL
)
1727 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1728 LISTBOX_SetSelection( wnd
, descr
, index
,
1729 !descr
->items
[index
].selected
, FALSE
);
1731 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1735 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1736 LISTBOX_SetSelection( wnd
, descr
, index
,
1737 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1738 !descr
->items
[index
].selected
), FALSE
);
1742 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1743 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1744 : descr
->lphc
->self
->hwndSelf
) ;
1746 SetCapture( wnd
->hwndSelf
);
1747 if (index
!= -1 && !descr
->lphc
)
1749 if (descr
->style
& LBS_NOTIFY
)
1750 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1751 MAKELPARAM( x
, y
) );
1752 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1754 POINT pt
= { x
, y
};
1755 // if (DragDetect( wnd->hwndSelf, pt ))
1756 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1763 /***********************************************************************
1764 * LISTBOX_HandleLButtonUp
1766 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1768 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1769 KillTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1770 LISTBOX_Timer
= LB_TIMER_NONE
;
1771 if (GetCapture() == wnd
->hwndSelf
)
1774 if (descr
->style
& LBS_NOTIFY
)
1775 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1781 /***********************************************************************
1782 * LISTBOX_HandleTimer
1784 * Handle scrolling upon a timer event.
1785 * Return TRUE if scrolling should continue.
1787 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1788 INT index
, TIMER_DIRECTION dir
)
1793 if (descr
->top_item
) index
= descr
->top_item
- 1;
1797 if (descr
->top_item
) index
-= descr
->page_size
;
1800 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1801 if (index
== descr
->focus_item
) index
++;
1802 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1804 case LB_TIMER_RIGHT
:
1805 if (index
+ descr
->page_size
< descr
->nb_items
)
1806 index
+= descr
->page_size
;
1811 if (index
== descr
->focus_item
) return FALSE
;
1812 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1817 /***********************************************************************
1818 * LISTBOX_HandleSystemTimer
1820 * WM_SYSTIMER handler.
1822 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1824 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1826 KillTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1827 LISTBOX_Timer
= LB_TIMER_NONE
;
1833 /***********************************************************************
1834 * LISTBOX_HandleMouseMove
1836 * WM_MOUSEMOVE handler.
1838 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1842 TIMER_DIRECTION dir
;
1844 if (descr
->style
& LBS_MULTICOLUMN
)
1847 else if (y
>= descr
->item_height
* descr
->page_size
)
1848 y
= descr
->item_height
* descr
->page_size
- 1;
1852 dir
= LB_TIMER_LEFT
;
1855 else if (x
>= descr
->width
)
1857 dir
= LB_TIMER_RIGHT
;
1858 x
= descr
->width
- 1;
1860 else dir
= LB_TIMER_NONE
; /* inside */
1864 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1865 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1866 else dir
= LB_TIMER_NONE
; /* inside */
1869 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1870 if (index
== -1) index
= descr
->focus_item
;
1871 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1873 /* Start/stop the system timer */
1875 if (dir
!= LB_TIMER_NONE
)
1876 SetTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1877 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1878 KillTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1879 LISTBOX_Timer
= dir
;
1883 /***********************************************************************
1884 * LISTBOX_HandleKeyDown
1886 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1889 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1891 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
1892 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1894 if (caret
== -2) return 0;
1896 if (caret
== -1) switch(wParam
)
1899 if (descr
->style
& LBS_MULTICOLUMN
)
1901 if (descr
->focus_item
>= descr
->page_size
)
1902 caret
= descr
->focus_item
- descr
->page_size
;
1907 caret
= descr
->focus_item
- 1;
1908 if (caret
< 0) caret
= 0;
1911 if (descr
->style
& LBS_MULTICOLUMN
)
1913 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1914 caret
= descr
->focus_item
+ descr
->page_size
;
1919 caret
= descr
->focus_item
+ 1;
1920 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1923 if (descr
->style
& LBS_MULTICOLUMN
)
1925 INT page
= descr
->width
/ descr
->column_width
;
1926 if (page
< 1) page
= 1;
1927 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1929 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1930 if (caret
< 0) caret
= 0;
1933 if (descr
->style
& LBS_MULTICOLUMN
)
1935 INT page
= descr
->width
/ descr
->column_width
;
1936 if (page
< 1) page
= 1;
1937 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1939 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1940 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1946 caret
= descr
->nb_items
- 1;
1949 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1950 else if (descr
->style
& LBS_MULTIPLESEL
)
1952 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1953 !descr
->items
[descr
->focus_item
].selected
,
1954 (descr
->style
& LBS_NOTIFY
) != 0 );
1956 else if (descr
->selected_item
== -1)
1958 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1959 (descr
->style
& LBS_NOTIFY
) != 0 );
1965 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1966 !(GetKeyState( VK_SHIFT
) & 0x8000))
1967 descr
->anchor_item
= caret
;
1968 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1969 if (descr
->style
& LBS_NOTIFY
)
1971 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1973 /* make sure that combo parent doesn't hide us */
1974 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1976 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1983 /***********************************************************************
1984 * LISTBOX_HandleChar
1986 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
1990 char str
[2] = { wParam
& 0xff, '\0' };
1992 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1994 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
1995 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1997 if (caret
== -2) return 0;
2000 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2003 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2004 if (descr
->style
& LBS_NOTIFY
)
2005 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2011 /***********************************************************************
2014 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2017 MEASUREITEMSTRUCT mis
;
2020 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2022 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2024 HeapFree( GetProcessHeap(), 0, descr
);
2027 GetClientRect( wnd
->hwndSelf
, &rect
);
2028 descr
->owner
= GetParent( wnd
->hwndSelf
);
2029 descr
->style
= wnd
->dwStyle
;
2030 descr
->width
= rect
.right
- rect
.left
;
2031 descr
->height
= rect
.bottom
- rect
.top
;
2032 descr
->items
= NULL
;
2033 descr
->nb_items
= 0;
2034 descr
->top_item
= 0;
2035 descr
->selected_item
= -1;
2036 descr
->focus_item
= 0;
2037 descr
->anchor_item
= -1;
2038 descr
->item_height
= 1;
2039 descr
->page_size
= 1;
2040 descr
->column_width
= 150;
2041 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2042 descr
->horz_pos
= 0;
2045 descr
->caret_on
= TRUE
;
2047 descr
->locale
= 0; /* FIXME */
2052 DPRINT("[%04x]: resetting owner %04x -> %04x\n",
2053 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2054 descr
->owner
= lphc
->self
->hwndSelf
;
2057 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2059 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2061 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2062 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2063 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2064 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2066 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2068 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2070 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2071 descr
->item_height
= lphc
->RectButton
.bottom
- lphc
->RectButton
.top
- 6;
2075 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2077 mis
.CtlType
= ODT_LISTBOX
;
2082 mis
.itemHeight
= descr
->item_height
;
2083 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2084 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2092 /***********************************************************************
2095 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2097 LISTBOX_ResetContent( wnd
, descr
);
2098 HeapDestroy( descr
->heap
);
2099 HeapFree( GetProcessHeap(), 0, descr
);
2105 /***********************************************************************
2108 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2109 WPARAM wParam
, LPARAM lParam
)
2113 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2116 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2118 if (msg
== WM_CREATE
)
2120 if (!LISTBOX_Create( wnd
, NULL
)) return -1;
2121 DPRINT( "creating wnd=%04x descr=%p\n",
2122 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2125 /* Ignore all other messages before we get a WM_CREATE */
2126 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2129 DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
2130 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2133 case LB_RESETCONTENT
:
2134 LISTBOX_ResetContent( wnd
, descr
);
2138 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2139 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2141 case LB_INSERTSTRING
:
2142 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2146 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2147 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2149 case LB_DELETESTRING
:
2150 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2152 case LB_GETITEMDATA
:
2153 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2155 return descr
->items
[wParam
].data
;
2157 case LB_SETITEMDATA
:
2158 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2160 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2164 return descr
->nb_items
;
2167 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2170 if (wParam
>= descr
->nb_items
) return LB_ERR
;
2171 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2176 return descr
->selected_item
;
2179 case LB_GETTOPINDEX
:
2180 return descr
->top_item
;
2182 case LB_GETITEMHEIGHT
:
2183 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2186 case LB_SETITEMHEIGHT
:
2187 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2189 case LB_ITEMFROMPOINT
:
2191 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2192 RECT rect
= { 0, 0, descr
->width
, descr
->height
};
2193 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2194 PtInRect( &rect
, pt
) );
2198 case LB_SETCARETINDEX
:
2199 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2202 case LB_GETCARETINDEX
:
2203 return descr
->focus_item
;
2206 case LB_SETTOPINDEX
:
2207 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2210 case LB_SETCOLUMNWIDTH
:
2211 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2215 case LB_GETITEMRECT
:
2216 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2220 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2223 case LB_FINDSTRINGEXACT
:
2224 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2227 case LB_SELECTSTRING
:
2229 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2230 (LPCSTR
)lParam
, FALSE
);
2231 if (index
== LB_ERR
) return LB_ERR
;
2232 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2238 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2240 return descr
->items
[wParam
].selected
;
2243 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2247 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2248 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2250 case LB_GETSELCOUNT
:
2251 return LISTBOX_GetSelCount( wnd
, descr
);
2254 case LB_GETSELITEMS
:
2255 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2257 case LB_SELITEMRANGE
:
2258 if (LOWORD(lParam
) <= HIWORD(lParam
))
2259 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2260 HIWORD(lParam
), wParam
);
2262 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2263 LOWORD(lParam
), wParam
);
2266 case LB_SELITEMRANGEEX
:
2267 if ((INT
)lParam
>= (INT
)wParam
)
2268 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2270 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2273 case LB_GETHORIZONTALEXTENT
:
2274 return descr
->horz_extent
;
2277 case LB_SETHORIZONTALEXTENT
:
2278 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2280 case LB_GETANCHORINDEX
:
2281 return descr
->anchor_item
;
2284 case LB_SETANCHORINDEX
:
2285 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2287 descr
->anchor_item
= (INT
)wParam
;
2293 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2296 return descr
->locale
;
2299 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2302 case LB_INITSTORAGE
:
2303 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2306 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2310 case LB_SETTABSTOPS
:
2311 return LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2312 (LPINT
)lParam
, FALSE
);
2316 if (descr
->caret_on
) return LB_OKAY
;
2317 descr
->caret_on
= TRUE
;
2318 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2319 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2324 if (!descr
->caret_on
) return LB_OKAY
;
2325 descr
->caret_on
= FALSE
;
2326 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2327 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2331 return LISTBOX_Destroy( wnd
, descr
);
2334 InvalidateRect( hwnd
, NULL
, TRUE
);
2338 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2342 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2347 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2348 : BeginPaint( hwnd
, &ps
);
2349 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2350 if( !wParam
) EndPaint( hwnd
, &ps
);
2355 LISTBOX_UpdateSize( wnd
, descr
);
2362 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2363 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2367 descr
->caret_on
= TRUE
;
2368 if (descr
->focus_item
!= -1)
2369 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2370 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2374 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2375 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2376 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2380 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2383 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2385 case WM_LBUTTONDOWN
:
2386 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2387 (INT
)LOWORD(lParam
),
2388 (INT
)HIWORD(lParam
) );
2390 case WM_LBUTTONDBLCLK
:
2391 if (descr
->style
& LBS_NOTIFY
)
2392 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2396 if (GetCapture() == hwnd
)
2397 LISTBOX_HandleMouseMove( wnd
, descr
, (INT
)LOWORD(lParam
),
2398 (INT
)HIWORD(lParam
) );
2402 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2405 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2408 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2411 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2414 if (IS_OWNERDRAW(descr
))
2416 RECT rect
= { 0, 0, descr
->width
, descr
->height
};
2417 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2418 wParam
, (LPARAM
)wnd
->hwndSelf
);
2419 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2425 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2429 case WM_QUERYDROPOBJECT
:
2434 // LPDRAGINFO dragInfo = (LPDRAGINFO)lParam;
2435 // dragInfo->l = LISTBOX_GetItemFromPoint( wnd, descr, dragInfo->pt.x,
2436 // dragInfo->pt.y );
2437 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2442 if (TWEAK_WineLook
> WIN31_LOOK
)
2443 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2444 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2447 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2448 DPRINT( "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2449 hwnd
, msg
, wParam
, lParam
);
2450 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2455 /***********************************************************************
2458 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2460 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2464 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2467 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2469 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2470 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2477 /***********************************************************************
2480 * NOTE: in Windows, winproc address of the ComboLBox is the same
2481 * as that of the Listbox.
2483 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
2484 WPARAM wParam
, LPARAM lParam
)
2487 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2491 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2493 DPRINT( "[%04x]: msg %s wp %08x lp %08lx\n",
2494 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2496 if( descr
|| msg
== WM_CREATE
)
2498 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2503 #define lpcs ((LPCREATESTRUCT)lParam)
2504 DPRINT( "\tpassed parent handle = 0x%08x\n",
2505 (UINT
)lpcs
->lpCreateParams
);
2507 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2509 return LISTBOX_Create( wnd
, lphc
);
2511 case WM_LBUTTONDOWN
:
2512 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2513 (INT
)LOWORD(lParam
), (INT
)HIWORD(lParam
));
2515 /* avoid activation at all costs */
2517 case WM_MOUSEACTIVATE
:
2518 return MA_NOACTIVATE
;
2524 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2526 /* for some reason(?) Windows makes it possible to
2527 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2529 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2530 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2531 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2533 COMBO_FlipListbox( lphc
, FALSE
);
2537 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2540 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2541 return (lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2544 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2549 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2552 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2554 DPRINT("\t default on msg [%04x]\n", (UINT
)msg
);