4 * Copyright 1996 Alexandre Julliard
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 printf( "Listbox:\n" );
105 printf( "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 printf( "%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
& LBS_NOREDRAW
) return;
176 info
.cbSize
= sizeof(info
);
178 if (descr
->style
& LBS_MULTICOLUMN
)
181 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
182 info
.nPos
= descr
->top_item
/ descr
->page_size
;
183 info
.nPage
= descr
->width
/ descr
->column_width
;
184 if (info
.nPage
< 1) info
.nPage
= 1;
185 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
186 if (descr
->style
& LBS_DISABLENOSCROLL
)
187 info
.fMask
|= SIF_DISABLENOSCROLL
;
188 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
190 info
.fMask
= SIF_RANGE
;
191 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
196 info
.nMax
= descr
->nb_items
- 1;
197 info
.nPos
= descr
->top_item
;
198 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
199 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
200 if (descr
->style
& LBS_DISABLENOSCROLL
)
201 info
.fMask
|= SIF_DISABLENOSCROLL
;
202 SetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
204 if (descr
->horz_extent
)
207 info
.nMax
= descr
->horz_extent
- 1;
208 info
.nPos
= descr
->horz_pos
;
209 info
.nPage
= descr
->width
;
210 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
211 if (descr
->style
& LBS_DISABLENOSCROLL
)
212 info
.fMask
|= SIF_DISABLENOSCROLL
;
213 SetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
219 /***********************************************************************
222 * Set the top item of the listbox, scrolling up or down if necessary.
224 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
227 INT32 max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
228 if (index
> max
) index
= max
;
229 if (index
< 0) index
= 0;
230 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
231 if (descr
->top_item
== index
) return LB_OKAY
;
232 if (descr
->style
& LBS_MULTICOLUMN
)
234 INT32 diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
235 if (scroll
&& (abs(diff
) < descr
->width
))
236 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
237 SW_INVALIDATE
| SW_ERASE
);
244 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
248 if (index
> descr
->top_item
)
250 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
251 diff
-= descr
->items
[i
].height
;
255 for (i
= index
; i
< descr
->top_item
; i
++)
256 diff
+= descr
->items
[i
].height
;
260 diff
= (descr
->top_item
- index
) * descr
->item_height
;
262 if (abs(diff
) < descr
->height
)
263 ScrollWindowEx32( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
264 SW_INVALIDATE
| SW_ERASE
);
268 if (!scroll
) InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
269 descr
->top_item
= index
;
270 LISTBOX_UpdateScroll( wnd
, descr
);
275 /***********************************************************************
278 * Update the page size. Should be called when the size of
279 * the client area or the item height changes.
281 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
285 if ((page_size
= descr
->height
/ descr
->item_height
) < 1) page_size
= 1;
286 if (page_size
== descr
->page_size
) return;
287 descr
->page_size
= page_size
;
288 if (descr
->style
& LBS_MULTICOLUMN
)
289 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
290 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
294 /***********************************************************************
297 * Update the size of the listbox. Should be called when the size of
298 * the client area changes.
300 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
304 GetClientRect32( wnd
->hwndSelf
, &rect
);
305 descr
->width
= rect
.right
- rect
.left
;
306 descr
->height
= rect
.bottom
- rect
.top
;
307 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
))
309 if ((descr
->height
> descr
->item_height
) &&
310 (descr
->height
% descr
->item_height
))
312 dprintf_listbox(stddeb
, "Listbox %04x: changing height %d -> %d\n",
313 wnd
->hwndSelf
, descr
->height
,
314 descr
->height
- descr
->height
%descr
->item_height
);
315 SetWindowPos32( wnd
->hwndSelf
, 0, 0, 0,
316 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
317 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
318 (descr
->height
% descr
->item_height
),
319 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
323 dprintf_listbox( stddeb
, "Listbox %04x: new size = %d,%d\n",
324 wnd
->hwndSelf
, descr
->width
, descr
->height
);
325 LISTBOX_UpdatePage( wnd
, descr
);
326 LISTBOX_UpdateScroll( wnd
, descr
);
330 /***********************************************************************
331 * LISTBOX_GetItemRect
333 * Get the rectangle enclosing an item, in listbox client coordinates.
334 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
336 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
339 /* Index <= 0 is legal even on empty listboxes */
340 if (index
&& (index
>= descr
->nb_items
)) return -1;
341 SetRect32( rect
, 0, 0, descr
->width
, descr
->height
);
342 if (descr
->style
& LBS_MULTICOLUMN
)
344 INT32 col
= (index
/ descr
->page_size
) -
345 (descr
->top_item
/ descr
->page_size
);
346 rect
->left
+= col
* descr
->column_width
;
347 rect
->right
= rect
->left
+ descr
->column_width
;
348 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
349 rect
->bottom
= rect
->top
+ descr
->item_height
;
351 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
354 rect
->right
+= descr
->horz_pos
;
355 if ((index
>= 0) && (index
< descr
->nb_items
))
357 if (index
< descr
->top_item
)
359 for (i
= descr
->top_item
-1; i
>= index
; i
--)
360 rect
->top
-= descr
->items
[i
].height
;
364 for (i
= descr
->top_item
; i
< index
; i
++)
365 rect
->top
+= descr
->items
[i
].height
;
367 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
373 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
374 rect
->bottom
= rect
->top
+ descr
->item_height
;
375 rect
->right
+= descr
->horz_pos
;
378 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
379 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
383 /***********************************************************************
384 * LISTBOX_GetItemFromPoint
386 * Return the item nearest from point (x,y) (in client coordinates).
388 static INT32
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
391 INT32 index
= descr
->top_item
;
393 if (!descr
->nb_items
) return -1; /* No items */
394 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
399 while (index
< descr
->nb_items
)
401 if ((pos
+= descr
->items
[index
].height
) > y
) break;
410 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
414 else if (descr
->style
& LBS_MULTICOLUMN
)
416 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
417 if (y
>= 0) index
+= y
/ descr
->item_height
;
418 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
419 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
423 index
+= (y
/ descr
->item_height
);
425 if (index
< 0) return 0;
426 if (index
>= descr
->nb_items
) return -1;
431 /***********************************************************************
436 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
,
437 const RECT32
*rect
, INT32 index
, UINT32 action
)
439 LB_ITEMDATA
*item
= NULL
;
440 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
442 if (IS_OWNERDRAW(descr
))
444 DRAWITEMSTRUCT32 dis
;
445 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
447 dis
.CtlType
= ODT_LISTBOX
;
449 dis
.hwndItem
= wnd
->hwndSelf
;
450 dis
.itemAction
= action
;
454 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
455 if ((descr
->focus_item
== index
) &&
457 (GetFocus32() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
458 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
459 dis
.itemData
= item
? item
->data
: 0;
461 dprintf_listbox( stddeb
, "Listbox %04x: drawitem %d (%s) action=%02x "
462 "state=%02x rect=%d,%d-%d,%d\n",
463 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
464 dis
.itemState
, rect
->left
, rect
->top
,
465 rect
->right
, rect
->bottom
);
466 SendMessage32A(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
470 COLORREF oldText
= 0, oldBk
= 0;
472 if (action
== ODA_FOCUS
)
474 DrawFocusRect32( hdc
, rect
);
477 if (item
&& item
->selected
)
479 oldBk
= SetBkColor32( hdc
, GetSysColor32( COLOR_HIGHLIGHT
) );
480 oldText
= SetTextColor32( hdc
, GetSysColor32(COLOR_HIGHLIGHTTEXT
));
483 dprintf_listbox( stddeb
, "Listbox %04x: painting %d (%s) action=%02x "
484 "rect=%d,%d-%d,%d\n",
485 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
486 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
488 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
489 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
490 else if (!(descr
->style
& LBS_USETABSTOPS
))
491 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
492 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
493 strlen(item
->str
), NULL
);
495 TabbedTextOut32A( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
496 item
->str
, strlen(item
->str
),
497 descr
->nb_tabs
, descr
->tabs
, 0);
498 if (item
&& item
->selected
)
500 SetBkColor32( hdc
, oldBk
);
501 SetTextColor32( hdc
, oldText
);
503 if ((descr
->focus_item
== index
) &&
505 (GetFocus32() == wnd
->hwndSelf
)) DrawFocusRect32( hdc
, rect
);
510 /***********************************************************************
513 * Change the redraw flag.
515 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL32 on
)
519 if (!(descr
->style
& LBS_NOREDRAW
)) return;
520 descr
->style
&= ~LBS_NOREDRAW
;
521 LISTBOX_UpdateScroll( wnd
, descr
);
523 else descr
->style
|= LBS_NOREDRAW
;
527 /***********************************************************************
528 * LISTBOX_RepaintItem
530 * Repaint a single item synchronously.
532 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
538 HBRUSH32 hbrush
, oldBrush
= 0;
540 if (descr
->style
& LBS_NOREDRAW
) return;
541 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
542 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
543 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
544 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
545 hdc
, (LPARAM
)wnd
->hwndSelf
);
546 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
547 if (wnd
->dwStyle
& WS_DISABLED
)
548 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
549 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
550 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
551 if (oldFont
) SelectObject32( hdc
, oldFont
);
552 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
553 ReleaseDC32( wnd
->hwndSelf
, hdc
);
557 /***********************************************************************
558 * LISTBOX_InitStorage
560 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT32 nb_items
,
565 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
566 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
568 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
569 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
570 nb_items
* sizeof(LB_ITEMDATA
) )))
572 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
580 /***********************************************************************
581 * LISTBOX_SetTabStops
583 static BOOL32
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT32 count
,
584 LPINT32 tabs
, BOOL32 short_ints
)
586 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
587 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
588 if (!(descr
->nb_tabs
= count
))
593 /* FIXME: count = 1 */
594 if (!(descr
->tabs
= (INT32
*)HeapAlloc( descr
->heap
, 0,
595 descr
->nb_tabs
* sizeof(INT32
) )))
600 LPINT16 p
= (LPINT16
)tabs
;
601 dprintf_listbox( stddeb
, "Listbox %04x: settabstops ", wnd
->hwndSelf
);
602 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
603 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
604 dprintf_listbox( stddeb
, "%hd ", descr
->tabs
[i
]);
606 dprintf_listbox( stddeb
, "\n");
608 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT32
) );
609 /* FIXME: repaint the window? */
614 /***********************************************************************
617 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
620 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
621 if (HAS_STRINGS(descr
))
623 lstrcpy32A( buffer
, descr
->items
[index
].str
);
624 return strlen(buffer
);
628 memcpy( buffer
, &descr
->items
[index
].data
, sizeof(DWORD
) );
629 return sizeof(DWORD
);
634 /***********************************************************************
635 * LISTBOX_FindStringPos
637 * Find the nearest string located before a given string in sort order.
638 * If 'exact' is TRUE, return an error if we don't get an exact match.
640 static INT32
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
643 INT32 index
, min
, max
, res
= -1;
645 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
647 max
= descr
->nb_items
;
650 index
= (min
+ max
) / 2;
651 if (HAS_STRINGS(descr
))
652 res
= lstrcmpi32A( descr
->items
[index
].str
, str
);
655 COMPAREITEMSTRUCT32 cis
;
656 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
658 cis
.CtlType
= ODT_LISTBOX
;
660 cis
.hwndItem
= wnd
->hwndSelf
;
662 cis
.itemData1
= descr
->items
[index
].data
;
664 cis
.itemData2
= (DWORD
)str
;
665 cis
.dwLocaleId
= descr
->locale
;
666 res
= SendMessage32A( descr
->owner
, WM_COMPAREITEM
,
669 if (!res
) return index
;
670 if (res
> 0) max
= index
;
671 else min
= index
+ 1;
673 return exact
? -1 : max
;
677 /***********************************************************************
678 * LISTBOX_FindFileStrPos
680 * Find the nearest string located before a given string in directory
681 * sort order (i.e. first files, then directories, then drives).
683 static INT32
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
685 INT32 min
, max
, res
= -1;
687 if (!HAS_STRINGS(descr
))
688 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
690 max
= descr
->nb_items
;
693 INT32 index
= (min
+ max
) / 2;
694 const char *p
= descr
->items
[index
].str
;
695 if (*p
== '[') /* drive or directory */
697 if (*str
!= '[') res
= -1;
698 else if (p
[1] == '-') /* drive */
700 if (str
[1] == '-') res
= str
[2] - p
[2];
705 if (str
[1] == '-') res
= 1;
706 else res
= lstrcmpi32A( str
, p
);
711 if (*str
== '[') res
= 1;
712 else res
= lstrcmpi32A( str
, p
);
714 if (!res
) return index
;
715 if (res
< 0) max
= index
;
716 else min
= index
+ 1;
722 /***********************************************************************
725 * Find the item beginning with a given string.
727 static INT32
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT32 start
,
728 LPCSTR str
, BOOL32 exact
)
733 if (start
>= descr
->nb_items
) start
= -1;
734 item
= descr
->items
+ start
+ 1;
735 if (HAS_STRINGS(descr
))
737 if (!str
) return LB_ERR
;
740 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
741 if (!lstrcmpi32A( str
, item
->str
)) return i
;
742 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
743 if (!lstrcmpi32A( str
, item
->str
)) return i
;
747 /* Special case for drives and directories: ignore prefix */
748 #define CHECK_DRIVE(item) \
749 if ((item)->str[0] == '[') \
751 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
752 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
756 INT32 len
= strlen(str
);
757 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
759 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
762 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
764 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
772 if (exact
&& (descr
->style
& LBS_SORT
))
773 /* If sorted, use a WM_COMPAREITEM binary search */
774 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
776 /* Otherwise use a linear search */
777 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
778 if (item
->data
== (DWORD
)str
) return i
;
779 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
780 if (item
->data
== (DWORD
)str
) return i
;
786 /***********************************************************************
787 * LISTBOX_GetSelCount
789 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
792 LB_ITEMDATA
*item
= descr
->items
;
794 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
795 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
796 if (item
->selected
) count
++;
801 /***********************************************************************
802 * LISTBOX_GetSelItems16
804 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
808 LB_ITEMDATA
*item
= descr
->items
;
810 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
811 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
812 if (item
->selected
) array
[count
++] = (INT16
)i
;
817 /***********************************************************************
818 * LISTBOX_GetSelItems32
820 static LRESULT
LISTBOX_GetSelItems32( WND
*wnd
, LB_DESCR
*descr
, INT32 max
,
824 LB_ITEMDATA
*item
= descr
->items
;
826 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
827 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
828 if (item
->selected
) array
[count
++] = i
;
833 /***********************************************************************
836 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
)
838 INT32 i
, col_pos
= descr
->page_size
- 1;
841 HBRUSH32 hbrush
, oldBrush
= 0;
843 SetRect32( &rect
, 0, 0, descr
->width
, descr
->height
);
844 if (descr
->style
& LBS_NOREDRAW
) return 0;
845 if (descr
->style
& LBS_MULTICOLUMN
)
846 rect
.right
= rect
.left
+ descr
->column_width
;
847 else if (descr
->horz_pos
)
849 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
850 rect
.right
+= descr
->horz_pos
;
853 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
854 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
855 hdc
, (LPARAM
)wnd
->hwndSelf
);
856 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
857 if (wnd
->dwStyle
& WS_DISABLED
)
858 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
860 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
861 (GetFocus32() == wnd
->hwndSelf
))
863 /* Special case for empty listbox: paint focus rect */
864 rect
.bottom
= rect
.top
+ descr
->item_height
;
865 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
867 rect
.top
= rect
.bottom
;
870 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
872 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
873 rect
.bottom
= rect
.top
+ descr
->item_height
;
875 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
877 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
878 rect
.top
= rect
.bottom
;
880 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
882 if (!IS_OWNERDRAW(descr
))
884 /* Clear the bottom of the column */
885 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
886 if (rect
.top
< descr
->height
)
888 rect
.bottom
= descr
->height
;
889 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
890 &rect
, NULL
, 0, NULL
);
894 /* Go to the next column */
895 rect
.left
+= descr
->column_width
;
896 rect
.right
+= descr
->column_width
;
898 col_pos
= descr
->page_size
- 1;
903 if (rect
.top
>= descr
->height
) break;
907 if (!IS_OWNERDRAW(descr
))
909 /* Clear the remainder of the client area */
910 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
911 if (rect
.top
< descr
->height
)
913 rect
.bottom
= descr
->height
;
914 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
915 &rect
, NULL
, 0, NULL
);
917 if (rect
.right
< descr
->width
)
919 rect
.left
= rect
.right
;
920 rect
.right
= descr
->width
;
922 rect
.bottom
= descr
->height
;
923 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
924 &rect
, NULL
, 0, NULL
);
927 if (oldFont
) SelectObject32( hdc
, oldFont
);
928 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
933 /***********************************************************************
934 * LISTBOX_InvalidateItems
936 * Invalidate all items from a given item. If the specified item is not
937 * visible, nothing happens.
939 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
943 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
945 rect
.bottom
= descr
->height
;
946 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
947 if (descr
->style
& LBS_MULTICOLUMN
)
949 /* Repaint the other columns */
950 rect
.left
= rect
.right
;
951 rect
.right
= descr
->width
;
953 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
959 /***********************************************************************
960 * LISTBOX_GetItemHeight
962 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
964 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
966 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
967 return descr
->items
[index
].height
;
969 else return descr
->item_height
;
973 /***********************************************************************
974 * LISTBOX_SetItemHeight
976 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
979 if (!height
) height
= 1;
981 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
983 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
984 dprintf_listbox( stddeb
, "Listbox %04x: item %d height = %d\n",
985 wnd
->hwndSelf
, index
, height
);
986 descr
->items
[index
].height
= height
;
987 LISTBOX_UpdateScroll( wnd
, descr
);
988 LISTBOX_InvalidateItems( wnd
, descr
, index
);
990 else if (height
!= descr
->item_height
)
992 dprintf_listbox( stddeb
, "Listbox %04x: new height = %d\n",
993 wnd
->hwndSelf
, height
);
994 descr
->item_height
= height
;
995 LISTBOX_UpdatePage( wnd
, descr
);
996 LISTBOX_UpdateScroll( wnd
, descr
);
997 InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
1003 /***********************************************************************
1004 * LISTBOX_SetHorizontalPos
1006 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT32 pos
)
1010 if (pos
> descr
->horz_extent
- descr
->width
)
1011 pos
= descr
->horz_extent
- descr
->width
;
1012 if (pos
< 0) pos
= 0;
1013 if (!(diff
= descr
->horz_pos
- pos
)) return;
1014 dprintf_listbox( stddeb
, "Listbox %04x: new horz pos = %d\n",
1015 wnd
->hwndSelf
, pos
);
1016 descr
->horz_pos
= pos
;
1017 LISTBOX_UpdateScroll( wnd
, descr
);
1018 if (abs(diff
) < descr
->width
)
1019 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1020 SW_INVALIDATE
| SW_ERASE
);
1022 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1026 /***********************************************************************
1027 * LISTBOX_SetHorizontalExtent
1029 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1032 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1034 if (extent
<= 0) extent
= 1;
1035 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1036 dprintf_listbox( stddeb
, "Listbox %04x: new horz extent = %d\n",
1037 wnd
->hwndSelf
, extent
);
1038 descr
->horz_extent
= extent
;
1039 if (descr
->horz_pos
> extent
- descr
->width
)
1040 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1042 LISTBOX_UpdateScroll( wnd
, descr
);
1047 /***********************************************************************
1048 * LISTBOX_SetColumnWidth
1050 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT32 width
)
1052 width
+= 2; /* For left and right margin */
1053 if (width
== descr
->column_width
) return LB_OKAY
;
1054 dprintf_listbox( stddeb
, "Listbox %04x: new column width = %d\n",
1055 wnd
->hwndSelf
, width
);
1056 descr
->column_width
= width
;
1057 LISTBOX_UpdatePage( wnd
, descr
);
1062 /***********************************************************************
1065 * Returns the item height.
1067 static INT32
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT32 font
)
1070 HFONT32 oldFont
= 0;
1075 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1077 fprintf( stderr
, "LISTBOX_SetFont: unable to get DC\n" );
1080 if (font
) oldFont
= SelectObject32( hdc
, font
);
1081 GetTextMetrics32A( hdc
, &tm
);
1082 if (oldFont
) SelectObject32( hdc
, oldFont
);
1083 ReleaseDC32( wnd
->hwndSelf
, hdc
);
1084 if (!IS_OWNERDRAW(descr
))
1085 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
+ 2 );
1086 return tm
.tmHeight
+ 2;
1090 /***********************************************************************
1091 * LISTBOX_MakeItemVisible
1093 * Make sure that a given item is partially or fully visible.
1095 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1100 if (index
<= descr
->top_item
) top
= index
;
1101 else if (descr
->style
& LBS_MULTICOLUMN
)
1103 INT32 cols
= descr
->width
;
1104 if (!fully
) cols
+= descr
->column_width
- 1;
1105 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1107 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1108 top
= index
- descr
->page_size
* (cols
- 1);
1110 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1112 INT32 height
= fully
? descr
->items
[index
].height
: 1;
1113 for (top
= index
; top
> descr
->top_item
; top
--)
1114 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1118 if (index
< descr
->top_item
+ descr
->page_size
) return;
1119 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1120 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1121 top
= index
- descr
->page_size
+ 1;
1123 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1127 /***********************************************************************
1128 * LISTBOX_SelectItemRange
1130 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1132 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT32 first
,
1133 INT32 last
, BOOL32 on
)
1137 /* A few sanity checks */
1139 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1140 if (last
== -1) last
= descr
->nb_items
- 1;
1141 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1142 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1143 /* selected_item reflects last selected/unselected item on multiple sel */
1144 descr
->selected_item
= last
;
1146 if (on
) /* Turn selection on */
1148 for (i
= first
; i
<= last
; i
++)
1150 if (descr
->items
[i
].selected
) continue;
1151 descr
->items
[i
].selected
= TRUE
;
1152 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1155 else /* Turn selection off */
1157 for (i
= first
; i
<= last
; i
++)
1159 if (!descr
->items
[i
].selected
) continue;
1160 descr
->items
[i
].selected
= FALSE
;
1161 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1168 /***********************************************************************
1169 * LISTBOX_SetCaretIndex
1171 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1172 BOOL32 fully_visible
)
1174 INT32 oldfocus
= descr
->focus_item
;
1176 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1177 if (index
== oldfocus
) return LB_OKAY
;
1178 descr
->focus_item
= index
;
1179 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1180 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1183 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1184 if (descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1185 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1191 /***********************************************************************
1192 * LISTBOX_SetSelection
1194 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1195 BOOL32 on
, BOOL32 send_notify
)
1197 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1198 if (descr
->style
& LBS_MULTIPLESEL
)
1200 if (index
== -1) /* Select all items */
1201 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1202 else /* Only one item */
1203 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1207 INT32 oldsel
= descr
->selected_item
;
1208 if (index
== oldsel
) return LB_OKAY
;
1209 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1210 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1211 descr
->selected_item
= index
;
1212 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1213 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1214 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1215 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1221 /***********************************************************************
1224 * Change the caret position and extend the selection to the new caret.
1226 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1227 BOOL32 fully_visible
)
1229 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1230 if (descr
->style
& LBS_EXTENDEDSEL
)
1232 if (descr
->anchor_item
!= -1)
1234 INT32 first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1235 INT32 last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1237 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1238 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1239 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1242 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1244 /* Set selection to new caret item */
1245 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1250 /***********************************************************************
1251 * LISTBOX_InsertItem
1253 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1254 LPSTR str
, DWORD data
)
1259 if (index
== -1) index
= descr
->nb_items
;
1260 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1261 if (!descr
->items
) max_items
= 0;
1262 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1263 if (descr
->nb_items
== max_items
)
1265 /* We need to grow the array */
1266 max_items
+= LB_ARRAY_GRANULARITY
;
1267 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1268 max_items
* sizeof(LB_ITEMDATA
) )))
1270 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1273 descr
->items
= item
;
1276 /* Insert the item structure */
1278 item
= &descr
->items
[index
];
1279 if (index
< descr
->nb_items
)
1280 RtlMoveMemory( item
+ 1, item
,
1281 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1285 item
->selected
= FALSE
;
1288 /* Get item height */
1290 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1292 MEASUREITEMSTRUCT32 mis
;
1293 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1295 mis
.CtlType
= ODT_LISTBOX
;
1298 mis
.itemData
= descr
->items
[index
].data
;
1299 mis
.itemHeight
= descr
->item_height
;
1300 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1301 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1302 dprintf_listbox( stddeb
, "Listbox %04x: measure item %d (%s) = %d\n",
1303 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1306 /* Repaint the items */
1308 LISTBOX_UpdateScroll( wnd
, descr
);
1309 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1311 /* Move selection and focused item */
1313 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1314 if (index
<= descr
->focus_item
)
1316 descr
->focus_item
++;
1317 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
- 1, FALSE
);
1320 /* If listbox was empty, set focus to the first item */
1322 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1327 /***********************************************************************
1328 * LISTBOX_InsertString
1330 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1333 LPSTR new_str
= NULL
;
1337 if (HAS_STRINGS(descr
))
1339 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1341 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1345 else data
= (DWORD
)str
;
1347 if (index
== -1) index
= descr
->nb_items
;
1348 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1350 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1354 dprintf_listbox( stddeb
, "Listbox %04x: added item %d '%s'\n",
1355 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1360 /***********************************************************************
1361 * LISTBOX_DeleteItem
1363 * Delete the content of an item. 'index' must be a valid index.
1365 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1367 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1368 * while Win95 sends it for all items with user data.
1369 * It's probably better to send it too often than not
1370 * often enough, so this is what we do here.
1372 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1374 DELETEITEMSTRUCT32 dis
;
1375 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1377 dis
.CtlType
= ODT_LISTBOX
;
1380 dis
.hwndItem
= wnd
->hwndSelf
;
1381 dis
.itemData
= descr
->items
[index
].data
;
1382 SendMessage32A( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1384 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1385 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1389 /***********************************************************************
1390 * LISTBOX_RemoveItem
1392 * Remove an item from the listbox and delete its content.
1394 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1399 if (index
== -1) index
= descr
->nb_items
- 1;
1400 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1401 LISTBOX_DeleteItem( wnd
, descr
, index
);
1403 /* Remove the item */
1405 item
= &descr
->items
[index
];
1406 if (index
< descr
->nb_items
-1)
1407 RtlMoveMemory( item
, item
+ 1,
1408 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1410 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1412 /* Shrink the item array if possible */
1414 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1415 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1417 max_items
-= LB_ARRAY_GRANULARITY
;
1418 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1419 max_items
* sizeof(LB_ITEMDATA
) );
1420 if (item
) descr
->items
= item
;
1423 /* Repaint the items */
1425 LISTBOX_UpdateScroll( wnd
, descr
);
1426 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1428 /* Move selection and focused item */
1430 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1431 if (index
<= descr
->focus_item
)
1433 descr
->focus_item
--;
1434 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
+ 1, FALSE
);
1440 /***********************************************************************
1441 * LISTBOX_ResetContent
1443 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1447 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1448 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1449 descr
->nb_items
= 0;
1450 descr
->top_item
= 0;
1451 descr
->selected_item
= -1;
1452 descr
->focus_item
= 0;
1453 descr
->anchor_item
= -1;
1454 descr
->items
= NULL
;
1455 LISTBOX_UpdateScroll( wnd
, descr
);
1456 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1460 /***********************************************************************
1463 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT32 count
)
1467 if (HAS_STRINGS(descr
)) return LB_ERR
;
1468 /* FIXME: this is far from optimal... */
1469 if (count
> descr
->nb_items
)
1471 while (count
> descr
->nb_items
)
1472 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1475 else if (count
< descr
->nb_items
)
1477 while (count
< descr
->nb_items
)
1478 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1485 /***********************************************************************
1488 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT32 attrib
,
1489 LPCSTR filespec
, BOOL32 long_names
)
1492 LRESULT ret
= LB_OKAY
;
1493 WIN32_FIND_DATA32A entry
;
1496 if ((handle
= FindFirstFile32A(filespec
,&entry
)) == INVALID_HANDLE_VALUE32
)
1498 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1505 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1507 if (!(attrib
& DDL_DIRECTORY
) ||
1508 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1509 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1510 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1512 else /* not a directory */
1514 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1515 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1517 if ((attrib
& DDL_EXCLUSIVE
) &&
1518 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1521 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1522 else strcpy( buffer
, entry
.cAlternateFileName
);
1524 if (!long_names
) CharLower32A( buffer
);
1525 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1526 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1528 } while (FindNextFile32A( handle
, &entry
));
1529 FindClose32( handle
);
1532 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1534 char buffer
[] = "[-a-]";
1536 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1538 if (!DRIVE_IsValid(drive
)) continue;
1539 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1547 /***********************************************************************
1548 * LISTBOX_HandleVScroll
1550 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1551 WPARAM32 wParam
, LPARAM lParam
)
1555 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1556 switch(LOWORD(wParam
))
1559 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1562 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1565 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1566 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1569 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1570 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1572 case SB_THUMBPOSITION
:
1573 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1576 info
.cbSize
= sizeof(info
);
1577 info
.fMask
= SIF_TRACKPOS
;
1578 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1579 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1582 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1585 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1592 /***********************************************************************
1593 * LISTBOX_HandleHScroll
1595 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1596 WPARAM32 wParam
, LPARAM lParam
)
1601 if (descr
->style
& LBS_MULTICOLUMN
)
1603 switch(LOWORD(wParam
))
1606 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1610 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1614 page
= descr
->width
/ descr
->column_width
;
1615 if (page
< 1) page
= 1;
1616 LISTBOX_SetTopItem( wnd
, descr
,
1617 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1620 page
= descr
->width
/ descr
->column_width
;
1621 if (page
< 1) page
= 1;
1622 LISTBOX_SetTopItem( wnd
, descr
,
1623 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1625 case SB_THUMBPOSITION
:
1626 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1630 info
.cbSize
= sizeof(info
);
1631 info
.fMask
= SIF_TRACKPOS
;
1632 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1633 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1637 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1640 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1644 else if (descr
->horz_extent
)
1646 switch(LOWORD(wParam
))
1649 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1652 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1655 LISTBOX_SetHorizontalPos( wnd
, descr
,
1656 descr
->horz_pos
- descr
->width
);
1659 LISTBOX_SetHorizontalPos( wnd
, descr
,
1660 descr
->horz_pos
+ descr
->width
);
1662 case SB_THUMBPOSITION
:
1663 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1666 info
.cbSize
= sizeof(info
);
1667 info
.fMask
= SIF_TRACKPOS
;
1668 GetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
);
1669 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1672 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1675 LISTBOX_SetHorizontalPos( wnd
, descr
,
1676 descr
->horz_extent
- descr
->width
);
1684 /***********************************************************************
1685 * LISTBOX_HandleLButtonDown
1687 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1688 WPARAM32 wParam
, INT32 x
, INT32 y
)
1690 INT32 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1691 dprintf_listbox( stddeb
, "Listbox %04x: lbuttondown %d,%d item %d\n",
1692 wnd
->hwndSelf
, x
, y
, index
);
1693 if (!descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
)) return 0;
1696 if (descr
->style
& LBS_EXTENDEDSEL
)
1698 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1699 if (wParam
& MK_CONTROL
)
1701 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1702 LISTBOX_SetSelection( wnd
, descr
, index
,
1703 !descr
->items
[index
].selected
, FALSE
);
1705 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1709 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1710 LISTBOX_SetSelection( wnd
, descr
, index
,
1711 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1712 !descr
->items
[index
].selected
), FALSE
);
1716 if( !descr
->lphc
) SetFocus32( wnd
->hwndSelf
);
1717 else SetFocus32( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1718 : descr
->lphc
->self
->hwndSelf
) ;
1720 SetCapture32( wnd
->hwndSelf
);
1721 if (index
!= -1 && !descr
->lphc
)
1723 if (descr
->style
& LBS_NOTIFY
)
1724 SendMessage32A( descr
->owner
, WM_LBTRACKPOINT
, index
,
1725 MAKELPARAM( x
, y
) );
1726 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1728 POINT32 pt
= { x
, y
};
1729 if (DragDetect32( wnd
->hwndSelf
, pt
))
1730 SendMessage32A( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1737 /***********************************************************************
1738 * LISTBOX_HandleLButtonUp
1740 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1742 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1743 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1744 LISTBOX_Timer
= LB_TIMER_NONE
;
1745 if (GetCapture32() == wnd
->hwndSelf
)
1748 if (descr
->style
& LBS_NOTIFY
)
1749 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1755 /***********************************************************************
1756 * LISTBOX_HandleTimer
1758 * Handle scrolling upon a timer event.
1759 * Return TRUE if scrolling should continue.
1761 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1762 INT32 index
, TIMER_DIRECTION dir
)
1767 if (descr
->top_item
) index
= descr
->top_item
- 1;
1771 if (descr
->top_item
) index
-= descr
->page_size
;
1774 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1775 if (index
== descr
->focus_item
) index
++;
1776 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1778 case LB_TIMER_RIGHT
:
1779 if (index
+ descr
->page_size
< descr
->nb_items
)
1780 index
+= descr
->page_size
;
1785 if (index
== descr
->focus_item
) return FALSE
;
1786 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1791 /***********************************************************************
1792 * LISTBOX_HandleSystemTimer
1794 * WM_SYSTIMER handler.
1796 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1798 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1800 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1801 LISTBOX_Timer
= LB_TIMER_NONE
;
1807 /***********************************************************************
1808 * LISTBOX_HandleMouseMove
1810 * WM_MOUSEMOVE handler.
1812 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1816 TIMER_DIRECTION dir
;
1818 if (descr
->style
& LBS_MULTICOLUMN
)
1821 else if (y
>= descr
->item_height
* descr
->page_size
)
1822 y
= descr
->item_height
* descr
->page_size
- 1;
1826 dir
= LB_TIMER_LEFT
;
1829 else if (x
>= descr
->width
)
1831 dir
= LB_TIMER_RIGHT
;
1832 x
= descr
->width
- 1;
1834 else dir
= LB_TIMER_NONE
; /* inside */
1838 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1839 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1840 else dir
= LB_TIMER_NONE
; /* inside */
1843 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1844 if (index
== -1) index
= descr
->focus_item
;
1845 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1847 /* Start/stop the system timer */
1849 if (dir
!= LB_TIMER_NONE
)
1850 SetSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1851 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1852 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1853 LISTBOX_Timer
= dir
;
1857 /***********************************************************************
1858 * LISTBOX_HandleKeyDown
1860 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM32 wParam
)
1863 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1865 caret
= SendMessage32A( descr
->owner
, WM_VKEYTOITEM
,
1866 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1868 if (caret
== -2) return 0;
1870 if (caret
== -1) switch(wParam
)
1873 if (descr
->style
& LBS_MULTICOLUMN
)
1875 if (descr
->focus_item
>= descr
->page_size
)
1876 caret
= descr
->focus_item
- descr
->page_size
;
1881 caret
= descr
->focus_item
- 1;
1882 if (caret
< 0) caret
= 0;
1885 if (descr
->style
& LBS_MULTICOLUMN
)
1887 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1888 caret
= descr
->focus_item
+ descr
->page_size
;
1893 caret
= descr
->focus_item
+ 1;
1894 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1897 if (descr
->style
& LBS_MULTICOLUMN
)
1899 INT32 page
= descr
->width
/ descr
->column_width
;
1900 if (page
< 1) page
= 1;
1901 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1903 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1904 if (caret
< 0) caret
= 0;
1907 if (descr
->style
& LBS_MULTICOLUMN
)
1909 INT32 page
= descr
->width
/ descr
->column_width
;
1910 if (page
< 1) page
= 1;
1911 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1913 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1914 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1920 caret
= descr
->nb_items
- 1;
1923 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1924 else if (descr
->style
& LBS_MULTIPLESEL
)
1926 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1927 !descr
->items
[descr
->focus_item
].selected
,
1928 (descr
->style
& LBS_NOTIFY
) != 0 );
1930 else if (descr
->selected_item
== -1)
1932 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1933 (descr
->style
& LBS_NOTIFY
) != 0 );
1939 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1940 !(GetKeyState32( VK_SHIFT
) & 0x8000))
1941 descr
->anchor_item
= caret
;
1942 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1943 if (descr
->style
& LBS_NOTIFY
)
1945 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1947 /* make sure that combo parent doesn't hide us */
1948 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1950 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1957 /***********************************************************************
1958 * LISTBOX_HandleChar
1960 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
1964 char str
[2] = { wParam
& 0xff, '\0' };
1966 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1968 caret
= SendMessage32A( descr
->owner
, WM_CHARTOITEM
,
1969 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1971 if (caret
== -2) return 0;
1974 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
1977 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1978 if (descr
->style
& LBS_NOTIFY
)
1979 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1985 /***********************************************************************
1988 static BOOL32
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
1991 MEASUREITEMSTRUCT32 mis
;
1994 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
1996 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
1998 HeapFree( GetProcessHeap(), 0, descr
);
2001 GetClientRect32( wnd
->hwndSelf
, &rect
);
2002 descr
->owner
= GetParent32( wnd
->hwndSelf
);
2003 descr
->style
= wnd
->dwStyle
;
2004 descr
->width
= rect
.right
- rect
.left
;
2005 descr
->height
= rect
.bottom
- rect
.top
;
2006 descr
->items
= NULL
;
2007 descr
->nb_items
= 0;
2008 descr
->top_item
= 0;
2009 descr
->selected_item
= -1;
2010 descr
->focus_item
= 0;
2011 descr
->anchor_item
= -1;
2012 descr
->item_height
= 1;
2013 descr
->page_size
= 1;
2014 descr
->column_width
= 150;
2015 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2016 descr
->horz_pos
= 0;
2019 descr
->caret_on
= TRUE
;
2021 descr
->locale
= 0; /* FIXME */
2026 dprintf_combo(stddeb
,"ComboLBox [%04x]: resetting owner %04x -> %04x\n",
2027 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2028 descr
->owner
= lphc
->self
->hwndSelf
;
2031 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2033 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2035 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2036 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2037 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2038 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2040 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2042 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2044 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2045 descr
->item_height
= lphc
->RectButton
.bottom
- lphc
->RectButton
.top
- 6;
2049 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2051 mis
.CtlType
= ODT_LISTBOX
;
2056 mis
.itemHeight
= descr
->item_height
;
2057 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2058 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2066 /***********************************************************************
2069 static BOOL32
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2071 LISTBOX_ResetContent( wnd
, descr
);
2072 HeapDestroy( descr
->heap
);
2073 HeapFree( GetProcessHeap(), 0, descr
);
2079 /***********************************************************************
2082 LRESULT WINAPI
ListBoxWndProc( HWND32 hwnd
, UINT32 msg
,
2083 WPARAM32 wParam
, LPARAM lParam
)
2087 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2090 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2092 if (msg
== WM_CREATE
)
2094 if (!LISTBOX_Create( wnd
, NULL
)) return -1;
2095 dprintf_listbox( stddeb
, "Listbox: creating wnd=%04x descr=%p\n",
2096 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2099 /* Ignore all other messages before we get a WM_CREATE */
2100 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2103 dprintf_listbox( stddeb
, "Listbox %04x: msg %s wp %08x lp %08lx\n",
2104 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2107 case LB_RESETCONTENT16
:
2108 case LB_RESETCONTENT32
:
2109 LISTBOX_ResetContent( wnd
, descr
);
2112 case LB_ADDSTRING16
:
2113 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2115 case LB_ADDSTRING32
:
2116 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2117 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2119 case LB_INSERTSTRING16
:
2120 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2121 wParam
= (INT32
)(INT16
)wParam
;
2123 case LB_INSERTSTRING32
:
2124 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2127 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2130 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2131 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2133 case LB_DELETESTRING16
:
2134 case LB_DELETESTRING32
:
2135 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2137 case LB_GETITEMDATA16
:
2138 case LB_GETITEMDATA32
:
2139 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2141 return descr
->items
[wParam
].data
;
2143 case LB_SETITEMDATA16
:
2144 case LB_SETITEMDATA32
:
2145 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2147 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2152 return descr
->nb_items
;
2155 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2158 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2160 case LB_GETTEXTLEN16
:
2162 case LB_GETTEXTLEN32
:
2163 if (wParam
>= descr
->nb_items
) return LB_ERR
;
2164 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2167 case LB_GETCURSEL16
:
2168 case LB_GETCURSEL32
:
2169 return descr
->selected_item
;
2171 case LB_GETTOPINDEX16
:
2172 case LB_GETTOPINDEX32
:
2173 return descr
->top_item
;
2175 case LB_GETITEMHEIGHT16
:
2176 case LB_GETITEMHEIGHT32
:
2177 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2179 case LB_SETITEMHEIGHT16
:
2180 lParam
= LOWORD(lParam
);
2182 case LB_SETITEMHEIGHT32
:
2183 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2185 case LB_ITEMFROMPOINT32
:
2187 POINT32 pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2188 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2189 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2190 PtInRect32( &rect
, pt
) );
2193 case LB_SETCARETINDEX16
:
2194 case LB_SETCARETINDEX32
:
2195 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2197 case LB_GETCARETINDEX16
:
2198 case LB_GETCARETINDEX32
:
2199 return descr
->focus_item
;
2201 case LB_SETTOPINDEX16
:
2202 case LB_SETTOPINDEX32
:
2203 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2205 case LB_SETCOLUMNWIDTH16
:
2206 case LB_SETCOLUMNWIDTH32
:
2207 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2209 case LB_GETITEMRECT16
:
2212 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2213 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2217 case LB_GETITEMRECT32
:
2218 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT32
*)lParam
);
2220 case LB_FINDSTRING16
:
2221 wParam
= (INT32
)(INT16
)wParam
;
2222 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2224 case LB_FINDSTRING32
:
2225 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2227 case LB_FINDSTRINGEXACT16
:
2228 wParam
= (INT32
)(INT16
)wParam
;
2229 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2231 case LB_FINDSTRINGEXACT32
:
2232 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2234 case LB_SELECTSTRING16
:
2235 wParam
= (INT32
)(INT16
)wParam
;
2236 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2238 case LB_SELECTSTRING32
:
2240 INT32 index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2241 (LPCSTR
)lParam
, FALSE
);
2242 if (index
== LB_ERR
) return LB_ERR
;
2243 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2248 wParam
= (INT32
)(INT16
)wParam
;
2251 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2253 return descr
->items
[wParam
].selected
;
2256 lParam
= (INT32
)(INT16
)lParam
;
2259 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2261 case LB_SETCURSEL16
:
2262 wParam
= (INT32
)(INT16
)wParam
;
2264 case LB_SETCURSEL32
:
2265 if (wParam
!= -1) LISTBOX_MakeItemVisible( wnd
, descr
, wParam
, TRUE
);
2266 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2268 case LB_GETSELCOUNT16
:
2269 case LB_GETSELCOUNT32
:
2270 return LISTBOX_GetSelCount( wnd
, descr
);
2272 case LB_GETSELITEMS16
:
2273 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2274 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2276 case LB_GETSELITEMS32
:
2277 return LISTBOX_GetSelItems32( wnd
, descr
, wParam
, (LPINT32
)lParam
);
2279 case LB_SELITEMRANGE16
:
2280 case LB_SELITEMRANGE32
:
2281 if (LOWORD(lParam
) <= HIWORD(lParam
))
2282 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2283 HIWORD(lParam
), wParam
);
2285 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2286 LOWORD(lParam
), wParam
);
2288 case LB_SELITEMRANGEEX16
:
2289 case LB_SELITEMRANGEEX32
:
2290 if ((INT32
)lParam
>= (INT32
)wParam
)
2291 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2293 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2295 case LB_GETHORIZONTALEXTENT16
:
2296 case LB_GETHORIZONTALEXTENT32
:
2297 return descr
->horz_extent
;
2299 case LB_SETHORIZONTALEXTENT16
:
2300 case LB_SETHORIZONTALEXTENT32
:
2301 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2303 case LB_GETANCHORINDEX16
:
2304 case LB_GETANCHORINDEX32
:
2305 return descr
->anchor_item
;
2307 case LB_SETANCHORINDEX16
:
2308 wParam
= (INT32
)(INT16
)wParam
;
2310 case LB_SETANCHORINDEX32
:
2311 if (((INT32
)wParam
< -1) || ((INT32
)wParam
>= descr
->nb_items
))
2313 descr
->anchor_item
= (INT32
)wParam
;
2317 return LISTBOX_Directory( wnd
, descr
, wParam
,
2318 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2321 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2323 case LB_GETLOCALE32
:
2324 return descr
->locale
;
2326 case LB_SETLOCALE32
:
2327 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2330 case LB_INITSTORAGE32
:
2331 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2334 return LISTBOX_SetCount( wnd
, descr
, (INT32
)wParam
);
2336 case LB_SETTABSTOPS16
:
2337 return LISTBOX_SetTabStops( wnd
, descr
, (INT32
)(INT16
)wParam
,
2338 (LPINT32
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2340 case LB_SETTABSTOPS32
:
2341 return LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2342 (LPINT32
)lParam
, FALSE
);
2346 if (descr
->caret_on
) return LB_OKAY
;
2347 descr
->caret_on
= TRUE
;
2348 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2349 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2354 if (!descr
->caret_on
) return LB_OKAY
;
2355 descr
->caret_on
= FALSE
;
2356 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2357 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2361 return LISTBOX_Destroy( wnd
, descr
);
2364 InvalidateRect32( hwnd
, NULL
, TRUE
);
2368 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2372 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2377 HDC32 hdc
= ( wParam
) ? ((HDC32
)wParam
)
2378 : BeginPaint32( hwnd
, &ps
);
2379 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2380 if( !wParam
) EndPaint32( hwnd
, &ps
);
2385 LISTBOX_UpdateSize( wnd
, descr
);
2392 LISTBOX_SetFont( wnd
, descr
, (HFONT32
)wParam
);
2393 if (lParam
) InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
2397 descr
->caret_on
= TRUE
;
2398 if (descr
->focus_item
!= -1)
2399 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2400 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2404 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2405 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2406 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2410 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2413 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2415 case WM_LBUTTONDOWN
:
2416 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2417 (INT16
)LOWORD(lParam
),
2418 (INT16
)HIWORD(lParam
) );
2420 case WM_LBUTTONDBLCLK
:
2421 if (descr
->style
& LBS_NOTIFY
)
2422 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2426 if (GetCapture32() == hwnd
)
2427 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2428 (INT16
)HIWORD(lParam
) );
2432 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2435 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2438 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2441 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2444 if (IS_OWNERDRAW(descr
))
2446 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2447 HBRUSH32 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
2448 wParam
, (LPARAM
)wnd
->hwndSelf
);
2449 if (hbrush
) FillRect32( (HDC32
)wParam
, &rect
, hbrush
);
2455 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2459 case WM_QUERYDROPOBJECT
:
2464 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2465 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2467 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2472 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2473 dprintf_listbox(stddeb
,"Listbox %04x: unknown msg %04x wp %08x lp %08lx\n",
2474 hwnd
, msg
, wParam
, lParam
);
2475 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2480 /***********************************************************************
2483 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT32 attrib
, LPSTR dir
, BOOL32 bLong
)
2485 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2489 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2492 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2494 RedrawWindow32( lphc
->self
->hwndSelf
, NULL
, 0,
2495 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2502 /***********************************************************************
2505 * NOTE: in Windows, winproc address of the ComboLBox is the same
2506 * as that of the Listbox.
2508 LRESULT WINAPI
ComboLBWndProc( HWND32 hwnd
, UINT32 msg
,
2509 WPARAM32 wParam
, LPARAM lParam
)
2512 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2516 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2518 dprintf_combo( stddeb
, "ComboLBox [%04x]: msg %s wp %08x lp %08lx\n",
2519 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2521 if( descr
|| msg
== WM_CREATE
)
2523 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2528 #define lpcs ((LPCREATESTRUCT32A)lParam)
2529 dprintf_combo(stddeb
, "\tpassed parent handle = 0x%08x\n",
2530 (UINT32
)lpcs
->lpCreateParams
);
2532 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2534 return LISTBOX_Create( wnd
, lphc
);
2536 case WM_LBUTTONDOWN
:
2537 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2538 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2540 /* avoid activation at all costs */
2542 case WM_MOUSEACTIVATE
:
2543 return MA_NOACTIVATE
;
2549 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2551 /* for some reason(?) Windows makes it possible to
2552 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2554 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2555 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2556 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2558 COMBO_FlipListbox( lphc
, FALSE
);
2562 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2565 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2568 lRet
= DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2570 dprintf_combo(stddeb
,"\tComboLBox: default on msg [%04x]\n", (UINT16
)msg
);