4 * Copyright 1996 Alexandre Julliard
8 #include "wine/winuser16.h"
26 /* Items array granularity */
27 #define LB_ARRAY_GRANULARITY 16
29 /* Scrolling timeout in ms */
30 #define LB_SCROLL_TIMEOUT 50
32 /* Listbox system timer id */
38 LPSTR str
; /* Item text */
39 BOOL32 selected
; /* Is item selected? */
40 UINT32 height
; /* Item height (only for OWNERDRAWVARIABLE) */
41 DWORD data
; /* User data */
44 /* Listbox structure */
47 HANDLE32 heap
; /* Heap for this listbox */
48 HWND32 owner
; /* Owner window to send notifications to */
49 UINT32 style
; /* Window style */
50 INT32 width
; /* Window width */
51 INT32 height
; /* Window height */
52 LB_ITEMDATA
*items
; /* Array of items */
53 INT32 nb_items
; /* Number of items */
54 INT32 top_item
; /* Top visible item */
55 INT32 selected_item
; /* Selected item */
56 INT32 focus_item
; /* Item that has the focus */
57 INT32 anchor_item
; /* Anchor item for extended selection */
58 INT32 item_height
; /* Default item height */
59 INT32 page_size
; /* Items per listbox page */
60 INT32 column_width
; /* Column width for multi-column listboxes */
61 INT32 horz_extent
; /* Horizontal extent (0 if no hscroll) */
62 INT32 horz_pos
; /* Horizontal position */
63 INT32 nb_tabs
; /* Number of tabs in array */
64 INT32
*tabs
; /* Array of tabs */
65 BOOL32 caret_on
; /* Is caret on? */
66 HFONT32 font
; /* Current font */
67 LCID locale
; /* Current locale for string comparisons */
68 LPHEADCOMBO lphc
; /* ComboLBox */
72 #define IS_OWNERDRAW(descr) \
73 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
75 #define HAS_STRINGS(descr) \
76 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
78 #define SEND_NOTIFICATION(wnd,descr,code) \
79 (SendMessage32A( (descr)->owner, WM_COMMAND, \
80 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
82 /* Current timer status */
92 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
95 /***********************************************************************
98 void LISTBOX_Dump( WND
*wnd
)
102 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
104 DUMP( "Listbox:\n" );
105 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
106 wnd
->hwndSelf
, (UINT32
)descr
, descr
->heap
, descr
->nb_items
,
108 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
110 DUMP( "%4d: %-40s %d %08lx %3d\n",
111 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
116 /***********************************************************************
117 * LISTBOX_GetCurrentPageSize
119 * Return the current page size
121 static INT32
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
124 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
125 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
127 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
129 if (i
== descr
->top_item
) return 1;
130 else return i
- descr
->top_item
;
134 /***********************************************************************
135 * LISTBOX_GetMaxTopIndex
137 * Return the maximum possible index for the top of the listbox.
139 static INT32
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
143 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
145 page
= descr
->height
;
146 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
147 if ((page
-= descr
->items
[max
].height
) < 0) break;
148 if (max
< descr
->nb_items
- 1) max
++;
150 else if (descr
->style
& LBS_MULTICOLUMN
)
152 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
153 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
154 max
= (max
- page
) * descr
->page_size
;
158 max
= descr
->nb_items
- descr
->page_size
;
160 if (max
< 0) max
= 0;
165 /***********************************************************************
166 * LISTBOX_UpdateScroll
168 * Update the scrollbars. Should be called whenever the content
169 * of the listbox changes.
171 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
175 if (!(descr
->style
& WS_VSCROLL
)) return;
176 /* It is important that we check descr->style, and not wnd->dwStyle,
177 for WS_VSCROLL, as the former is exactly the one passed in
178 argument to CreateWindow.
179 In Windows (and from now on in Wine :) a listbox created
180 with such a style (no WS_SCROLL) does not update
181 the scrollbar with listbox-related data, thus letting
182 the programmer use it for his/her own purposes. */
184 if (descr
->style
& LBS_NOREDRAW
) return;
185 info
.cbSize
= sizeof(info
);
187 if (descr
->style
& LBS_MULTICOLUMN
)
190 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
191 info
.nPos
= descr
->top_item
/ descr
->page_size
;
192 info
.nPage
= descr
->width
/ descr
->column_width
;
193 if (info
.nPage
< 1) info
.nPage
= 1;
194 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
195 if (descr
->style
& LBS_DISABLENOSCROLL
)
196 info
.fMask
|= SIF_DISABLENOSCROLL
;
197 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
199 info
.fMask
= SIF_RANGE
;
200 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
205 info
.nMax
= descr
->nb_items
- 1;
206 info
.nPos
= descr
->top_item
;
207 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
208 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
209 if (descr
->style
& LBS_DISABLENOSCROLL
)
210 info
.fMask
|= SIF_DISABLENOSCROLL
;
211 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
213 if (descr
->horz_extent
)
216 info
.nMax
= descr
->horz_extent
- 1;
217 info
.nPos
= descr
->horz_pos
;
218 info
.nPage
= descr
->width
;
219 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
220 if (descr
->style
& LBS_DISABLENOSCROLL
)
221 info
.fMask
|= SIF_DISABLENOSCROLL
;
222 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
228 /***********************************************************************
231 * Set the top item of the listbox, scrolling up or down if necessary.
233 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
236 INT32 max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
237 if (index
> max
) index
= max
;
238 if (index
< 0) index
= 0;
239 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
240 if (descr
->top_item
== index
) return LB_OKAY
;
241 if (descr
->style
& LBS_MULTICOLUMN
)
243 INT32 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
244 if (scroll
&& (abs(diff
) < descr
->width
))
245 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
246 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
254 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
258 if (index
> descr
->top_item
)
260 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
261 diff
-= descr
->items
[i
].height
;
265 for (i
= index
; i
< descr
->top_item
; i
++)
266 diff
+= descr
->items
[i
].height
;
270 diff
= (descr
->top_item
- index
) * descr
->item_height
;
272 if (abs(diff
) < descr
->height
)
273 ScrollWindowEx32( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
274 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
278 if (!scroll
) InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
279 descr
->top_item
= index
;
280 LISTBOX_UpdateScroll( wnd
, descr
);
285 /***********************************************************************
288 * Update the page size. Should be called when the size of
289 * the client area or the item height changes.
291 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
295 if ((page_size
= descr
->height
/ descr
->item_height
) < 1) page_size
= 1;
296 if (page_size
== descr
->page_size
) return;
297 descr
->page_size
= page_size
;
298 if (descr
->style
& LBS_MULTICOLUMN
)
299 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
300 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
304 /***********************************************************************
307 * Update the size of the listbox. Should be called when the size of
308 * the client area changes.
310 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
314 GetClientRect32( wnd
->hwndSelf
, &rect
);
315 descr
->width
= rect
.right
- rect
.left
;
316 descr
->height
= rect
.bottom
- rect
.top
;
317 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
319 if ((descr
->height
> descr
->item_height
) &&
320 (descr
->height
% descr
->item_height
))
322 TRACE(listbox
, "[%04x]: changing height %d -> %d\n",
323 wnd
->hwndSelf
, descr
->height
,
324 descr
->height
- descr
->height
%descr
->item_height
);
325 SetWindowPos32( wnd
->hwndSelf
, 0, 0, 0,
326 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
327 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
328 (descr
->height
% descr
->item_height
),
329 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
333 TRACE(listbox
, "[%04x]: new size = %d,%d\n",
334 wnd
->hwndSelf
, descr
->width
, descr
->height
);
335 LISTBOX_UpdatePage( wnd
, descr
);
336 LISTBOX_UpdateScroll( wnd
, descr
);
340 /***********************************************************************
341 * LISTBOX_GetItemRect
343 * Get the rectangle enclosing an item, in listbox client coordinates.
344 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
346 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
349 /* Index <= 0 is legal even on empty listboxes */
350 if (index
&& (index
>= descr
->nb_items
)) return -1;
351 SetRect32( rect
, 0, 0, descr
->width
, descr
->height
);
352 if (descr
->style
& LBS_MULTICOLUMN
)
354 INT32 col
= (index
/ descr
->page_size
) -
355 (descr
->top_item
/ descr
->page_size
);
356 rect
->left
+= col
* descr
->column_width
;
357 rect
->right
= rect
->left
+ descr
->column_width
;
358 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
359 rect
->bottom
= rect
->top
+ descr
->item_height
;
361 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
364 rect
->right
+= descr
->horz_pos
;
365 if ((index
>= 0) && (index
< descr
->nb_items
))
367 if (index
< descr
->top_item
)
369 for (i
= descr
->top_item
-1; i
>= index
; i
--)
370 rect
->top
-= descr
->items
[i
].height
;
374 for (i
= descr
->top_item
; i
< index
; i
++)
375 rect
->top
+= descr
->items
[i
].height
;
377 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
383 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
384 rect
->bottom
= rect
->top
+ descr
->item_height
;
385 rect
->right
+= descr
->horz_pos
;
388 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
389 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
393 /***********************************************************************
394 * LISTBOX_GetItemFromPoint
396 * Return the item nearest from point (x,y) (in client coordinates).
398 static INT32
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
401 INT32 index
= descr
->top_item
;
403 if (!descr
->nb_items
) return -1; /* No items */
404 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
409 while (index
< descr
->nb_items
)
411 if ((pos
+= descr
->items
[index
].height
) > y
) break;
420 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
424 else if (descr
->style
& LBS_MULTICOLUMN
)
426 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
427 if (y
>= 0) index
+= y
/ descr
->item_height
;
428 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
429 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
433 index
+= (y
/ descr
->item_height
);
435 if (index
< 0) return 0;
436 if (index
>= descr
->nb_items
) return -1;
441 /***********************************************************************
446 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
,
447 const RECT32
*rect
, INT32 index
, UINT32 action
)
449 LB_ITEMDATA
*item
= NULL
;
450 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
452 if (IS_OWNERDRAW(descr
))
454 DRAWITEMSTRUCT32 dis
;
455 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
459 if (action
== ODA_FOCUS
)
460 DrawFocusRect32( hdc
, rect
);
462 FIXME(listbox
,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
465 dis
.CtlType
= ODT_LISTBOX
;
467 dis
.hwndItem
= wnd
->hwndSelf
;
468 dis
.itemAction
= action
;
472 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
473 if ((descr
->focus_item
== index
) &&
475 (GetFocus32() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
476 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
477 dis
.itemData
= item
? item
->data
: 0;
479 TRACE(listbox
, "[%04x]: drawitem %d (%s) action=%02x "
480 "state=%02x rect=%d,%d-%d,%d\n",
481 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
482 dis
.itemState
, rect
->left
, rect
->top
,
483 rect
->right
, rect
->bottom
);
484 SendMessage32A(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
488 COLORREF oldText
= 0, oldBk
= 0;
490 if (action
== ODA_FOCUS
)
492 DrawFocusRect32( hdc
, rect
);
495 if (item
&& item
->selected
)
497 oldBk
= SetBkColor32( hdc
, GetSysColor32( COLOR_HIGHLIGHT
) );
498 oldText
= SetTextColor32( hdc
, GetSysColor32(COLOR_HIGHLIGHTTEXT
));
501 TRACE(listbox
, "[%04x]: painting %d (%s) action=%02x "
502 "rect=%d,%d-%d,%d\n",
503 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
504 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
506 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
507 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
508 else if (!(descr
->style
& LBS_USETABSTOPS
))
509 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
510 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
511 strlen(item
->str
), NULL
);
514 /* Output empty string to paint background in the full width. */
515 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
516 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
517 TabbedTextOut32A( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
518 item
->str
, strlen(item
->str
),
519 descr
->nb_tabs
, descr
->tabs
, 0);
521 if (item
&& item
->selected
)
523 SetBkColor32( hdc
, oldBk
);
524 SetTextColor32( hdc
, oldText
);
526 if ((descr
->focus_item
== index
) &&
528 (GetFocus32() == wnd
->hwndSelf
)) DrawFocusRect32( hdc
, rect
);
533 /***********************************************************************
536 * Change the redraw flag.
538 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL32 on
)
542 if (!(descr
->style
& LBS_NOREDRAW
)) return;
543 descr
->style
&= ~LBS_NOREDRAW
;
544 LISTBOX_UpdateScroll( wnd
, descr
);
546 else descr
->style
|= LBS_NOREDRAW
;
550 /***********************************************************************
551 * LISTBOX_RepaintItem
553 * Repaint a single item synchronously.
555 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
561 HBRUSH32 hbrush
, oldBrush
= 0;
563 if (descr
->style
& LBS_NOREDRAW
) return;
564 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
565 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
566 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
567 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
568 hdc
, (LPARAM
)wnd
->hwndSelf
);
569 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
570 if (wnd
->dwStyle
& WS_DISABLED
)
571 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
572 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
573 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
574 if (oldFont
) SelectObject32( hdc
, oldFont
);
575 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
576 ReleaseDC32( wnd
->hwndSelf
, hdc
);
580 /***********************************************************************
581 * LISTBOX_InitStorage
583 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT32 nb_items
,
588 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
589 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
591 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
592 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
593 nb_items
* sizeof(LB_ITEMDATA
) )))
595 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
603 /***********************************************************************
604 * LISTBOX_SetTabStops
606 static BOOL32
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT32 count
,
607 LPINT32 tabs
, BOOL32 short_ints
)
609 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
610 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
611 if (!(descr
->nb_tabs
= count
))
616 /* FIXME: count = 1 */
617 if (!(descr
->tabs
= (INT32
*)HeapAlloc( descr
->heap
, 0,
618 descr
->nb_tabs
* sizeof(INT32
) )))
623 LPINT16 p
= (LPINT16
)tabs
;
624 dbg_decl_str(listbox
, 256);
626 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
627 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
628 if(TRACE_ON(listbox
))
629 dsprintf(listbox
, "%hd ", descr
->tabs
[i
]);
631 TRACE(listbox
, "[%04x]: settabstops %s\n",
632 wnd
->hwndSelf
, dbg_str(listbox
));
634 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT32
) );
635 /* FIXME: repaint the window? */
640 /***********************************************************************
643 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
646 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
647 if (HAS_STRINGS(descr
))
650 return strlen(descr
->items
[index
].str
);
651 lstrcpy32A( buffer
, descr
->items
[index
].str
);
652 return strlen(buffer
);
655 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
656 return sizeof(DWORD
);
661 /***********************************************************************
662 * LISTBOX_FindStringPos
664 * Find the nearest string located before a given string in sort order.
665 * If 'exact' is TRUE, return an error if we don't get an exact match.
667 static INT32
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
670 INT32 index
, min
, max
, res
= -1;
672 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
674 max
= descr
->nb_items
;
677 index
= (min
+ max
) / 2;
678 if (HAS_STRINGS(descr
))
679 res
= lstrcmpi32A( descr
->items
[index
].str
, str
);
682 COMPAREITEMSTRUCT32 cis
;
683 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
685 cis
.CtlType
= ODT_LISTBOX
;
687 cis
.hwndItem
= wnd
->hwndSelf
;
689 cis
.itemData1
= descr
->items
[index
].data
;
691 cis
.itemData2
= (DWORD
)str
;
692 cis
.dwLocaleId
= descr
->locale
;
693 res
= SendMessage32A( descr
->owner
, WM_COMPAREITEM
,
696 if (!res
) return index
;
697 if (res
> 0) max
= index
;
698 else min
= index
+ 1;
700 return exact
? -1 : max
;
704 /***********************************************************************
705 * LISTBOX_FindFileStrPos
707 * Find the nearest string located before a given string in directory
708 * sort order (i.e. first files, then directories, then drives).
710 static INT32
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
712 INT32 min
, max
, res
= -1;
714 if (!HAS_STRINGS(descr
))
715 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
717 max
= descr
->nb_items
;
720 INT32 index
= (min
+ max
) / 2;
721 const char *p
= descr
->items
[index
].str
;
722 if (*p
== '[') /* drive or directory */
724 if (*str
!= '[') res
= -1;
725 else if (p
[1] == '-') /* drive */
727 if (str
[1] == '-') res
= str
[2] - p
[2];
732 if (str
[1] == '-') res
= 1;
733 else res
= lstrcmpi32A( str
, p
);
738 if (*str
== '[') res
= 1;
739 else res
= lstrcmpi32A( str
, p
);
741 if (!res
) return index
;
742 if (res
< 0) max
= index
;
743 else min
= index
+ 1;
749 /***********************************************************************
752 * Find the item beginning with a given string.
754 static INT32
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT32 start
,
755 LPCSTR str
, BOOL32 exact
)
760 if (start
>= descr
->nb_items
) start
= -1;
761 item
= descr
->items
+ start
+ 1;
762 if (HAS_STRINGS(descr
))
764 if (!str
) return LB_ERR
;
767 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
768 if (!lstrcmpi32A( str
, item
->str
)) return i
;
769 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
770 if (!lstrcmpi32A( str
, item
->str
)) return i
;
774 /* Special case for drives and directories: ignore prefix */
775 #define CHECK_DRIVE(item) \
776 if ((item)->str[0] == '[') \
778 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
779 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
783 INT32 len
= strlen(str
);
784 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
786 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
789 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
791 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
799 if (exact
&& (descr
->style
& LBS_SORT
))
800 /* If sorted, use a WM_COMPAREITEM binary search */
801 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
803 /* Otherwise use a linear search */
804 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
805 if (item
->data
== (DWORD
)str
) return i
;
806 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
807 if (item
->data
== (DWORD
)str
) return i
;
813 /***********************************************************************
814 * LISTBOX_GetSelCount
816 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
819 LB_ITEMDATA
*item
= descr
->items
;
821 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
822 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
823 if (item
->selected
) count
++;
828 /***********************************************************************
829 * LISTBOX_GetSelItems16
831 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
835 LB_ITEMDATA
*item
= descr
->items
;
837 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
838 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
839 if (item
->selected
) array
[count
++] = (INT16
)i
;
844 /***********************************************************************
845 * LISTBOX_GetSelItems32
847 static LRESULT
LISTBOX_GetSelItems32( WND
*wnd
, LB_DESCR
*descr
, INT32 max
,
851 LB_ITEMDATA
*item
= descr
->items
;
853 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
854 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
855 if (item
->selected
) array
[count
++] = i
;
860 /***********************************************************************
863 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
)
865 INT32 i
, col_pos
= descr
->page_size
- 1;
868 HBRUSH32 hbrush
, oldBrush
= 0;
870 SetRect32( &rect
, 0, 0, descr
->width
, descr
->height
);
871 if (descr
->style
& LBS_NOREDRAW
) return 0;
872 if (descr
->style
& LBS_MULTICOLUMN
)
873 rect
.right
= rect
.left
+ descr
->column_width
;
874 else if (descr
->horz_pos
)
876 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
877 rect
.right
+= descr
->horz_pos
;
880 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
881 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
882 hdc
, (LPARAM
)wnd
->hwndSelf
);
883 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
884 if (wnd
->dwStyle
& WS_DISABLED
)
885 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
887 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
888 (GetFocus32() == wnd
->hwndSelf
))
890 /* Special case for empty listbox: paint focus rect */
891 rect
.bottom
= rect
.top
+ descr
->item_height
;
892 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
894 rect
.top
= rect
.bottom
;
897 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
899 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
900 rect
.bottom
= rect
.top
+ descr
->item_height
;
902 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
904 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
905 rect
.top
= rect
.bottom
;
907 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
909 if (!IS_OWNERDRAW(descr
))
911 /* Clear the bottom of the column */
912 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
913 if (rect
.top
< descr
->height
)
915 rect
.bottom
= descr
->height
;
916 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
917 &rect
, NULL
, 0, NULL
);
921 /* Go to the next column */
922 rect
.left
+= descr
->column_width
;
923 rect
.right
+= descr
->column_width
;
925 col_pos
= descr
->page_size
- 1;
930 if (rect
.top
>= descr
->height
) break;
934 if (!IS_OWNERDRAW(descr
))
936 /* Clear the remainder of the client area */
937 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
938 if (rect
.top
< descr
->height
)
940 rect
.bottom
= descr
->height
;
941 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
942 &rect
, NULL
, 0, NULL
);
944 if (rect
.right
< descr
->width
)
946 rect
.left
= rect
.right
;
947 rect
.right
= descr
->width
;
949 rect
.bottom
= descr
->height
;
950 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
951 &rect
, NULL
, 0, NULL
);
954 if (oldFont
) SelectObject32( hdc
, oldFont
);
955 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
960 /***********************************************************************
961 * LISTBOX_InvalidateItems
963 * Invalidate all items from a given item. If the specified item is not
964 * visible, nothing happens.
966 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
970 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
972 rect
.bottom
= descr
->height
;
973 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
974 if (descr
->style
& LBS_MULTICOLUMN
)
976 /* Repaint the other columns */
977 rect
.left
= rect
.right
;
978 rect
.right
= descr
->width
;
980 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
986 /***********************************************************************
987 * LISTBOX_GetItemHeight
989 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
991 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
993 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
994 return descr
->items
[index
].height
;
996 else return descr
->item_height
;
1000 /***********************************************************************
1001 * LISTBOX_SetItemHeight
1003 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1006 if (!height
) height
= 1;
1008 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1010 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1011 TRACE(listbox
, "[%04x]: item %d height = %d\n",
1012 wnd
->hwndSelf
, index
, height
);
1013 descr
->items
[index
].height
= height
;
1014 LISTBOX_UpdateScroll( wnd
, descr
);
1015 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1017 else if (height
!= descr
->item_height
)
1019 TRACE(listbox
, "[%04x]: new height = %d\n",
1020 wnd
->hwndSelf
, height
);
1021 descr
->item_height
= height
;
1022 LISTBOX_UpdatePage( wnd
, descr
);
1023 LISTBOX_UpdateScroll( wnd
, descr
);
1024 InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
1030 /***********************************************************************
1031 * LISTBOX_SetHorizontalPos
1033 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT32 pos
)
1037 if (pos
> descr
->horz_extent
- descr
->width
)
1038 pos
= descr
->horz_extent
- descr
->width
;
1039 if (pos
< 0) pos
= 0;
1040 if (!(diff
= descr
->horz_pos
- pos
)) return;
1041 TRACE(listbox
, "[%04x]: new horz pos = %d\n",
1042 wnd
->hwndSelf
, pos
);
1043 descr
->horz_pos
= pos
;
1044 LISTBOX_UpdateScroll( wnd
, descr
);
1045 if (abs(diff
) < descr
->width
)
1046 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1047 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1049 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1053 /***********************************************************************
1054 * LISTBOX_SetHorizontalExtent
1056 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1059 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1061 if (extent
<= 0) extent
= 1;
1062 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1063 TRACE(listbox
, "[%04x]: new horz extent = %d\n",
1064 wnd
->hwndSelf
, extent
);
1065 descr
->horz_extent
= extent
;
1066 if (descr
->horz_pos
> extent
- descr
->width
)
1067 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1069 LISTBOX_UpdateScroll( wnd
, descr
);
1074 /***********************************************************************
1075 * LISTBOX_SetColumnWidth
1077 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT32 width
)
1079 width
+= 2; /* For left and right margin */
1080 if (width
== descr
->column_width
) return LB_OKAY
;
1081 TRACE(listbox
, "[%04x]: new column width = %d\n",
1082 wnd
->hwndSelf
, width
);
1083 descr
->column_width
= width
;
1084 LISTBOX_UpdatePage( wnd
, descr
);
1089 /***********************************************************************
1092 * Returns the item height.
1094 static INT32
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT32 font
)
1097 HFONT32 oldFont
= 0;
1102 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1104 ERR(listbox
, "unable to get DC.\n" );
1107 if (font
) oldFont
= SelectObject32( hdc
, font
);
1108 GetTextMetrics32A( hdc
, &tm
);
1109 if (oldFont
) SelectObject32( hdc
, oldFont
);
1110 ReleaseDC32( wnd
->hwndSelf
, hdc
);
1111 if (!IS_OWNERDRAW(descr
))
1112 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1113 return tm
.tmHeight
;
1117 /***********************************************************************
1118 * LISTBOX_MakeItemVisible
1120 * Make sure that a given item is partially or fully visible.
1122 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1127 if (index
<= descr
->top_item
) top
= index
;
1128 else if (descr
->style
& LBS_MULTICOLUMN
)
1130 INT32 cols
= descr
->width
;
1131 if (!fully
) cols
+= descr
->column_width
- 1;
1132 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1134 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1135 top
= index
- descr
->page_size
* (cols
- 1);
1137 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1139 INT32 height
= fully
? descr
->items
[index
].height
: 1;
1140 for (top
= index
; top
> descr
->top_item
; top
--)
1141 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1145 if (index
< descr
->top_item
+ descr
->page_size
) return;
1146 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1147 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1148 top
= index
- descr
->page_size
+ 1;
1150 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1154 /***********************************************************************
1155 * LISTBOX_SelectItemRange
1157 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1159 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT32 first
,
1160 INT32 last
, BOOL32 on
)
1164 /* A few sanity checks */
1166 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1167 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1168 if (last
== -1) last
= descr
->nb_items
- 1;
1169 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1170 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1171 /* selected_item reflects last selected/unselected item on multiple sel */
1172 descr
->selected_item
= last
;
1174 if (on
) /* Turn selection on */
1176 for (i
= first
; i
<= last
; i
++)
1178 if (descr
->items
[i
].selected
) continue;
1179 descr
->items
[i
].selected
= TRUE
;
1180 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1183 else /* Turn selection off */
1185 for (i
= first
; i
<= last
; i
++)
1187 if (!descr
->items
[i
].selected
) continue;
1188 descr
->items
[i
].selected
= FALSE
;
1189 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1196 /***********************************************************************
1197 * LISTBOX_SetCaretIndex
1200 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1203 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1204 BOOL32 fully_visible
)
1206 INT32 oldfocus
= descr
->focus_item
;
1208 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1209 if (index
== oldfocus
) return LB_OKAY
;
1210 descr
->focus_item
= index
;
1211 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1212 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1214 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1215 if (descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1216 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1222 /***********************************************************************
1223 * LISTBOX_SetSelection
1225 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1226 BOOL32 on
, BOOL32 send_notify
)
1228 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1229 if (descr
->style
& LBS_MULTIPLESEL
)
1231 if (index
== -1) /* Select all items */
1232 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1233 else /* Only one item */
1234 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1238 INT32 oldsel
= descr
->selected_item
;
1239 if (index
== oldsel
) return LB_OKAY
;
1240 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1241 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1242 descr
->selected_item
= index
;
1243 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1244 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1245 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1246 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1248 if( descr
->lphc
) /* set selection change flag for parent combo */
1249 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1255 /***********************************************************************
1258 * Change the caret position and extend the selection to the new caret.
1260 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1261 BOOL32 fully_visible
)
1263 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1264 if (descr
->style
& LBS_EXTENDEDSEL
)
1266 if (descr
->anchor_item
!= -1)
1268 INT32 first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1269 INT32 last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1271 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1272 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1273 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1276 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1278 /* Set selection to new caret item */
1279 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1284 /***********************************************************************
1285 * LISTBOX_InsertItem
1287 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1288 LPSTR str
, DWORD data
)
1293 if (index
== -1) index
= descr
->nb_items
;
1294 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1295 if (!descr
->items
) max_items
= 0;
1296 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1297 if (descr
->nb_items
== max_items
)
1299 /* We need to grow the array */
1300 max_items
+= LB_ARRAY_GRANULARITY
;
1301 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1302 max_items
* sizeof(LB_ITEMDATA
) )))
1304 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1307 descr
->items
= item
;
1310 /* Insert the item structure */
1312 item
= &descr
->items
[index
];
1313 if (index
< descr
->nb_items
)
1314 RtlMoveMemory( item
+ 1, item
,
1315 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1319 item
->selected
= FALSE
;
1322 /* Get item height */
1324 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1326 MEASUREITEMSTRUCT32 mis
;
1327 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1329 mis
.CtlType
= ODT_LISTBOX
;
1332 mis
.itemData
= descr
->items
[index
].data
;
1333 mis
.itemHeight
= descr
->item_height
;
1334 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1335 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1336 TRACE(listbox
, "[%04x]: measure item %d (%s) = %d\n",
1337 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1340 /* Repaint the items */
1342 LISTBOX_UpdateScroll( wnd
, descr
);
1343 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1345 /* Move selection and focused item */
1347 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1348 if (index
<= descr
->focus_item
)
1350 descr
->focus_item
++;
1351 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1354 /* If listbox was empty, set focus to the first item */
1356 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1361 /***********************************************************************
1362 * LISTBOX_InsertString
1364 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1367 LPSTR new_str
= NULL
;
1371 if (HAS_STRINGS(descr
))
1373 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1375 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1379 else data
= (DWORD
)str
;
1381 if (index
== -1) index
= descr
->nb_items
;
1382 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1384 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1388 TRACE(listbox
, "[%04x]: added item %d '%s'\n",
1389 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1394 /***********************************************************************
1395 * LISTBOX_DeleteItem
1397 * Delete the content of an item. 'index' must be a valid index.
1399 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1401 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1402 * while Win95 sends it for all items with user data.
1403 * It's probably better to send it too often than not
1404 * often enough, so this is what we do here.
1406 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1408 DELETEITEMSTRUCT32 dis
;
1409 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1411 dis
.CtlType
= ODT_LISTBOX
;
1414 dis
.hwndItem
= wnd
->hwndSelf
;
1415 dis
.itemData
= descr
->items
[index
].data
;
1416 SendMessage32A( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1418 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1419 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1423 /***********************************************************************
1424 * LISTBOX_RemoveItem
1426 * Remove an item from the listbox and delete its content.
1428 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1433 if (index
== -1) index
= descr
->nb_items
- 1;
1434 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1435 LISTBOX_DeleteItem( wnd
, descr
, index
);
1437 /* Remove the item */
1439 item
= &descr
->items
[index
];
1440 if (index
< descr
->nb_items
-1)
1441 RtlMoveMemory( item
, item
+ 1,
1442 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1444 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1446 /* Shrink the item array if possible */
1448 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1449 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1451 max_items
-= LB_ARRAY_GRANULARITY
;
1452 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1453 max_items
* sizeof(LB_ITEMDATA
) );
1454 if (item
) descr
->items
= item
;
1457 /* Repaint the items */
1459 LISTBOX_UpdateScroll( wnd
, descr
);
1460 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1462 /* Move selection and focused item */
1464 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1465 if (index
<= descr
->focus_item
)
1467 descr
->focus_item
--;
1468 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1474 /***********************************************************************
1475 * LISTBOX_ResetContent
1477 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1481 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1482 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1483 descr
->nb_items
= 0;
1484 descr
->top_item
= 0;
1485 descr
->selected_item
= -1;
1486 descr
->focus_item
= 0;
1487 descr
->anchor_item
= -1;
1488 descr
->items
= NULL
;
1489 LISTBOX_UpdateScroll( wnd
, descr
);
1490 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1494 /***********************************************************************
1497 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT32 count
)
1501 if (HAS_STRINGS(descr
)) return LB_ERR
;
1502 /* FIXME: this is far from optimal... */
1503 if (count
> descr
->nb_items
)
1505 while (count
> descr
->nb_items
)
1506 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1509 else if (count
< descr
->nb_items
)
1511 while (count
< descr
->nb_items
)
1512 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1519 /***********************************************************************
1522 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT32 attrib
,
1523 LPCSTR filespec
, BOOL32 long_names
)
1526 LRESULT ret
= LB_OKAY
;
1527 WIN32_FIND_DATA32A entry
;
1530 if ((handle
= FindFirstFile32A(filespec
,&entry
)) == INVALID_HANDLE_VALUE32
)
1532 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1539 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1541 if (!(attrib
& DDL_DIRECTORY
) ||
1542 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1543 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1544 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1546 else /* not a directory */
1548 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1549 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1551 if ((attrib
& DDL_EXCLUSIVE
) &&
1552 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1555 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1556 else strcpy( buffer
, entry
.cAlternateFileName
);
1558 if (!long_names
) CharLower32A( buffer
);
1559 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1560 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1562 } while (FindNextFile32A( handle
, &entry
));
1563 FindClose32( handle
);
1566 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1568 char buffer
[] = "[-a-]";
1570 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1572 if (!DRIVE_IsValid(drive
)) continue;
1573 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1581 /***********************************************************************
1582 * LISTBOX_HandleVScroll
1584 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1585 WPARAM32 wParam
, LPARAM lParam
)
1589 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1590 switch(LOWORD(wParam
))
1593 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1596 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1599 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1600 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1603 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1604 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1606 case SB_THUMBPOSITION
:
1607 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1610 info
.cbSize
= sizeof(info
);
1611 info
.fMask
= SIF_TRACKPOS
;
1612 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1613 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1616 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1619 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1626 /***********************************************************************
1627 * LISTBOX_HandleHScroll
1629 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1630 WPARAM32 wParam
, LPARAM lParam
)
1635 if (descr
->style
& LBS_MULTICOLUMN
)
1637 switch(LOWORD(wParam
))
1640 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1644 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1648 page
= descr
->width
/ descr
->column_width
;
1649 if (page
< 1) page
= 1;
1650 LISTBOX_SetTopItem( wnd
, descr
,
1651 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1654 page
= descr
->width
/ descr
->column_width
;
1655 if (page
< 1) page
= 1;
1656 LISTBOX_SetTopItem( wnd
, descr
,
1657 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1659 case SB_THUMBPOSITION
:
1660 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1664 info
.cbSize
= sizeof(info
);
1665 info
.fMask
= SIF_TRACKPOS
;
1666 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1667 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1671 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1674 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1678 else if (descr
->horz_extent
)
1680 switch(LOWORD(wParam
))
1683 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1686 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1689 LISTBOX_SetHorizontalPos( wnd
, descr
,
1690 descr
->horz_pos
- descr
->width
);
1693 LISTBOX_SetHorizontalPos( wnd
, descr
,
1694 descr
->horz_pos
+ descr
->width
);
1696 case SB_THUMBPOSITION
:
1697 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1700 info
.cbSize
= sizeof(info
);
1701 info
.fMask
= SIF_TRACKPOS
;
1702 GetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
);
1703 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1706 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1709 LISTBOX_SetHorizontalPos( wnd
, descr
,
1710 descr
->horz_extent
- descr
->width
);
1718 /***********************************************************************
1719 * LISTBOX_HandleLButtonDown
1721 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1722 WPARAM32 wParam
, INT32 x
, INT32 y
)
1724 INT32 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1725 TRACE(listbox
, "[%04x]: lbuttondown %d,%d item %d\n",
1726 wnd
->hwndSelf
, x
, y
, index
);
1727 if (!descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
)) return 0;
1730 if (descr
->style
& LBS_EXTENDEDSEL
)
1732 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1733 if (wParam
& MK_CONTROL
)
1735 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1736 LISTBOX_SetSelection( wnd
, descr
, index
,
1737 !descr
->items
[index
].selected
, FALSE
);
1739 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1743 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1744 LISTBOX_SetSelection( wnd
, descr
, index
,
1745 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1746 !descr
->items
[index
].selected
), FALSE
);
1750 if( !descr
->lphc
) SetFocus32( wnd
->hwndSelf
);
1751 else SetFocus32( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1752 : descr
->lphc
->self
->hwndSelf
) ;
1754 SetCapture32( wnd
->hwndSelf
);
1755 if (index
!= -1 && !descr
->lphc
)
1757 if (descr
->style
& LBS_NOTIFY
)
1758 SendMessage32A( descr
->owner
, WM_LBTRACKPOINT
, index
,
1759 MAKELPARAM( x
, y
) );
1760 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1762 POINT32 pt
= { x
, y
};
1763 if (DragDetect32( wnd
->hwndSelf
, pt
))
1764 SendMessage32A( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1771 /***********************************************************************
1772 * LISTBOX_HandleLButtonUp
1774 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1776 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1777 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1778 LISTBOX_Timer
= LB_TIMER_NONE
;
1779 if (GetCapture32() == wnd
->hwndSelf
)
1782 if (descr
->style
& LBS_NOTIFY
)
1783 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1789 /***********************************************************************
1790 * LISTBOX_HandleTimer
1792 * Handle scrolling upon a timer event.
1793 * Return TRUE if scrolling should continue.
1795 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1796 INT32 index
, TIMER_DIRECTION dir
)
1801 if (descr
->top_item
) index
= descr
->top_item
- 1;
1805 if (descr
->top_item
) index
-= descr
->page_size
;
1808 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1809 if (index
== descr
->focus_item
) index
++;
1810 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1812 case LB_TIMER_RIGHT
:
1813 if (index
+ descr
->page_size
< descr
->nb_items
)
1814 index
+= descr
->page_size
;
1819 if (index
== descr
->focus_item
) return FALSE
;
1820 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1825 /***********************************************************************
1826 * LISTBOX_HandleSystemTimer
1828 * WM_SYSTIMER handler.
1830 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1832 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1834 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1835 LISTBOX_Timer
= LB_TIMER_NONE
;
1841 /***********************************************************************
1842 * LISTBOX_HandleMouseMove
1844 * WM_MOUSEMOVE handler.
1846 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1850 TIMER_DIRECTION dir
;
1852 if (descr
->style
& LBS_MULTICOLUMN
)
1855 else if (y
>= descr
->item_height
* descr
->page_size
)
1856 y
= descr
->item_height
* descr
->page_size
- 1;
1860 dir
= LB_TIMER_LEFT
;
1863 else if (x
>= descr
->width
)
1865 dir
= LB_TIMER_RIGHT
;
1866 x
= descr
->width
- 1;
1868 else dir
= LB_TIMER_NONE
; /* inside */
1872 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1873 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1874 else dir
= LB_TIMER_NONE
; /* inside */
1877 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1878 if (index
== -1) index
= descr
->focus_item
;
1879 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1881 /* Start/stop the system timer */
1883 if (dir
!= LB_TIMER_NONE
)
1884 SetSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1885 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1886 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1887 LISTBOX_Timer
= dir
;
1891 /***********************************************************************
1892 * LISTBOX_HandleKeyDown
1894 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM32 wParam
)
1897 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1899 caret
= SendMessage32A( descr
->owner
, WM_VKEYTOITEM
,
1900 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1902 if (caret
== -2) return 0;
1904 if (caret
== -1) switch(wParam
)
1907 if (descr
->style
& LBS_MULTICOLUMN
)
1909 if (descr
->focus_item
>= descr
->page_size
)
1910 caret
= descr
->focus_item
- descr
->page_size
;
1915 caret
= descr
->focus_item
- 1;
1916 if (caret
< 0) caret
= 0;
1919 if (descr
->style
& LBS_MULTICOLUMN
)
1921 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1922 caret
= descr
->focus_item
+ descr
->page_size
;
1927 caret
= descr
->focus_item
+ 1;
1928 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1931 if (descr
->style
& LBS_MULTICOLUMN
)
1933 INT32 page
= descr
->width
/ descr
->column_width
;
1934 if (page
< 1) page
= 1;
1935 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1937 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1938 if (caret
< 0) caret
= 0;
1941 if (descr
->style
& LBS_MULTICOLUMN
)
1943 INT32 page
= descr
->width
/ descr
->column_width
;
1944 if (page
< 1) page
= 1;
1945 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1947 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1948 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1954 caret
= descr
->nb_items
- 1;
1957 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1958 else if (descr
->style
& LBS_MULTIPLESEL
)
1960 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1961 !descr
->items
[descr
->focus_item
].selected
,
1962 (descr
->style
& LBS_NOTIFY
) != 0 );
1964 else if (descr
->selected_item
== -1)
1966 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1967 (descr
->style
& LBS_NOTIFY
) != 0 );
1973 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1974 !(GetKeyState32( VK_SHIFT
) & 0x8000))
1975 descr
->anchor_item
= caret
;
1976 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1977 if (descr
->style
& LBS_NOTIFY
)
1979 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1981 /* make sure that combo parent doesn't hide us */
1982 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1984 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1991 /***********************************************************************
1992 * LISTBOX_HandleChar
1994 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
1998 char str
[2] = { wParam
& 0xff, '\0' };
2000 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2002 caret
= SendMessage32A( descr
->owner
, WM_CHARTOITEM
,
2003 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2005 if (caret
== -2) return 0;
2008 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2011 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2012 if (descr
->style
& LBS_NOTIFY
)
2013 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2019 /***********************************************************************
2022 static BOOL32
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2025 MEASUREITEMSTRUCT32 mis
;
2028 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2030 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2032 HeapFree( GetProcessHeap(), 0, descr
);
2035 GetClientRect32( wnd
->hwndSelf
, &rect
);
2036 descr
->owner
= GetParent32( wnd
->hwndSelf
);
2037 descr
->style
= wnd
->dwStyle
;
2038 descr
->width
= rect
.right
- rect
.left
;
2039 descr
->height
= rect
.bottom
- rect
.top
;
2040 descr
->items
= NULL
;
2041 descr
->nb_items
= 0;
2042 descr
->top_item
= 0;
2043 descr
->selected_item
= -1;
2044 descr
->focus_item
= 0;
2045 descr
->anchor_item
= -1;
2046 descr
->item_height
= 1;
2047 descr
->page_size
= 1;
2048 descr
->column_width
= 150;
2049 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2050 descr
->horz_pos
= 0;
2053 descr
->caret_on
= TRUE
;
2055 descr
->locale
= 0; /* FIXME */
2060 TRACE(combo
,"[%04x]: resetting owner %04x -> %04x\n",
2061 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2062 descr
->owner
= lphc
->self
->hwndSelf
;
2065 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2067 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2069 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2070 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2071 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2072 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2074 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2076 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2078 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2079 descr
->item_height
= lphc
->RectButton
.bottom
- lphc
->RectButton
.top
- 6;
2083 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2085 mis
.CtlType
= ODT_LISTBOX
;
2090 mis
.itemHeight
= descr
->item_height
;
2091 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2092 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2100 /***********************************************************************
2103 static BOOL32
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2105 LISTBOX_ResetContent( wnd
, descr
);
2106 HeapDestroy( descr
->heap
);
2107 HeapFree( GetProcessHeap(), 0, descr
);
2113 /***********************************************************************
2116 LRESULT WINAPI
ListBoxWndProc( HWND32 hwnd
, UINT32 msg
,
2117 WPARAM32 wParam
, LPARAM lParam
)
2121 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2124 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2126 if (msg
== WM_CREATE
)
2128 if (!LISTBOX_Create( wnd
, NULL
)) return -1;
2129 TRACE(listbox
, "creating wnd=%04x descr=%p\n",
2130 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2133 /* Ignore all other messages before we get a WM_CREATE */
2134 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2137 TRACE(listbox
, "[%04x]: msg %s wp %08x lp %08lx\n",
2138 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2141 case LB_RESETCONTENT16
:
2142 case LB_RESETCONTENT32
:
2143 LISTBOX_ResetContent( wnd
, descr
);
2146 case LB_ADDSTRING16
:
2147 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2149 case LB_ADDSTRING32
:
2150 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2151 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2153 case LB_INSERTSTRING16
:
2154 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2155 wParam
= (INT32
)(INT16
)wParam
;
2157 case LB_INSERTSTRING32
:
2158 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2161 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2164 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2165 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2167 case LB_DELETESTRING16
:
2168 case LB_DELETESTRING32
:
2169 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2171 case LB_GETITEMDATA16
:
2172 case LB_GETITEMDATA32
:
2173 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2175 return descr
->items
[wParam
].data
;
2177 case LB_SETITEMDATA16
:
2178 case LB_SETITEMDATA32
:
2179 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2181 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2186 return descr
->nb_items
;
2189 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2192 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2194 case LB_GETTEXTLEN16
:
2196 case LB_GETTEXTLEN32
:
2197 if (wParam
>= descr
->nb_items
) return LB_ERR
;
2198 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2201 case LB_GETCURSEL16
:
2202 case LB_GETCURSEL32
:
2203 return descr
->selected_item
;
2205 case LB_GETTOPINDEX16
:
2206 case LB_GETTOPINDEX32
:
2207 return descr
->top_item
;
2209 case LB_GETITEMHEIGHT16
:
2210 case LB_GETITEMHEIGHT32
:
2211 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2213 case LB_SETITEMHEIGHT16
:
2214 lParam
= LOWORD(lParam
);
2216 case LB_SETITEMHEIGHT32
:
2217 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2219 case LB_ITEMFROMPOINT32
:
2221 POINT32 pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2222 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2223 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2224 PtInRect32( &rect
, pt
) );
2227 case LB_SETCARETINDEX16
:
2228 case LB_SETCARETINDEX32
:
2229 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2231 case LB_GETCARETINDEX16
:
2232 case LB_GETCARETINDEX32
:
2233 return descr
->focus_item
;
2235 case LB_SETTOPINDEX16
:
2236 case LB_SETTOPINDEX32
:
2237 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2239 case LB_SETCOLUMNWIDTH16
:
2240 case LB_SETCOLUMNWIDTH32
:
2241 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2243 case LB_GETITEMRECT16
:
2246 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2247 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2251 case LB_GETITEMRECT32
:
2252 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT32
*)lParam
);
2254 case LB_FINDSTRING16
:
2255 wParam
= (INT32
)(INT16
)wParam
;
2256 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2258 case LB_FINDSTRING32
:
2259 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2261 case LB_FINDSTRINGEXACT16
:
2262 wParam
= (INT32
)(INT16
)wParam
;
2263 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2265 case LB_FINDSTRINGEXACT32
:
2266 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2268 case LB_SELECTSTRING16
:
2269 wParam
= (INT32
)(INT16
)wParam
;
2270 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2272 case LB_SELECTSTRING32
:
2274 INT32 index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2275 (LPCSTR
)lParam
, FALSE
);
2276 if (index
== LB_ERR
) return LB_ERR
;
2277 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2282 wParam
= (INT32
)(INT16
)wParam
;
2285 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2287 return descr
->items
[wParam
].selected
;
2290 lParam
= (INT32
)(INT16
)lParam
;
2293 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2295 case LB_SETCURSEL16
:
2296 wParam
= (INT32
)(INT16
)wParam
;
2298 case LB_SETCURSEL32
:
2299 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2300 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2302 case LB_GETSELCOUNT16
:
2303 case LB_GETSELCOUNT32
:
2304 return LISTBOX_GetSelCount( wnd
, descr
);
2306 case LB_GETSELITEMS16
:
2307 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2308 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2310 case LB_GETSELITEMS32
:
2311 return LISTBOX_GetSelItems32( wnd
, descr
, wParam
, (LPINT32
)lParam
);
2313 case LB_SELITEMRANGE16
:
2314 case LB_SELITEMRANGE32
:
2315 if (LOWORD(lParam
) <= HIWORD(lParam
))
2316 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2317 HIWORD(lParam
), wParam
);
2319 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2320 LOWORD(lParam
), wParam
);
2322 case LB_SELITEMRANGEEX16
:
2323 case LB_SELITEMRANGEEX32
:
2324 if ((INT32
)lParam
>= (INT32
)wParam
)
2325 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2327 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2329 case LB_GETHORIZONTALEXTENT16
:
2330 case LB_GETHORIZONTALEXTENT32
:
2331 return descr
->horz_extent
;
2333 case LB_SETHORIZONTALEXTENT16
:
2334 case LB_SETHORIZONTALEXTENT32
:
2335 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2337 case LB_GETANCHORINDEX16
:
2338 case LB_GETANCHORINDEX32
:
2339 return descr
->anchor_item
;
2341 case LB_SETANCHORINDEX16
:
2342 wParam
= (INT32
)(INT16
)wParam
;
2344 case LB_SETANCHORINDEX32
:
2345 if (((INT32
)wParam
< -1) || ((INT32
)wParam
>= descr
->nb_items
))
2347 descr
->anchor_item
= (INT32
)wParam
;
2351 return LISTBOX_Directory( wnd
, descr
, wParam
,
2352 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2355 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2357 case LB_GETLOCALE32
:
2358 return descr
->locale
;
2360 case LB_SETLOCALE32
:
2361 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2364 case LB_INITSTORAGE32
:
2365 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2368 return LISTBOX_SetCount( wnd
, descr
, (INT32
)wParam
);
2370 case LB_SETTABSTOPS16
:
2371 return LISTBOX_SetTabStops( wnd
, descr
, (INT32
)(INT16
)wParam
,
2372 (LPINT32
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2374 case LB_SETTABSTOPS32
:
2375 return LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2376 (LPINT32
)lParam
, FALSE
);
2380 if (descr
->caret_on
) return LB_OKAY
;
2381 descr
->caret_on
= TRUE
;
2382 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2383 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2388 if (!descr
->caret_on
) return LB_OKAY
;
2389 descr
->caret_on
= FALSE
;
2390 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2391 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2395 return LISTBOX_Destroy( wnd
, descr
);
2398 InvalidateRect32( hwnd
, NULL
, TRUE
);
2402 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2406 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2411 HDC32 hdc
= ( wParam
) ? ((HDC32
)wParam
)
2412 : BeginPaint32( hwnd
, &ps
);
2413 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2414 if( !wParam
) EndPaint32( hwnd
, &ps
);
2419 LISTBOX_UpdateSize( wnd
, descr
);
2426 LISTBOX_SetFont( wnd
, descr
, (HFONT32
)wParam
);
2427 if (lParam
) InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
2431 descr
->caret_on
= TRUE
;
2432 if (descr
->focus_item
!= -1)
2433 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2434 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2438 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2439 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2440 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2444 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2447 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2449 case WM_LBUTTONDOWN
:
2450 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2451 (INT16
)LOWORD(lParam
),
2452 (INT16
)HIWORD(lParam
) );
2454 case WM_LBUTTONDBLCLK
:
2455 if (descr
->style
& LBS_NOTIFY
)
2456 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2460 if (GetCapture32() == hwnd
)
2461 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2462 (INT16
)HIWORD(lParam
) );
2466 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2469 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2472 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2475 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2478 if (IS_OWNERDRAW(descr
))
2480 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2481 HBRUSH32 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
2482 wParam
, (LPARAM
)wnd
->hwndSelf
);
2483 if (hbrush
) FillRect32( (HDC32
)wParam
, &rect
, hbrush
);
2489 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2493 case WM_QUERYDROPOBJECT
:
2498 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2499 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2501 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2506 if (TWEAK_WineLook
> WIN31_LOOK
)
2507 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2508 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2511 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2512 WARN(listbox
, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2513 hwnd
, msg
, wParam
, lParam
);
2514 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2519 /***********************************************************************
2522 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT32 attrib
, LPSTR dir
, BOOL32 bLong
)
2524 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2528 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2531 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2533 RedrawWindow32( lphc
->self
->hwndSelf
, NULL
, 0,
2534 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2541 /***********************************************************************
2544 * NOTE: in Windows, winproc address of the ComboLBox is the same
2545 * as that of the Listbox.
2547 LRESULT WINAPI
ComboLBWndProc( HWND32 hwnd
, UINT32 msg
,
2548 WPARAM32 wParam
, LPARAM lParam
)
2551 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2555 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2557 TRACE(combo
, "[%04x]: msg %s wp %08x lp %08lx\n",
2558 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2560 if( descr
|| msg
== WM_CREATE
)
2562 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2567 #define lpcs ((LPCREATESTRUCT32A)lParam)
2568 TRACE(combo
, "\tpassed parent handle = 0x%08x\n",
2569 (UINT32
)lpcs
->lpCreateParams
);
2571 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2573 return LISTBOX_Create( wnd
, lphc
);
2575 case WM_LBUTTONDOWN
:
2576 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2577 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2579 /* avoid activation at all costs */
2581 case WM_MOUSEACTIVATE
:
2582 return MA_NOACTIVATE
;
2588 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2590 /* for some reason(?) Windows makes it possible to
2591 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2593 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2594 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2595 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2597 COMBO_FlipListbox( lphc
, FALSE
);
2601 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2603 case LB_SETCURSEL16
:
2604 case LB_SETCURSEL32
:
2605 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2606 return (lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2609 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2614 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2617 lRet
= DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2619 TRACE(combo
,"\t default on msg [%04x]\n", (UINT16
)msg
);