4 * Copyright 1996 Alexandre Julliard
25 /* Items array granularity */
26 #define LB_ARRAY_GRANULARITY 16
28 /* Scrolling timeout in ms */
29 #define LB_SCROLL_TIMEOUT 50
31 /* Listbox system timer id */
37 LPSTR str
; /* Item text */
38 BOOL32 selected
; /* Is item selected? */
39 UINT32 height
; /* Item height (only for OWNERDRAWVARIABLE) */
40 DWORD data
; /* User data */
43 /* Listbox structure */
46 HANDLE32 heap
; /* Heap for this listbox */
47 HWND32 owner
; /* Owner window to send notifications to */
48 UINT32 style
; /* Window style */
49 INT32 width
; /* Window width */
50 INT32 height
; /* Window height */
51 LB_ITEMDATA
*items
; /* Array of items */
52 INT32 nb_items
; /* Number of items */
53 INT32 top_item
; /* Top visible item */
54 INT32 selected_item
; /* Selected item */
55 INT32 focus_item
; /* Item that has the focus */
56 INT32 anchor_item
; /* Anchor item for extended selection */
57 INT32 item_height
; /* Default item height */
58 INT32 page_size
; /* Items per listbox page */
59 INT32 column_width
; /* Column width for multi-column listboxes */
60 INT32 horz_extent
; /* Horizontal extent (0 if no hscroll) */
61 INT32 horz_pos
; /* Horizontal position */
62 INT32 nb_tabs
; /* Number of tabs in array */
63 INT32
*tabs
; /* Array of tabs */
64 BOOL32 caret_on
; /* Is caret on? */
65 HFONT32 font
; /* Current font */
66 LCID locale
; /* Current locale for string comparisons */
67 LPHEADCOMBO lphc
; /* ComboLBox */
71 #define IS_OWNERDRAW(descr) \
72 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
74 #define HAS_STRINGS(descr) \
75 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
77 #define SEND_NOTIFICATION(wnd,descr,code) \
78 (SendMessage32A( (descr)->owner, WM_COMMAND, \
79 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
81 /* Current timer status */
91 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
94 /***********************************************************************
97 void LISTBOX_Dump( WND
*wnd
)
101 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
103 DUMP( "Listbox:\n" );
104 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
105 wnd
->hwndSelf
, (UINT32
)descr
, descr
->heap
, descr
->nb_items
,
107 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
109 DUMP( "%4d: %-40s %d %08lx %3d\n",
110 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
115 /***********************************************************************
116 * LISTBOX_GetCurrentPageSize
118 * Return the current page size
120 static INT32
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
123 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
124 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
126 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
128 if (i
== descr
->top_item
) return 1;
129 else return i
- descr
->top_item
;
133 /***********************************************************************
134 * LISTBOX_GetMaxTopIndex
136 * Return the maximum possible index for the top of the listbox.
138 static INT32
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
142 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
144 page
= descr
->height
;
145 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
146 if ((page
-= descr
->items
[max
].height
) < 0) break;
147 if (max
< descr
->nb_items
- 1) max
++;
149 else if (descr
->style
& LBS_MULTICOLUMN
)
151 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
152 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
153 max
= (max
- page
) * descr
->page_size
;
157 max
= descr
->nb_items
- descr
->page_size
;
159 if (max
< 0) max
= 0;
164 /***********************************************************************
165 * LISTBOX_UpdateScroll
167 * Update the scrollbars. Should be called whenever the content
168 * of the listbox changes.
170 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
174 if (descr
->style
& LBS_NOREDRAW
) return;
175 info
.cbSize
= sizeof(info
);
177 if (descr
->style
& LBS_MULTICOLUMN
)
180 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
181 info
.nPos
= descr
->top_item
/ descr
->page_size
;
182 info
.nPage
= descr
->width
/ descr
->column_width
;
183 if (info
.nPage
< 1) info
.nPage
= 1;
184 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
185 if (descr
->style
& LBS_DISABLENOSCROLL
)
186 info
.fMask
|= SIF_DISABLENOSCROLL
;
187 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
189 info
.fMask
= SIF_RANGE
;
190 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
195 info
.nMax
= descr
->nb_items
- 1;
196 info
.nPos
= descr
->top_item
;
197 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
198 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
199 if (descr
->style
& LBS_DISABLENOSCROLL
)
200 info
.fMask
|= SIF_DISABLENOSCROLL
;
201 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
203 if (descr
->horz_extent
)
206 info
.nMax
= descr
->horz_extent
- 1;
207 info
.nPos
= descr
->horz_pos
;
208 info
.nPage
= descr
->width
;
209 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
210 if (descr
->style
& LBS_DISABLENOSCROLL
)
211 info
.fMask
|= SIF_DISABLENOSCROLL
;
212 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
218 /***********************************************************************
221 * Set the top item of the listbox, scrolling up or down if necessary.
223 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
226 INT32 max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
227 if (index
> max
) index
= max
;
228 if (index
< 0) index
= 0;
229 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
230 if (descr
->top_item
== index
) return LB_OKAY
;
231 if (descr
->style
& LBS_MULTICOLUMN
)
233 INT32 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
234 if (scroll
&& (abs(diff
) < descr
->width
))
235 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
236 SW_INVALIDATE
| SW_ERASE
);
243 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
247 if (index
> descr
->top_item
)
249 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
250 diff
-= descr
->items
[i
].height
;
254 for (i
= index
; i
< descr
->top_item
; i
++)
255 diff
+= descr
->items
[i
].height
;
259 diff
= (descr
->top_item
- index
) * descr
->item_height
;
261 if (abs(diff
) < descr
->height
)
262 ScrollWindowEx32( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
263 SW_INVALIDATE
| SW_ERASE
);
267 if (!scroll
) InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
268 descr
->top_item
= index
;
269 LISTBOX_UpdateScroll( wnd
, descr
);
274 /***********************************************************************
277 * Update the page size. Should be called when the size of
278 * the client area or the item height changes.
280 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
284 if ((page_size
= descr
->height
/ descr
->item_height
) < 1) page_size
= 1;
285 if (page_size
== descr
->page_size
) return;
286 descr
->page_size
= page_size
;
287 if (descr
->style
& LBS_MULTICOLUMN
)
288 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
289 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
293 /***********************************************************************
296 * Update the size of the listbox. Should be called when the size of
297 * the client area changes.
299 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
303 GetClientRect32( wnd
->hwndSelf
, &rect
);
304 descr
->width
= rect
.right
- rect
.left
;
305 descr
->height
= rect
.bottom
- rect
.top
;
306 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
))
308 if ((descr
->height
> descr
->item_height
) &&
309 (descr
->height
% descr
->item_height
))
311 TRACE(listbox
, "[%04x]: changing height %d -> %d\n",
312 wnd
->hwndSelf
, descr
->height
,
313 descr
->height
- descr
->height
%descr
->item_height
);
314 SetWindowPos32( wnd
->hwndSelf
, 0, 0, 0,
315 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
316 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
317 (descr
->height
% descr
->item_height
),
318 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
322 TRACE(listbox
, "[%04x]: new size = %d,%d\n",
323 wnd
->hwndSelf
, descr
->width
, descr
->height
);
324 LISTBOX_UpdatePage( wnd
, descr
);
325 LISTBOX_UpdateScroll( wnd
, descr
);
329 /***********************************************************************
330 * LISTBOX_GetItemRect
332 * Get the rectangle enclosing an item, in listbox client coordinates.
333 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
335 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
338 /* Index <= 0 is legal even on empty listboxes */
339 if (index
&& (index
>= descr
->nb_items
)) return -1;
340 SetRect32( rect
, 0, 0, descr
->width
, descr
->height
);
341 if (descr
->style
& LBS_MULTICOLUMN
)
343 INT32 col
= (index
/ descr
->page_size
) -
344 (descr
->top_item
/ descr
->page_size
);
345 rect
->left
+= col
* descr
->column_width
;
346 rect
->right
= rect
->left
+ descr
->column_width
;
347 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
348 rect
->bottom
= rect
->top
+ descr
->item_height
;
350 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
353 rect
->right
+= descr
->horz_pos
;
354 if ((index
>= 0) && (index
< descr
->nb_items
))
356 if (index
< descr
->top_item
)
358 for (i
= descr
->top_item
-1; i
>= index
; i
--)
359 rect
->top
-= descr
->items
[i
].height
;
363 for (i
= descr
->top_item
; i
< index
; i
++)
364 rect
->top
+= descr
->items
[i
].height
;
366 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
372 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
373 rect
->bottom
= rect
->top
+ descr
->item_height
;
374 rect
->right
+= descr
->horz_pos
;
377 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
378 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
382 /***********************************************************************
383 * LISTBOX_GetItemFromPoint
385 * Return the item nearest from point (x,y) (in client coordinates).
387 static INT32
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
390 INT32 index
= descr
->top_item
;
392 if (!descr
->nb_items
) return -1; /* No items */
393 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
398 while (index
< descr
->nb_items
)
400 if ((pos
+= descr
->items
[index
].height
) > y
) break;
409 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
413 else if (descr
->style
& LBS_MULTICOLUMN
)
415 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
416 if (y
>= 0) index
+= y
/ descr
->item_height
;
417 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
418 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
422 index
+= (y
/ descr
->item_height
);
424 if (index
< 0) return 0;
425 if (index
>= descr
->nb_items
) return -1;
430 /***********************************************************************
435 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
,
436 const RECT32
*rect
, INT32 index
, UINT32 action
)
438 LB_ITEMDATA
*item
= NULL
;
439 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
441 if (IS_OWNERDRAW(descr
))
443 DRAWITEMSTRUCT32 dis
;
444 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
446 dis
.CtlType
= ODT_LISTBOX
;
448 dis
.hwndItem
= wnd
->hwndSelf
;
449 dis
.itemAction
= action
;
453 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
454 if ((descr
->focus_item
== index
) &&
456 (GetFocus32() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
457 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
458 dis
.itemData
= item
? item
->data
: 0;
460 TRACE(listbox
, "[%04x]: drawitem %d (%s) action=%02x "
461 "state=%02x rect=%d,%d-%d,%d\n",
462 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
463 dis
.itemState
, rect
->left
, rect
->top
,
464 rect
->right
, rect
->bottom
);
465 SendMessage32A(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
469 COLORREF oldText
= 0, oldBk
= 0;
471 if (action
== ODA_FOCUS
)
473 DrawFocusRect32( hdc
, rect
);
476 if (item
&& item
->selected
)
478 oldBk
= SetBkColor32( hdc
, GetSysColor32( COLOR_HIGHLIGHT
) );
479 oldText
= SetTextColor32( hdc
, GetSysColor32(COLOR_HIGHLIGHTTEXT
));
482 TRACE(listbox
, "[%04x]: painting %d (%s) action=%02x "
483 "rect=%d,%d-%d,%d\n",
484 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
485 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
487 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
488 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
489 else if (!(descr
->style
& LBS_USETABSTOPS
))
490 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
491 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
492 strlen(item
->str
), NULL
);
495 /* Output empty string to paint background in the full width. */
496 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
497 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
498 TabbedTextOut32A( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
499 item
->str
, strlen(item
->str
),
500 descr
->nb_tabs
, descr
->tabs
, 0);
502 if (item
&& item
->selected
)
504 SetBkColor32( hdc
, oldBk
);
505 SetTextColor32( hdc
, oldText
);
507 if ((descr
->focus_item
== index
) &&
509 (GetFocus32() == wnd
->hwndSelf
)) DrawFocusRect32( hdc
, rect
);
514 /***********************************************************************
517 * Change the redraw flag.
519 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL32 on
)
523 if (!(descr
->style
& LBS_NOREDRAW
)) return;
524 descr
->style
&= ~LBS_NOREDRAW
;
525 LISTBOX_UpdateScroll( wnd
, descr
);
527 else descr
->style
|= LBS_NOREDRAW
;
531 /***********************************************************************
532 * LISTBOX_RepaintItem
534 * Repaint a single item synchronously.
536 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
542 HBRUSH32 hbrush
, oldBrush
= 0;
544 if (descr
->style
& LBS_NOREDRAW
) return;
545 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
546 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
547 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
548 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
549 hdc
, (LPARAM
)wnd
->hwndSelf
);
550 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
551 if (wnd
->dwStyle
& WS_DISABLED
)
552 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
553 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
554 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
555 if (oldFont
) SelectObject32( hdc
, oldFont
);
556 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
557 ReleaseDC32( wnd
->hwndSelf
, hdc
);
561 /***********************************************************************
562 * LISTBOX_InitStorage
564 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT32 nb_items
,
569 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
570 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
572 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
573 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
574 nb_items
* sizeof(LB_ITEMDATA
) )))
576 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
584 /***********************************************************************
585 * LISTBOX_SetTabStops
587 static BOOL32
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT32 count
,
588 LPINT32 tabs
, BOOL32 short_ints
)
590 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
591 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
592 if (!(descr
->nb_tabs
= count
))
597 /* FIXME: count = 1 */
598 if (!(descr
->tabs
= (INT32
*)HeapAlloc( descr
->heap
, 0,
599 descr
->nb_tabs
* sizeof(INT32
) )))
604 LPINT16 p
= (LPINT16
)tabs
;
605 dbg_decl_str(listbox
, 256);
607 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
608 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
609 if(TRACE_ON(listbox
))
610 dsprintf(listbox
, "%hd ", descr
->tabs
[i
]);
612 TRACE(listbox
, "[%04x]: settabstops %s\n",
613 wnd
->hwndSelf
, dbg_str(listbox
));
615 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT32
) );
616 /* FIXME: repaint the window? */
621 /***********************************************************************
624 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
627 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
628 if (HAS_STRINGS(descr
))
630 lstrcpy32A( buffer
, descr
->items
[index
].str
);
631 return strlen(buffer
);
635 memcpy( buffer
, &descr
->items
[index
].data
, sizeof(DWORD
) );
636 return sizeof(DWORD
);
641 /***********************************************************************
642 * LISTBOX_FindStringPos
644 * Find the nearest string located before a given string in sort order.
645 * If 'exact' is TRUE, return an error if we don't get an exact match.
647 static INT32
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
650 INT32 index
, min
, max
, res
= -1;
652 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
654 max
= descr
->nb_items
;
657 index
= (min
+ max
) / 2;
658 if (HAS_STRINGS(descr
))
659 res
= lstrcmpi32A( descr
->items
[index
].str
, str
);
662 COMPAREITEMSTRUCT32 cis
;
663 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
665 cis
.CtlType
= ODT_LISTBOX
;
667 cis
.hwndItem
= wnd
->hwndSelf
;
669 cis
.itemData1
= descr
->items
[index
].data
;
671 cis
.itemData2
= (DWORD
)str
;
672 cis
.dwLocaleId
= descr
->locale
;
673 res
= SendMessage32A( descr
->owner
, WM_COMPAREITEM
,
676 if (!res
) return index
;
677 if (res
> 0) max
= index
;
678 else min
= index
+ 1;
680 return exact
? -1 : max
;
684 /***********************************************************************
685 * LISTBOX_FindFileStrPos
687 * Find the nearest string located before a given string in directory
688 * sort order (i.e. first files, then directories, then drives).
690 static INT32
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
692 INT32 min
, max
, res
= -1;
694 if (!HAS_STRINGS(descr
))
695 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
697 max
= descr
->nb_items
;
700 INT32 index
= (min
+ max
) / 2;
701 const char *p
= descr
->items
[index
].str
;
702 if (*p
== '[') /* drive or directory */
704 if (*str
!= '[') res
= -1;
705 else if (p
[1] == '-') /* drive */
707 if (str
[1] == '-') res
= str
[2] - p
[2];
712 if (str
[1] == '-') res
= 1;
713 else res
= lstrcmpi32A( str
, p
);
718 if (*str
== '[') res
= 1;
719 else res
= lstrcmpi32A( str
, p
);
721 if (!res
) return index
;
722 if (res
< 0) max
= index
;
723 else min
= index
+ 1;
729 /***********************************************************************
732 * Find the item beginning with a given string.
734 static INT32
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT32 start
,
735 LPCSTR str
, BOOL32 exact
)
740 if (start
>= descr
->nb_items
) start
= -1;
741 item
= descr
->items
+ start
+ 1;
742 if (HAS_STRINGS(descr
))
744 if (!str
) return LB_ERR
;
747 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
748 if (!lstrcmpi32A( str
, item
->str
)) return i
;
749 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
750 if (!lstrcmpi32A( str
, item
->str
)) return i
;
754 /* Special case for drives and directories: ignore prefix */
755 #define CHECK_DRIVE(item) \
756 if ((item)->str[0] == '[') \
758 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
759 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
763 INT32 len
= strlen(str
);
764 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
766 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
769 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
771 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
779 if (exact
&& (descr
->style
& LBS_SORT
))
780 /* If sorted, use a WM_COMPAREITEM binary search */
781 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
783 /* Otherwise use a linear search */
784 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
785 if (item
->data
== (DWORD
)str
) return i
;
786 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
787 if (item
->data
== (DWORD
)str
) return i
;
793 /***********************************************************************
794 * LISTBOX_GetSelCount
796 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
799 LB_ITEMDATA
*item
= descr
->items
;
801 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
802 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
803 if (item
->selected
) count
++;
808 /***********************************************************************
809 * LISTBOX_GetSelItems16
811 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
815 LB_ITEMDATA
*item
= descr
->items
;
817 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
818 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
819 if (item
->selected
) array
[count
++] = (INT16
)i
;
824 /***********************************************************************
825 * LISTBOX_GetSelItems32
827 static LRESULT
LISTBOX_GetSelItems32( WND
*wnd
, LB_DESCR
*descr
, INT32 max
,
831 LB_ITEMDATA
*item
= descr
->items
;
833 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
834 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
835 if (item
->selected
) array
[count
++] = i
;
840 /***********************************************************************
843 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
)
845 INT32 i
, col_pos
= descr
->page_size
- 1;
848 HBRUSH32 hbrush
, oldBrush
= 0;
850 SetRect32( &rect
, 0, 0, descr
->width
, descr
->height
);
851 if (descr
->style
& LBS_NOREDRAW
) return 0;
852 if (descr
->style
& LBS_MULTICOLUMN
)
853 rect
.right
= rect
.left
+ descr
->column_width
;
854 else if (descr
->horz_pos
)
856 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
857 rect
.right
+= descr
->horz_pos
;
860 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
861 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
862 hdc
, (LPARAM
)wnd
->hwndSelf
);
863 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
864 if (wnd
->dwStyle
& WS_DISABLED
)
865 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
867 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
868 (GetFocus32() == wnd
->hwndSelf
))
870 /* Special case for empty listbox: paint focus rect */
871 rect
.bottom
= rect
.top
+ descr
->item_height
;
872 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
874 rect
.top
= rect
.bottom
;
877 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
879 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
880 rect
.bottom
= rect
.top
+ descr
->item_height
;
882 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
884 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
885 rect
.top
= rect
.bottom
;
887 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
889 if (!IS_OWNERDRAW(descr
))
891 /* Clear the bottom of the column */
892 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
893 if (rect
.top
< descr
->height
)
895 rect
.bottom
= descr
->height
;
896 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
897 &rect
, NULL
, 0, NULL
);
901 /* Go to the next column */
902 rect
.left
+= descr
->column_width
;
903 rect
.right
+= descr
->column_width
;
905 col_pos
= descr
->page_size
- 1;
910 if (rect
.top
>= descr
->height
) break;
914 if (!IS_OWNERDRAW(descr
))
916 /* Clear the remainder of the client area */
917 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
918 if (rect
.top
< descr
->height
)
920 rect
.bottom
= descr
->height
;
921 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
922 &rect
, NULL
, 0, NULL
);
924 if (rect
.right
< descr
->width
)
926 rect
.left
= rect
.right
;
927 rect
.right
= descr
->width
;
929 rect
.bottom
= descr
->height
;
930 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
931 &rect
, NULL
, 0, NULL
);
934 if (oldFont
) SelectObject32( hdc
, oldFont
);
935 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
940 /***********************************************************************
941 * LISTBOX_InvalidateItems
943 * Invalidate all items from a given item. If the specified item is not
944 * visible, nothing happens.
946 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
950 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
952 rect
.bottom
= descr
->height
;
953 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
954 if (descr
->style
& LBS_MULTICOLUMN
)
956 /* Repaint the other columns */
957 rect
.left
= rect
.right
;
958 rect
.right
= descr
->width
;
960 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
966 /***********************************************************************
967 * LISTBOX_GetItemHeight
969 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
971 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
973 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
974 return descr
->items
[index
].height
;
976 else return descr
->item_height
;
980 /***********************************************************************
981 * LISTBOX_SetItemHeight
983 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
986 if (!height
) height
= 1;
988 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
990 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
991 TRACE(listbox
, "[%04x]: item %d height = %d\n",
992 wnd
->hwndSelf
, index
, height
);
993 descr
->items
[index
].height
= height
;
994 LISTBOX_UpdateScroll( wnd
, descr
);
995 LISTBOX_InvalidateItems( wnd
, descr
, index
);
997 else if (height
!= descr
->item_height
)
999 TRACE(listbox
, "[%04x]: new height = %d\n",
1000 wnd
->hwndSelf
, height
);
1001 descr
->item_height
= height
;
1002 LISTBOX_UpdatePage( wnd
, descr
);
1003 LISTBOX_UpdateScroll( wnd
, descr
);
1004 InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
1010 /***********************************************************************
1011 * LISTBOX_SetHorizontalPos
1013 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT32 pos
)
1017 if (pos
> descr
->horz_extent
- descr
->width
)
1018 pos
= descr
->horz_extent
- descr
->width
;
1019 if (pos
< 0) pos
= 0;
1020 if (!(diff
= descr
->horz_pos
- pos
)) return;
1021 TRACE(listbox
, "[%04x]: new horz pos = %d\n",
1022 wnd
->hwndSelf
, pos
);
1023 descr
->horz_pos
= pos
;
1024 LISTBOX_UpdateScroll( wnd
, descr
);
1025 if (abs(diff
) < descr
->width
)
1026 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1027 SW_INVALIDATE
| SW_ERASE
);
1029 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1033 /***********************************************************************
1034 * LISTBOX_SetHorizontalExtent
1036 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1039 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1041 if (extent
<= 0) extent
= 1;
1042 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1043 TRACE(listbox
, "[%04x]: new horz extent = %d\n",
1044 wnd
->hwndSelf
, extent
);
1045 descr
->horz_extent
= extent
;
1046 if (descr
->horz_pos
> extent
- descr
->width
)
1047 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1049 LISTBOX_UpdateScroll( wnd
, descr
);
1054 /***********************************************************************
1055 * LISTBOX_SetColumnWidth
1057 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT32 width
)
1059 width
+= 2; /* For left and right margin */
1060 if (width
== descr
->column_width
) return LB_OKAY
;
1061 TRACE(listbox
, "[%04x]: new column width = %d\n",
1062 wnd
->hwndSelf
, width
);
1063 descr
->column_width
= width
;
1064 LISTBOX_UpdatePage( wnd
, descr
);
1069 /***********************************************************************
1072 * Returns the item height.
1074 static INT32
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT32 font
)
1077 HFONT32 oldFont
= 0;
1082 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1084 ERR(listbox
, "unable to get DC.\n" );
1087 if (font
) oldFont
= SelectObject32( hdc
, font
);
1088 GetTextMetrics32A( hdc
, &tm
);
1089 if (oldFont
) SelectObject32( hdc
, oldFont
);
1090 ReleaseDC32( wnd
->hwndSelf
, hdc
);
1091 if (!IS_OWNERDRAW(descr
))
1092 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
+ 2 );
1093 return tm
.tmHeight
+ 2;
1097 /***********************************************************************
1098 * LISTBOX_MakeItemVisible
1100 * Make sure that a given item is partially or fully visible.
1102 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1107 if (index
<= descr
->top_item
) top
= index
;
1108 else if (descr
->style
& LBS_MULTICOLUMN
)
1110 INT32 cols
= descr
->width
;
1111 if (!fully
) cols
+= descr
->column_width
- 1;
1112 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1114 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1115 top
= index
- descr
->page_size
* (cols
- 1);
1117 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1119 INT32 height
= fully
? descr
->items
[index
].height
: 1;
1120 for (top
= index
; top
> descr
->top_item
; top
--)
1121 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1125 if (index
< descr
->top_item
+ descr
->page_size
) return;
1126 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1127 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1128 top
= index
- descr
->page_size
+ 1;
1130 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1134 /***********************************************************************
1135 * LISTBOX_SelectItemRange
1137 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1139 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT32 first
,
1140 INT32 last
, BOOL32 on
)
1144 /* A few sanity checks */
1146 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1147 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1148 if (last
== -1) last
= descr
->nb_items
- 1;
1149 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1150 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1151 /* selected_item reflects last selected/unselected item on multiple sel */
1152 descr
->selected_item
= last
;
1154 if (on
) /* Turn selection on */
1156 for (i
= first
; i
<= last
; i
++)
1158 if (descr
->items
[i
].selected
) continue;
1159 descr
->items
[i
].selected
= TRUE
;
1160 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1163 else /* Turn selection off */
1165 for (i
= first
; i
<= last
; i
++)
1167 if (!descr
->items
[i
].selected
) continue;
1168 descr
->items
[i
].selected
= FALSE
;
1169 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1176 /***********************************************************************
1177 * LISTBOX_SetCaretIndex
1179 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1180 BOOL32 fully_visible
)
1182 INT32 oldfocus
= descr
->focus_item
;
1184 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1185 if (index
== oldfocus
) return LB_OKAY
;
1186 descr
->focus_item
= index
;
1187 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1188 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1191 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1192 if (descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1193 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1199 /***********************************************************************
1200 * LISTBOX_SetSelection
1202 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1203 BOOL32 on
, BOOL32 send_notify
)
1205 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1206 if (descr
->style
& LBS_MULTIPLESEL
)
1208 if (index
== -1) /* Select all items */
1209 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1210 else /* Only one item */
1211 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1215 INT32 oldsel
= descr
->selected_item
;
1216 if (index
== oldsel
) return LB_OKAY
;
1217 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1218 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1219 descr
->selected_item
= index
;
1220 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1221 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1222 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1223 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1229 /***********************************************************************
1232 * Change the caret position and extend the selection to the new caret.
1234 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1235 BOOL32 fully_visible
)
1237 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1238 if (descr
->style
& LBS_EXTENDEDSEL
)
1240 if (descr
->anchor_item
!= -1)
1242 INT32 first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1243 INT32 last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1245 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1246 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1247 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1250 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1252 /* Set selection to new caret item */
1253 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1258 /***********************************************************************
1259 * LISTBOX_InsertItem
1261 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1262 LPSTR str
, DWORD data
)
1267 if (index
== -1) index
= descr
->nb_items
;
1268 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1269 if (!descr
->items
) max_items
= 0;
1270 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1271 if (descr
->nb_items
== max_items
)
1273 /* We need to grow the array */
1274 max_items
+= LB_ARRAY_GRANULARITY
;
1275 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1276 max_items
* sizeof(LB_ITEMDATA
) )))
1278 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1281 descr
->items
= item
;
1284 /* Insert the item structure */
1286 item
= &descr
->items
[index
];
1287 if (index
< descr
->nb_items
)
1288 RtlMoveMemory( item
+ 1, item
,
1289 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1293 item
->selected
= FALSE
;
1296 /* Get item height */
1298 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1300 MEASUREITEMSTRUCT32 mis
;
1301 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1303 mis
.CtlType
= ODT_LISTBOX
;
1306 mis
.itemData
= descr
->items
[index
].data
;
1307 mis
.itemHeight
= descr
->item_height
;
1308 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1309 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1310 TRACE(listbox
, "[%04x]: measure item %d (%s) = %d\n",
1311 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1314 /* Repaint the items */
1316 LISTBOX_UpdateScroll( wnd
, descr
);
1317 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1319 /* Move selection and focused item */
1321 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1322 if (index
<= descr
->focus_item
)
1324 descr
->focus_item
++;
1325 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
- 1, FALSE
);
1328 /* If listbox was empty, set focus to the first item */
1330 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1335 /***********************************************************************
1336 * LISTBOX_InsertString
1338 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1341 LPSTR new_str
= NULL
;
1345 if (HAS_STRINGS(descr
))
1347 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1349 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1353 else data
= (DWORD
)str
;
1355 if (index
== -1) index
= descr
->nb_items
;
1356 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1358 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1362 TRACE(listbox
, "[%04x]: added item %d '%s'\n",
1363 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1368 /***********************************************************************
1369 * LISTBOX_DeleteItem
1371 * Delete the content of an item. 'index' must be a valid index.
1373 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1375 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1376 * while Win95 sends it for all items with user data.
1377 * It's probably better to send it too often than not
1378 * often enough, so this is what we do here.
1380 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1382 DELETEITEMSTRUCT32 dis
;
1383 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1385 dis
.CtlType
= ODT_LISTBOX
;
1388 dis
.hwndItem
= wnd
->hwndSelf
;
1389 dis
.itemData
= descr
->items
[index
].data
;
1390 SendMessage32A( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1392 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1393 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1397 /***********************************************************************
1398 * LISTBOX_RemoveItem
1400 * Remove an item from the listbox and delete its content.
1402 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1407 if (index
== -1) index
= descr
->nb_items
- 1;
1408 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1409 LISTBOX_DeleteItem( wnd
, descr
, index
);
1411 /* Remove the item */
1413 item
= &descr
->items
[index
];
1414 if (index
< descr
->nb_items
-1)
1415 RtlMoveMemory( item
, item
+ 1,
1416 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1418 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1420 /* Shrink the item array if possible */
1422 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1423 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1425 max_items
-= LB_ARRAY_GRANULARITY
;
1426 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1427 max_items
* sizeof(LB_ITEMDATA
) );
1428 if (item
) descr
->items
= item
;
1431 /* Repaint the items */
1433 LISTBOX_UpdateScroll( wnd
, descr
);
1434 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1436 /* Move selection and focused item */
1438 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1439 if (index
<= descr
->focus_item
)
1441 descr
->focus_item
--;
1442 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
+ 1, FALSE
);
1448 /***********************************************************************
1449 * LISTBOX_ResetContent
1451 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1455 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1456 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1457 descr
->nb_items
= 0;
1458 descr
->top_item
= 0;
1459 descr
->selected_item
= -1;
1460 descr
->focus_item
= 0;
1461 descr
->anchor_item
= -1;
1462 descr
->items
= NULL
;
1463 LISTBOX_UpdateScroll( wnd
, descr
);
1464 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1468 /***********************************************************************
1471 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT32 count
)
1475 if (HAS_STRINGS(descr
)) return LB_ERR
;
1476 /* FIXME: this is far from optimal... */
1477 if (count
> descr
->nb_items
)
1479 while (count
> descr
->nb_items
)
1480 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1483 else if (count
< descr
->nb_items
)
1485 while (count
< descr
->nb_items
)
1486 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1493 /***********************************************************************
1496 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT32 attrib
,
1497 LPCSTR filespec
, BOOL32 long_names
)
1500 LRESULT ret
= LB_OKAY
;
1501 WIN32_FIND_DATA32A entry
;
1504 if ((handle
= FindFirstFile32A(filespec
,&entry
)) == INVALID_HANDLE_VALUE32
)
1506 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1513 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1515 if (!(attrib
& DDL_DIRECTORY
) ||
1516 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1517 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1518 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1520 else /* not a directory */
1522 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1523 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1525 if ((attrib
& DDL_EXCLUSIVE
) &&
1526 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1529 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1530 else strcpy( buffer
, entry
.cAlternateFileName
);
1532 if (!long_names
) CharLower32A( buffer
);
1533 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1534 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1536 } while (FindNextFile32A( handle
, &entry
));
1537 FindClose32( handle
);
1540 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1542 char buffer
[] = "[-a-]";
1544 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1546 if (!DRIVE_IsValid(drive
)) continue;
1547 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1555 /***********************************************************************
1556 * LISTBOX_HandleVScroll
1558 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1559 WPARAM32 wParam
, LPARAM lParam
)
1563 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1564 switch(LOWORD(wParam
))
1567 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1570 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1573 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1574 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1577 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1578 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1580 case SB_THUMBPOSITION
:
1581 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1584 info
.cbSize
= sizeof(info
);
1585 info
.fMask
= SIF_TRACKPOS
;
1586 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1587 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1590 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1593 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1600 /***********************************************************************
1601 * LISTBOX_HandleHScroll
1603 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1604 WPARAM32 wParam
, LPARAM lParam
)
1609 if (descr
->style
& LBS_MULTICOLUMN
)
1611 switch(LOWORD(wParam
))
1614 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1618 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1622 page
= descr
->width
/ descr
->column_width
;
1623 if (page
< 1) page
= 1;
1624 LISTBOX_SetTopItem( wnd
, descr
,
1625 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1628 page
= descr
->width
/ descr
->column_width
;
1629 if (page
< 1) page
= 1;
1630 LISTBOX_SetTopItem( wnd
, descr
,
1631 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1633 case SB_THUMBPOSITION
:
1634 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1638 info
.cbSize
= sizeof(info
);
1639 info
.fMask
= SIF_TRACKPOS
;
1640 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1641 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1645 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1648 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1652 else if (descr
->horz_extent
)
1654 switch(LOWORD(wParam
))
1657 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1660 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1663 LISTBOX_SetHorizontalPos( wnd
, descr
,
1664 descr
->horz_pos
- descr
->width
);
1667 LISTBOX_SetHorizontalPos( wnd
, descr
,
1668 descr
->horz_pos
+ descr
->width
);
1670 case SB_THUMBPOSITION
:
1671 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1674 info
.cbSize
= sizeof(info
);
1675 info
.fMask
= SIF_TRACKPOS
;
1676 GetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
);
1677 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1680 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1683 LISTBOX_SetHorizontalPos( wnd
, descr
,
1684 descr
->horz_extent
- descr
->width
);
1692 /***********************************************************************
1693 * LISTBOX_HandleLButtonDown
1695 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1696 WPARAM32 wParam
, INT32 x
, INT32 y
)
1698 INT32 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1699 TRACE(listbox
, "[%04x]: lbuttondown %d,%d item %d\n",
1700 wnd
->hwndSelf
, x
, y
, index
);
1701 if (!descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
)) return 0;
1704 if (descr
->style
& LBS_EXTENDEDSEL
)
1706 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1707 if (wParam
& MK_CONTROL
)
1709 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1710 LISTBOX_SetSelection( wnd
, descr
, index
,
1711 !descr
->items
[index
].selected
, FALSE
);
1713 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1717 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1718 LISTBOX_SetSelection( wnd
, descr
, index
,
1719 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1720 !descr
->items
[index
].selected
), FALSE
);
1724 if( !descr
->lphc
) SetFocus32( wnd
->hwndSelf
);
1725 else SetFocus32( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1726 : descr
->lphc
->self
->hwndSelf
) ;
1728 SetCapture32( wnd
->hwndSelf
);
1729 if (index
!= -1 && !descr
->lphc
)
1731 if (descr
->style
& LBS_NOTIFY
)
1732 SendMessage32A( descr
->owner
, WM_LBTRACKPOINT
, index
,
1733 MAKELPARAM( x
, y
) );
1734 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1736 POINT32 pt
= { x
, y
};
1737 if (DragDetect32( wnd
->hwndSelf
, pt
))
1738 SendMessage32A( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1745 /***********************************************************************
1746 * LISTBOX_HandleLButtonUp
1748 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1750 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1751 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1752 LISTBOX_Timer
= LB_TIMER_NONE
;
1753 if (GetCapture32() == wnd
->hwndSelf
)
1756 if (descr
->style
& LBS_NOTIFY
)
1757 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1763 /***********************************************************************
1764 * LISTBOX_HandleTimer
1766 * Handle scrolling upon a timer event.
1767 * Return TRUE if scrolling should continue.
1769 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1770 INT32 index
, TIMER_DIRECTION dir
)
1775 if (descr
->top_item
) index
= descr
->top_item
- 1;
1779 if (descr
->top_item
) index
-= descr
->page_size
;
1782 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1783 if (index
== descr
->focus_item
) index
++;
1784 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1786 case LB_TIMER_RIGHT
:
1787 if (index
+ descr
->page_size
< descr
->nb_items
)
1788 index
+= descr
->page_size
;
1793 if (index
== descr
->focus_item
) return FALSE
;
1794 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1799 /***********************************************************************
1800 * LISTBOX_HandleSystemTimer
1802 * WM_SYSTIMER handler.
1804 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1806 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1808 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1809 LISTBOX_Timer
= LB_TIMER_NONE
;
1815 /***********************************************************************
1816 * LISTBOX_HandleMouseMove
1818 * WM_MOUSEMOVE handler.
1820 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1824 TIMER_DIRECTION dir
;
1826 if (descr
->style
& LBS_MULTICOLUMN
)
1829 else if (y
>= descr
->item_height
* descr
->page_size
)
1830 y
= descr
->item_height
* descr
->page_size
- 1;
1834 dir
= LB_TIMER_LEFT
;
1837 else if (x
>= descr
->width
)
1839 dir
= LB_TIMER_RIGHT
;
1840 x
= descr
->width
- 1;
1842 else dir
= LB_TIMER_NONE
; /* inside */
1846 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1847 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1848 else dir
= LB_TIMER_NONE
; /* inside */
1851 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1852 if (index
== -1) index
= descr
->focus_item
;
1853 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1855 /* Start/stop the system timer */
1857 if (dir
!= LB_TIMER_NONE
)
1858 SetSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1859 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1860 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1861 LISTBOX_Timer
= dir
;
1865 /***********************************************************************
1866 * LISTBOX_HandleKeyDown
1868 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM32 wParam
)
1871 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1873 caret
= SendMessage32A( descr
->owner
, WM_VKEYTOITEM
,
1874 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1876 if (caret
== -2) return 0;
1878 if (caret
== -1) switch(wParam
)
1881 if (descr
->style
& LBS_MULTICOLUMN
)
1883 if (descr
->focus_item
>= descr
->page_size
)
1884 caret
= descr
->focus_item
- descr
->page_size
;
1889 caret
= descr
->focus_item
- 1;
1890 if (caret
< 0) caret
= 0;
1893 if (descr
->style
& LBS_MULTICOLUMN
)
1895 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1896 caret
= descr
->focus_item
+ descr
->page_size
;
1901 caret
= descr
->focus_item
+ 1;
1902 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1905 if (descr
->style
& LBS_MULTICOLUMN
)
1907 INT32 page
= descr
->width
/ descr
->column_width
;
1908 if (page
< 1) page
= 1;
1909 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1911 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1912 if (caret
< 0) caret
= 0;
1915 if (descr
->style
& LBS_MULTICOLUMN
)
1917 INT32 page
= descr
->width
/ descr
->column_width
;
1918 if (page
< 1) page
= 1;
1919 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1921 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1922 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1928 caret
= descr
->nb_items
- 1;
1931 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1932 else if (descr
->style
& LBS_MULTIPLESEL
)
1934 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1935 !descr
->items
[descr
->focus_item
].selected
,
1936 (descr
->style
& LBS_NOTIFY
) != 0 );
1938 else if (descr
->selected_item
== -1)
1940 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1941 (descr
->style
& LBS_NOTIFY
) != 0 );
1947 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1948 !(GetKeyState32( VK_SHIFT
) & 0x8000))
1949 descr
->anchor_item
= caret
;
1950 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1951 if (descr
->style
& LBS_NOTIFY
)
1953 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1955 /* make sure that combo parent doesn't hide us */
1956 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1958 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1965 /***********************************************************************
1966 * LISTBOX_HandleChar
1968 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
1972 char str
[2] = { wParam
& 0xff, '\0' };
1974 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1976 caret
= SendMessage32A( descr
->owner
, WM_CHARTOITEM
,
1977 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1979 if (caret
== -2) return 0;
1982 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
1985 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1986 if (descr
->style
& LBS_NOTIFY
)
1987 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1993 /***********************************************************************
1996 static BOOL32
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
1999 MEASUREITEMSTRUCT32 mis
;
2002 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2004 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2006 HeapFree( GetProcessHeap(), 0, descr
);
2009 GetClientRect32( wnd
->hwndSelf
, &rect
);
2010 descr
->owner
= GetParent32( wnd
->hwndSelf
);
2011 descr
->style
= wnd
->dwStyle
;
2012 descr
->width
= rect
.right
- rect
.left
;
2013 descr
->height
= rect
.bottom
- rect
.top
;
2014 descr
->items
= NULL
;
2015 descr
->nb_items
= 0;
2016 descr
->top_item
= 0;
2017 descr
->selected_item
= -1;
2018 descr
->focus_item
= 0;
2019 descr
->anchor_item
= -1;
2020 descr
->item_height
= 1;
2021 descr
->page_size
= 1;
2022 descr
->column_width
= 150;
2023 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2024 descr
->horz_pos
= 0;
2027 descr
->caret_on
= TRUE
;
2029 descr
->locale
= 0; /* FIXME */
2034 TRACE(combo
,"[%04x]: resetting owner %04x -> %04x\n",
2035 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2036 descr
->owner
= lphc
->self
->hwndSelf
;
2039 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2041 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2043 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2044 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2045 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2046 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2048 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2050 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2052 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2053 descr
->item_height
= lphc
->RectButton
.bottom
- lphc
->RectButton
.top
- 6;
2057 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2059 mis
.CtlType
= ODT_LISTBOX
;
2064 mis
.itemHeight
= descr
->item_height
;
2065 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2066 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2074 /***********************************************************************
2077 static BOOL32
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2079 LISTBOX_ResetContent( wnd
, descr
);
2080 HeapDestroy( descr
->heap
);
2081 HeapFree( GetProcessHeap(), 0, descr
);
2087 /***********************************************************************
2090 LRESULT WINAPI
ListBoxWndProc( HWND32 hwnd
, UINT32 msg
,
2091 WPARAM32 wParam
, LPARAM lParam
)
2095 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2098 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2100 if (msg
== WM_CREATE
)
2102 if (!LISTBOX_Create( wnd
, NULL
)) return -1;
2103 TRACE(listbox
, "creating wnd=%04x descr=%p\n",
2104 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2107 /* Ignore all other messages before we get a WM_CREATE */
2108 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2111 TRACE(listbox
, "[%04x]: msg %s wp %08x lp %08lx\n",
2112 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2115 case LB_RESETCONTENT16
:
2116 case LB_RESETCONTENT32
:
2117 LISTBOX_ResetContent( wnd
, descr
);
2120 case LB_ADDSTRING16
:
2121 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2123 case LB_ADDSTRING32
:
2124 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2125 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2127 case LB_INSERTSTRING16
:
2128 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2129 wParam
= (INT32
)(INT16
)wParam
;
2131 case LB_INSERTSTRING32
:
2132 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2135 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2138 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2139 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2141 case LB_DELETESTRING16
:
2142 case LB_DELETESTRING32
:
2143 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2145 case LB_GETITEMDATA16
:
2146 case LB_GETITEMDATA32
:
2147 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2149 return descr
->items
[wParam
].data
;
2151 case LB_SETITEMDATA16
:
2152 case LB_SETITEMDATA32
:
2153 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2155 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2160 return descr
->nb_items
;
2163 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2166 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2168 case LB_GETTEXTLEN16
:
2170 case LB_GETTEXTLEN32
:
2171 if (wParam
>= descr
->nb_items
) return LB_ERR
;
2172 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2175 case LB_GETCURSEL16
:
2176 case LB_GETCURSEL32
:
2177 return descr
->selected_item
;
2179 case LB_GETTOPINDEX16
:
2180 case LB_GETTOPINDEX32
:
2181 return descr
->top_item
;
2183 case LB_GETITEMHEIGHT16
:
2184 case LB_GETITEMHEIGHT32
:
2185 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2187 case LB_SETITEMHEIGHT16
:
2188 lParam
= LOWORD(lParam
);
2190 case LB_SETITEMHEIGHT32
:
2191 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2193 case LB_ITEMFROMPOINT32
:
2195 POINT32 pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2196 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2197 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2198 PtInRect32( &rect
, pt
) );
2201 case LB_SETCARETINDEX16
:
2202 case LB_SETCARETINDEX32
:
2203 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2205 case LB_GETCARETINDEX16
:
2206 case LB_GETCARETINDEX32
:
2207 return descr
->focus_item
;
2209 case LB_SETTOPINDEX16
:
2210 case LB_SETTOPINDEX32
:
2211 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2213 case LB_SETCOLUMNWIDTH16
:
2214 case LB_SETCOLUMNWIDTH32
:
2215 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2217 case LB_GETITEMRECT16
:
2220 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2221 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2225 case LB_GETITEMRECT32
:
2226 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT32
*)lParam
);
2228 case LB_FINDSTRING16
:
2229 wParam
= (INT32
)(INT16
)wParam
;
2230 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2232 case LB_FINDSTRING32
:
2233 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2235 case LB_FINDSTRINGEXACT16
:
2236 wParam
= (INT32
)(INT16
)wParam
;
2237 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2239 case LB_FINDSTRINGEXACT32
:
2240 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2242 case LB_SELECTSTRING16
:
2243 wParam
= (INT32
)(INT16
)wParam
;
2244 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2246 case LB_SELECTSTRING32
:
2248 INT32 index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2249 (LPCSTR
)lParam
, FALSE
);
2250 if (index
== LB_ERR
) return LB_ERR
;
2251 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2256 wParam
= (INT32
)(INT16
)wParam
;
2259 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2261 return descr
->items
[wParam
].selected
;
2264 lParam
= (INT32
)(INT16
)lParam
;
2267 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2269 case LB_SETCURSEL16
:
2270 wParam
= (INT32
)(INT16
)wParam
;
2272 case LB_SETCURSEL32
:
2273 if (wParam
!= -1) LISTBOX_MakeItemVisible( wnd
, descr
, wParam
, TRUE
);
2274 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2276 case LB_GETSELCOUNT16
:
2277 case LB_GETSELCOUNT32
:
2278 return LISTBOX_GetSelCount( wnd
, descr
);
2280 case LB_GETSELITEMS16
:
2281 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2282 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2284 case LB_GETSELITEMS32
:
2285 return LISTBOX_GetSelItems32( wnd
, descr
, wParam
, (LPINT32
)lParam
);
2287 case LB_SELITEMRANGE16
:
2288 case LB_SELITEMRANGE32
:
2289 if (LOWORD(lParam
) <= HIWORD(lParam
))
2290 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2291 HIWORD(lParam
), wParam
);
2293 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2294 LOWORD(lParam
), wParam
);
2296 case LB_SELITEMRANGEEX16
:
2297 case LB_SELITEMRANGEEX32
:
2298 if ((INT32
)lParam
>= (INT32
)wParam
)
2299 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2301 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2303 case LB_GETHORIZONTALEXTENT16
:
2304 case LB_GETHORIZONTALEXTENT32
:
2305 return descr
->horz_extent
;
2307 case LB_SETHORIZONTALEXTENT16
:
2308 case LB_SETHORIZONTALEXTENT32
:
2309 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2311 case LB_GETANCHORINDEX16
:
2312 case LB_GETANCHORINDEX32
:
2313 return descr
->anchor_item
;
2315 case LB_SETANCHORINDEX16
:
2316 wParam
= (INT32
)(INT16
)wParam
;
2318 case LB_SETANCHORINDEX32
:
2319 if (((INT32
)wParam
< -1) || ((INT32
)wParam
>= descr
->nb_items
))
2321 descr
->anchor_item
= (INT32
)wParam
;
2325 return LISTBOX_Directory( wnd
, descr
, wParam
,
2326 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2329 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2331 case LB_GETLOCALE32
:
2332 return descr
->locale
;
2334 case LB_SETLOCALE32
:
2335 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2338 case LB_INITSTORAGE32
:
2339 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2342 return LISTBOX_SetCount( wnd
, descr
, (INT32
)wParam
);
2344 case LB_SETTABSTOPS16
:
2345 return LISTBOX_SetTabStops( wnd
, descr
, (INT32
)(INT16
)wParam
,
2346 (LPINT32
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2348 case LB_SETTABSTOPS32
:
2349 return LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2350 (LPINT32
)lParam
, FALSE
);
2354 if (descr
->caret_on
) return LB_OKAY
;
2355 descr
->caret_on
= TRUE
;
2356 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2357 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2362 if (!descr
->caret_on
) return LB_OKAY
;
2363 descr
->caret_on
= FALSE
;
2364 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2365 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2369 return LISTBOX_Destroy( wnd
, descr
);
2372 InvalidateRect32( hwnd
, NULL
, TRUE
);
2376 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2380 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2385 HDC32 hdc
= ( wParam
) ? ((HDC32
)wParam
)
2386 : BeginPaint32( hwnd
, &ps
);
2387 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2388 if( !wParam
) EndPaint32( hwnd
, &ps
);
2393 LISTBOX_UpdateSize( wnd
, descr
);
2400 LISTBOX_SetFont( wnd
, descr
, (HFONT32
)wParam
);
2401 if (lParam
) InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
2405 descr
->caret_on
= TRUE
;
2406 if (descr
->focus_item
!= -1)
2407 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2408 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2412 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2413 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2414 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2418 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2421 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2423 case WM_LBUTTONDOWN
:
2424 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2425 (INT16
)LOWORD(lParam
),
2426 (INT16
)HIWORD(lParam
) );
2428 case WM_LBUTTONDBLCLK
:
2429 if (descr
->style
& LBS_NOTIFY
)
2430 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2434 if (GetCapture32() == hwnd
)
2435 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2436 (INT16
)HIWORD(lParam
) );
2440 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2443 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2446 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2449 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2452 if (IS_OWNERDRAW(descr
))
2454 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2455 HBRUSH32 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
2456 wParam
, (LPARAM
)wnd
->hwndSelf
);
2457 if (hbrush
) FillRect32( (HDC32
)wParam
, &rect
, hbrush
);
2463 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2467 case WM_QUERYDROPOBJECT
:
2472 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2473 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2475 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2480 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2481 WARN(listbox
, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2482 hwnd
, msg
, wParam
, lParam
);
2483 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2488 /***********************************************************************
2491 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT32 attrib
, LPSTR dir
, BOOL32 bLong
)
2493 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2497 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2500 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2502 RedrawWindow32( lphc
->self
->hwndSelf
, NULL
, 0,
2503 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2510 /***********************************************************************
2513 * NOTE: in Windows, winproc address of the ComboLBox is the same
2514 * as that of the Listbox.
2516 LRESULT WINAPI
ComboLBWndProc( HWND32 hwnd
, UINT32 msg
,
2517 WPARAM32 wParam
, LPARAM lParam
)
2520 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2524 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2526 TRACE(combo
, "[%04x]: msg %s wp %08x lp %08lx\n",
2527 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2529 if( descr
|| msg
== WM_CREATE
)
2531 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2536 #define lpcs ((LPCREATESTRUCT32A)lParam)
2537 TRACE(combo
, "\tpassed parent handle = 0x%08x\n",
2538 (UINT32
)lpcs
->lpCreateParams
);
2540 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2542 return LISTBOX_Create( wnd
, lphc
);
2544 case WM_LBUTTONDOWN
:
2545 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2546 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2548 /* avoid activation at all costs */
2550 case WM_MOUSEACTIVATE
:
2551 return MA_NOACTIVATE
;
2557 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2559 /* for some reason(?) Windows makes it possible to
2560 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2562 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2563 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2564 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2566 COMBO_FlipListbox( lphc
, FALSE
);
2570 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2573 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2578 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2581 lRet
= DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2583 TRACE(combo
,"\t default on msg [%04x]\n", (UINT16
)msg
);