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
);
487 /* FIXME: check LBS_USETABSTOPS style */
489 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
490 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
491 strlen(item
->str
), NULL
);
493 ExtTextOut32A( hdc
, rect
->left
+ 1, rect
->top
+ 1,
494 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
495 if (item
&& item
->selected
)
497 SetBkColor32( hdc
, oldBk
);
498 SetTextColor32( hdc
, oldText
);
500 if ((descr
->focus_item
== index
) &&
502 (GetFocus32() == wnd
->hwndSelf
)) DrawFocusRect32( hdc
, rect
);
507 /***********************************************************************
510 * Change the redraw flag.
512 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL32 on
)
516 if (!(descr
->style
& LBS_NOREDRAW
)) return;
517 descr
->style
&= ~LBS_NOREDRAW
;
518 LISTBOX_UpdateScroll( wnd
, descr
);
520 else descr
->style
|= LBS_NOREDRAW
;
524 /***********************************************************************
525 * LISTBOX_RepaintItem
527 * Repaint a single item synchronously.
529 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
535 HBRUSH32 hbrush
, oldBrush
= 0;
537 if (descr
->style
& LBS_NOREDRAW
) return;
538 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
539 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
540 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
541 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
542 hdc
, (LPARAM
)wnd
->hwndSelf
);
543 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
544 if (wnd
->dwStyle
& WS_DISABLED
)
545 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
546 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
547 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
548 if (oldFont
) SelectObject32( hdc
, oldFont
);
549 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
550 ReleaseDC32( wnd
->hwndSelf
, hdc
);
554 /***********************************************************************
555 * LISTBOX_InitStorage
557 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT32 nb_items
,
562 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
563 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
565 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
566 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
567 nb_items
* sizeof(LB_ITEMDATA
) )))
569 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
577 /***********************************************************************
578 * LISTBOX_SetTabStops
580 static BOOL32
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT32 count
,
581 LPINT32 tabs
, BOOL32 short_ints
)
583 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
584 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
585 if (!(descr
->nb_tabs
= count
))
590 /* FIXME: count = 1 */
591 if (!(descr
->tabs
= (INT32
*)HeapAlloc( descr
->heap
, 0,
592 descr
->nb_tabs
* sizeof(INT32
) )))
597 LPINT16 p
= (LPINT16
)tabs
;
598 for (i
= 0; i
< descr
->nb_tabs
; i
++) descr
->tabs
[i
] = *p
++;
600 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT32
) );
601 /* FIXME: repaint the window? */
606 /***********************************************************************
609 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
612 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
613 if (HAS_STRINGS(descr
))
615 lstrcpy32A( buffer
, descr
->items
[index
].str
);
616 return strlen(buffer
);
620 memcpy( buffer
, &descr
->items
[index
].data
, sizeof(DWORD
) );
621 return sizeof(DWORD
);
626 /***********************************************************************
627 * LISTBOX_FindStringPos
629 * Find the nearest string located before a given string in sort order.
630 * If 'exact' is TRUE, return an error if we don't get an exact match.
632 static INT32
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
635 INT32 index
, min
, max
, res
= -1;
637 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
639 max
= descr
->nb_items
;
642 index
= (min
+ max
) / 2;
643 if (HAS_STRINGS(descr
))
644 res
= lstrcmpi32A( descr
->items
[index
].str
, str
);
647 COMPAREITEMSTRUCT32 cis
;
648 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
650 cis
.CtlType
= ODT_LISTBOX
;
652 cis
.hwndItem
= wnd
->hwndSelf
;
654 cis
.itemData1
= descr
->items
[index
].data
;
656 cis
.itemData2
= (DWORD
)str
;
657 cis
.dwLocaleId
= descr
->locale
;
658 res
= SendMessage32A( descr
->owner
, WM_COMPAREITEM
,
661 if (!res
) return index
;
662 if (res
> 0) max
= index
;
663 else min
= index
+ 1;
665 return exact
? -1 : max
;
669 /***********************************************************************
670 * LISTBOX_FindFileStrPos
672 * Find the nearest string located before a given string in directory
673 * sort order (i.e. first files, then directories, then drives).
675 static INT32
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
677 INT32 min
, max
, res
= -1;
679 if (!HAS_STRINGS(descr
))
680 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
682 max
= descr
->nb_items
;
685 INT32 index
= (min
+ max
) / 2;
686 const char *p
= descr
->items
[index
].str
;
687 if (*p
== '[') /* drive or directory */
689 if (*str
!= '[') res
= -1;
690 else if (p
[1] == '-') /* drive */
692 if (str
[1] == '-') res
= str
[2] - p
[2];
697 if (str
[1] == '-') res
= 1;
698 else res
= lstrcmpi32A( str
, p
);
703 if (*str
== '[') res
= 1;
704 else res
= lstrcmpi32A( str
, p
);
706 if (!res
) return index
;
707 if (res
< 0) max
= index
;
708 else min
= index
+ 1;
714 /***********************************************************************
717 * Find the item beginning with a given string.
719 static INT32
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT32 start
,
720 LPCSTR str
, BOOL32 exact
)
725 if (start
>= descr
->nb_items
) start
= -1;
726 item
= descr
->items
+ start
+ 1;
727 if (HAS_STRINGS(descr
))
729 if (!str
) return LB_ERR
;
732 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
733 if (!lstrcmpi32A( str
, item
->str
)) return i
;
734 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
735 if (!lstrcmpi32A( str
, item
->str
)) return i
;
739 /* Special case for drives and directories: ignore prefix */
740 #define CHECK_DRIVE(item) \
741 if ((item)->str[0] == '[') \
743 if (!lstrncmpi32A( str, (item)->str+1, len )) return i; \
744 if (((item)->str[1] == '-') && !lstrncmpi32A(str,(item)->str+2,len)) \
748 INT32 len
= strlen(str
);
749 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
751 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
754 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
756 if (!lstrncmpi32A( str
, item
->str
, len
)) return i
;
764 if (exact
&& (descr
->style
& LBS_SORT
))
765 /* If sorted, use a WM_COMPAREITEM binary search */
766 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
768 /* Otherwise use a linear search */
769 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
770 if (item
->data
== (DWORD
)str
) return i
;
771 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
772 if (item
->data
== (DWORD
)str
) return i
;
778 /***********************************************************************
779 * LISTBOX_GetSelCount
781 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
784 LB_ITEMDATA
*item
= descr
->items
;
786 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
787 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
788 if (item
->selected
) count
++;
793 /***********************************************************************
794 * LISTBOX_GetSelItems16
796 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
800 LB_ITEMDATA
*item
= descr
->items
;
802 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
803 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
804 if (item
->selected
) array
[count
++] = (INT16
)i
;
809 /***********************************************************************
810 * LISTBOX_GetSelItems32
812 static LRESULT
LISTBOX_GetSelItems32( WND
*wnd
, LB_DESCR
*descr
, INT32 max
,
816 LB_ITEMDATA
*item
= descr
->items
;
818 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
819 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
820 if (item
->selected
) array
[count
++] = i
;
825 /***********************************************************************
828 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC32 hdc
)
830 INT32 i
, col_pos
= descr
->page_size
- 1;
833 HBRUSH32 hbrush
, oldBrush
= 0;
835 SetRect32( &rect
, 0, 0, descr
->width
, descr
->height
);
836 if (descr
->style
& LBS_NOREDRAW
) return 0;
837 if (descr
->style
& LBS_MULTICOLUMN
)
838 rect
.right
= rect
.left
+ descr
->column_width
;
839 else if (descr
->horz_pos
)
841 SetWindowOrgEx32( hdc
, descr
->horz_pos
, 0, NULL
);
842 rect
.right
+= descr
->horz_pos
;
845 if (descr
->font
) oldFont
= SelectObject32( hdc
, descr
->font
);
846 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
847 hdc
, (LPARAM
)wnd
->hwndSelf
);
848 if (hbrush
) oldBrush
= SelectObject32( hdc
, hbrush
);
849 if (wnd
->dwStyle
& WS_DISABLED
)
850 SetTextColor32( hdc
, GetSysColor32( COLOR_GRAYTEXT
) );
852 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
853 (GetFocus32() == wnd
->hwndSelf
))
855 /* Special case for empty listbox: paint focus rect */
856 rect
.bottom
= rect
.top
+ descr
->item_height
;
857 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
859 rect
.top
= rect
.bottom
;
862 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
864 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
865 rect
.bottom
= rect
.top
+ descr
->item_height
;
867 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
869 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
870 rect
.top
= rect
.bottom
;
872 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
874 if (!IS_OWNERDRAW(descr
))
876 /* Clear the bottom of the column */
877 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
878 if (rect
.top
< descr
->height
)
880 rect
.bottom
= descr
->height
;
881 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
882 &rect
, NULL
, 0, NULL
);
886 /* Go to the next column */
887 rect
.left
+= descr
->column_width
;
888 rect
.right
+= descr
->column_width
;
890 col_pos
= descr
->page_size
- 1;
895 if (rect
.top
>= descr
->height
) break;
899 if (!IS_OWNERDRAW(descr
))
901 /* Clear the remainder of the client area */
902 SetBkColor32( hdc
, GetSysColor32( COLOR_WINDOW
) );
903 if (rect
.top
< descr
->height
)
905 rect
.bottom
= descr
->height
;
906 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
907 &rect
, NULL
, 0, NULL
);
909 if (rect
.right
< descr
->width
)
911 rect
.left
= rect
.right
;
912 rect
.right
= descr
->width
;
914 rect
.bottom
= descr
->height
;
915 ExtTextOut32A( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
916 &rect
, NULL
, 0, NULL
);
919 if (oldFont
) SelectObject32( hdc
, oldFont
);
920 if (oldBrush
) SelectObject32( hdc
, oldBrush
);
925 /***********************************************************************
926 * LISTBOX_InvalidateItems
928 * Invalidate all items from a given item. If the specified item is not
929 * visible, nothing happens.
931 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
935 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
937 rect
.bottom
= descr
->height
;
938 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
939 if (descr
->style
& LBS_MULTICOLUMN
)
941 /* Repaint the other columns */
942 rect
.left
= rect
.right
;
943 rect
.right
= descr
->width
;
945 InvalidateRect32( wnd
->hwndSelf
, &rect
, TRUE
);
951 /***********************************************************************
952 * LISTBOX_GetItemHeight
954 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
956 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
958 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
959 return descr
->items
[index
].height
;
961 else return descr
->item_height
;
965 /***********************************************************************
966 * LISTBOX_SetItemHeight
968 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
971 if (!height
) height
= 1;
973 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
975 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
976 dprintf_listbox( stddeb
, "Listbox %04x: item %d height = %d\n",
977 wnd
->hwndSelf
, index
, height
);
978 descr
->items
[index
].height
= height
;
979 LISTBOX_UpdateScroll( wnd
, descr
);
980 LISTBOX_InvalidateItems( wnd
, descr
, index
);
982 else if (height
!= descr
->item_height
)
984 dprintf_listbox( stddeb
, "Listbox %04x: new height = %d\n",
985 wnd
->hwndSelf
, height
);
986 descr
->item_height
= height
;
987 LISTBOX_UpdatePage( wnd
, descr
);
988 LISTBOX_UpdateScroll( wnd
, descr
);
989 InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
995 /***********************************************************************
996 * LISTBOX_SetHorizontalPos
998 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT32 pos
)
1002 if (pos
> descr
->horz_extent
- descr
->width
)
1003 pos
= descr
->horz_extent
- descr
->width
;
1004 if (pos
< 0) pos
= 0;
1005 if (!(diff
= descr
->horz_pos
- pos
)) return;
1006 dprintf_listbox( stddeb
, "Listbox %04x: new horz pos = %d\n",
1007 wnd
->hwndSelf
, pos
);
1008 descr
->horz_pos
= pos
;
1009 LISTBOX_UpdateScroll( wnd
, descr
);
1010 if (abs(diff
) < descr
->width
)
1011 ScrollWindowEx32( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1012 SW_INVALIDATE
| SW_ERASE
);
1014 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1018 /***********************************************************************
1019 * LISTBOX_SetHorizontalExtent
1021 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1024 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1026 if (extent
<= 0) extent
= 1;
1027 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1028 dprintf_listbox( stddeb
, "Listbox %04x: new horz extent = %d\n",
1029 wnd
->hwndSelf
, extent
);
1030 descr
->horz_extent
= extent
;
1031 if (descr
->horz_pos
> extent
- descr
->width
)
1032 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1034 LISTBOX_UpdateScroll( wnd
, descr
);
1039 /***********************************************************************
1040 * LISTBOX_SetColumnWidth
1042 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT32 width
)
1044 width
+= 2; /* For left and right margin */
1045 if (width
== descr
->column_width
) return LB_OKAY
;
1046 dprintf_listbox( stddeb
, "Listbox %04x: new column width = %d\n",
1047 wnd
->hwndSelf
, width
);
1048 descr
->column_width
= width
;
1049 LISTBOX_UpdatePage( wnd
, descr
);
1054 /***********************************************************************
1057 * Returns the item height.
1059 static INT32
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT32 font
)
1062 HFONT32 oldFont
= 0;
1067 if (!(hdc
= GetDCEx32( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1069 fprintf( stderr
, "LISTBOX_SetFont: unable to get DC\n" );
1072 if (font
) oldFont
= SelectObject32( hdc
, font
);
1073 GetTextMetrics32A( hdc
, &tm
);
1074 if (oldFont
) SelectObject32( hdc
, oldFont
);
1075 ReleaseDC32( wnd
->hwndSelf
, hdc
);
1076 if (!IS_OWNERDRAW(descr
))
1077 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
+ 2 );
1078 return tm
.tmHeight
+ 2;
1082 /***********************************************************************
1083 * LISTBOX_MakeItemVisible
1085 * Make sure that a given item is partially or fully visible.
1087 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1092 if (index
<= descr
->top_item
) top
= index
;
1093 else if (descr
->style
& LBS_MULTICOLUMN
)
1095 INT32 cols
= descr
->width
;
1096 if (!fully
) cols
+= descr
->column_width
- 1;
1097 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1099 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1100 top
= index
- descr
->page_size
* (cols
- 1);
1102 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1104 INT32 height
= fully
? descr
->items
[index
].height
: 1;
1105 for (top
= index
; top
> descr
->top_item
; top
--)
1106 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1110 if (index
< descr
->top_item
+ descr
->page_size
) return;
1111 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1112 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1113 top
= index
- descr
->page_size
+ 1;
1115 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1119 /***********************************************************************
1120 * LISTBOX_SelectItemRange
1122 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1124 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT32 first
,
1125 INT32 last
, BOOL32 on
)
1129 /* A few sanity checks */
1131 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1132 if (last
== -1) last
= descr
->nb_items
- 1;
1133 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1134 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1135 /* selected_item reflects last selected/unselected item on multiple sel */
1136 descr
->selected_item
= last
;
1138 if (on
) /* Turn selection on */
1140 for (i
= first
; i
<= last
; i
++)
1142 if (descr
->items
[i
].selected
) continue;
1143 descr
->items
[i
].selected
= TRUE
;
1144 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1147 else /* Turn selection off */
1149 for (i
= first
; i
<= last
; i
++)
1151 if (!descr
->items
[i
].selected
) continue;
1152 descr
->items
[i
].selected
= FALSE
;
1153 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1160 /***********************************************************************
1161 * LISTBOX_SetCaretIndex
1163 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1164 BOOL32 fully_visible
)
1166 INT32 oldfocus
= descr
->focus_item
;
1168 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1169 if (index
== oldfocus
) return LB_OKAY
;
1170 descr
->focus_item
= index
;
1171 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1172 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1175 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1176 if (descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
))
1177 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1183 /***********************************************************************
1184 * LISTBOX_SetSelection
1186 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1187 BOOL32 on
, BOOL32 send_notify
)
1189 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1190 if (descr
->style
& LBS_MULTIPLESEL
)
1192 if (index
== -1) /* Select all items */
1193 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1194 else /* Only one item */
1195 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1199 INT32 oldsel
= descr
->selected_item
;
1200 if (index
== oldsel
) return LB_OKAY
;
1201 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1202 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1203 descr
->selected_item
= index
;
1204 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1205 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1206 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1207 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1213 /***********************************************************************
1216 * Change the caret position and extend the selection to the new caret.
1218 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1219 BOOL32 fully_visible
)
1221 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1222 if (descr
->style
& LBS_EXTENDEDSEL
)
1224 if (descr
->anchor_item
!= -1)
1226 INT32 first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1227 INT32 last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1229 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1230 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1231 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1234 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1236 /* Set selection to new caret item */
1237 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1242 /***********************************************************************
1243 * LISTBOX_InsertItem
1245 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1246 LPSTR str
, DWORD data
)
1251 if (index
== -1) index
= descr
->nb_items
;
1252 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1253 if (!descr
->items
) max_items
= 0;
1254 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1255 if (descr
->nb_items
== max_items
)
1257 /* We need to grow the array */
1258 max_items
+= LB_ARRAY_GRANULARITY
;
1259 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1260 max_items
* sizeof(LB_ITEMDATA
) )))
1262 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1265 descr
->items
= item
;
1268 /* Insert the item structure */
1270 item
= &descr
->items
[index
];
1271 if (index
< descr
->nb_items
)
1272 RtlMoveMemory( item
+ 1, item
,
1273 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1277 item
->selected
= FALSE
;
1280 /* Get item height */
1282 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1284 MEASUREITEMSTRUCT32 mis
;
1285 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1287 mis
.CtlType
= ODT_LISTBOX
;
1290 mis
.itemData
= descr
->items
[index
].data
;
1291 mis
.itemHeight
= descr
->item_height
;
1292 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1293 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1294 dprintf_listbox( stddeb
, "Listbox %04x: measure item %d (%s) = %d\n",
1295 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1298 /* Repaint the items */
1300 LISTBOX_UpdateScroll( wnd
, descr
);
1301 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1303 /* Move selection and focused item */
1305 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1306 if (index
<= descr
->focus_item
)
1308 descr
->focus_item
++;
1309 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
- 1, FALSE
);
1312 /* If listbox was empty, set focus to the first item */
1314 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1319 /***********************************************************************
1320 * LISTBOX_InsertString
1322 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT32 index
,
1325 LPSTR new_str
= NULL
;
1329 if (HAS_STRINGS(descr
))
1331 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1333 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1337 else data
= (DWORD
)str
;
1339 if (index
== -1) index
= descr
->nb_items
;
1340 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1342 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1346 dprintf_listbox( stddeb
, "Listbox %04x: added item %d '%s'\n",
1347 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1352 /***********************************************************************
1353 * LISTBOX_DeleteItem
1355 * Delete the content of an item. 'index' must be a valid index.
1357 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1359 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1360 * while Win95 sends it for all items with user data.
1361 * It's probably better to send it too often than not
1362 * often enough, so this is what we do here.
1364 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1366 DELETEITEMSTRUCT32 dis
;
1367 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1369 dis
.CtlType
= ODT_LISTBOX
;
1372 dis
.hwndItem
= wnd
->hwndSelf
;
1373 dis
.itemData
= descr
->items
[index
].data
;
1374 SendMessage32A( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1376 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1377 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1381 /***********************************************************************
1382 * LISTBOX_RemoveItem
1384 * Remove an item from the listbox and delete its content.
1386 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT32 index
)
1391 if (index
== -1) index
= descr
->nb_items
- 1;
1392 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1393 LISTBOX_DeleteItem( wnd
, descr
, index
);
1395 /* Remove the item */
1397 item
= &descr
->items
[index
];
1398 if (index
< descr
->nb_items
-1)
1399 RtlMoveMemory( item
, item
+ 1,
1400 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1402 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1404 /* Shrink the item array if possible */
1406 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1407 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1409 max_items
-= LB_ARRAY_GRANULARITY
;
1410 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1411 max_items
* sizeof(LB_ITEMDATA
) );
1412 if (item
) descr
->items
= item
;
1415 /* Repaint the items */
1417 LISTBOX_UpdateScroll( wnd
, descr
);
1418 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1420 /* Move selection and focused item */
1422 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1423 if (index
<= descr
->focus_item
)
1425 descr
->focus_item
--;
1426 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
+ 1, FALSE
);
1432 /***********************************************************************
1433 * LISTBOX_ResetContent
1435 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1439 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1440 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1441 descr
->nb_items
= 0;
1442 descr
->top_item
= 0;
1443 descr
->selected_item
= -1;
1444 descr
->focus_item
= 0;
1445 descr
->anchor_item
= -1;
1446 descr
->items
= NULL
;
1447 LISTBOX_UpdateScroll( wnd
, descr
);
1448 InvalidateRect32( wnd
->hwndSelf
, NULL
, TRUE
);
1452 /***********************************************************************
1455 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT32 count
)
1459 if (HAS_STRINGS(descr
)) return LB_ERR
;
1460 /* FIXME: this is far from optimal... */
1461 if (count
> descr
->nb_items
)
1463 while (count
> descr
->nb_items
)
1464 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1467 else if (count
< descr
->nb_items
)
1469 while (count
< descr
->nb_items
)
1470 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1477 /***********************************************************************
1480 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT32 attrib
,
1481 LPCSTR filespec
, BOOL32 long_names
)
1484 LRESULT ret
= LB_OKAY
;
1485 WIN32_FIND_DATA32A entry
;
1488 if ((handle
= FindFirstFile32A(filespec
,&entry
)) == INVALID_HANDLE_VALUE32
)
1490 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1497 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1499 if (!(attrib
& DDL_DIRECTORY
) ||
1500 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1501 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1502 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1504 else /* not a directory */
1506 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1507 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1509 if ((attrib
& DDL_EXCLUSIVE
) &&
1510 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1513 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1514 else strcpy( buffer
, entry
.cAlternateFileName
);
1516 if (!long_names
) CharLower32A( buffer
);
1517 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1518 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1520 } while (FindNextFile32A( handle
, &entry
));
1521 FindClose32( handle
);
1524 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1526 char buffer
[] = "[-a-]";
1528 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1530 if (!DRIVE_IsValid(drive
)) continue;
1531 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1539 /***********************************************************************
1540 * LISTBOX_HandleVScroll
1542 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1543 WPARAM32 wParam
, LPARAM lParam
)
1547 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1548 switch(LOWORD(wParam
))
1551 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1554 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1557 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1558 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1561 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1562 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1564 case SB_THUMBPOSITION
:
1565 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1568 info
.cbSize
= sizeof(info
);
1569 info
.fMask
= SIF_TRACKPOS
;
1570 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1571 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1574 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1577 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1584 /***********************************************************************
1585 * LISTBOX_HandleHScroll
1587 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1588 WPARAM32 wParam
, LPARAM lParam
)
1593 if (descr
->style
& LBS_MULTICOLUMN
)
1595 switch(LOWORD(wParam
))
1598 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1602 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1606 page
= descr
->width
/ descr
->column_width
;
1607 if (page
< 1) page
= 1;
1608 LISTBOX_SetTopItem( wnd
, descr
,
1609 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1612 page
= descr
->width
/ descr
->column_width
;
1613 if (page
< 1) page
= 1;
1614 LISTBOX_SetTopItem( wnd
, descr
,
1615 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1617 case SB_THUMBPOSITION
:
1618 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1622 info
.cbSize
= sizeof(info
);
1623 info
.fMask
= SIF_TRACKPOS
;
1624 GetScrollInfo32( wnd
->hwndSelf
, SB_VERT
, &info
);
1625 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1629 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1632 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1636 else if (descr
->horz_extent
)
1638 switch(LOWORD(wParam
))
1641 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1644 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1647 LISTBOX_SetHorizontalPos( wnd
, descr
,
1648 descr
->horz_pos
- descr
->width
);
1651 LISTBOX_SetHorizontalPos( wnd
, descr
,
1652 descr
->horz_pos
+ descr
->width
);
1654 case SB_THUMBPOSITION
:
1655 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1658 info
.cbSize
= sizeof(info
);
1659 info
.fMask
= SIF_TRACKPOS
;
1660 GetScrollInfo32( wnd
->hwndSelf
, SB_HORZ
, &info
);
1661 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1664 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1667 LISTBOX_SetHorizontalPos( wnd
, descr
,
1668 descr
->horz_extent
- descr
->width
);
1676 /***********************************************************************
1677 * LISTBOX_HandleLButtonDown
1679 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1680 WPARAM32 wParam
, INT32 x
, INT32 y
)
1682 INT32 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1683 dprintf_listbox( stddeb
, "Listbox %04x: lbuttondown %d,%d item %d\n",
1684 wnd
->hwndSelf
, x
, y
, index
);
1685 if (!descr
->caret_on
&& (GetFocus32() == wnd
->hwndSelf
)) return 0;
1688 if (descr
->style
& LBS_EXTENDEDSEL
)
1690 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1691 if (wParam
& MK_CONTROL
)
1693 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1694 LISTBOX_SetSelection( wnd
, descr
, index
,
1695 !descr
->items
[index
].selected
, FALSE
);
1697 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1701 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1702 LISTBOX_SetSelection( wnd
, descr
, index
,
1703 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1704 !descr
->items
[index
].selected
), FALSE
);
1708 if( !descr
->lphc
) SetFocus32( wnd
->hwndSelf
);
1709 else SetFocus32( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1710 : descr
->lphc
->self
->hwndSelf
) ;
1712 SetCapture32( wnd
->hwndSelf
);
1713 if (index
!= -1 && !descr
->lphc
)
1715 if (descr
->style
& LBS_NOTIFY
)
1716 SendMessage32A( descr
->owner
, WM_LBTRACKPOINT
, index
,
1717 MAKELPARAM( x
, y
) );
1718 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1720 POINT32 pt
= { x
, y
};
1721 if (DragDetect32( wnd
->hwndSelf
, pt
))
1722 SendMessage32A( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1729 /***********************************************************************
1730 * LISTBOX_HandleLButtonUp
1732 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1734 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1735 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1736 LISTBOX_Timer
= LB_TIMER_NONE
;
1737 if (GetCapture32() == wnd
->hwndSelf
)
1740 if (descr
->style
& LBS_NOTIFY
)
1741 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1747 /***********************************************************************
1748 * LISTBOX_HandleTimer
1750 * Handle scrolling upon a timer event.
1751 * Return TRUE if scrolling should continue.
1753 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1754 INT32 index
, TIMER_DIRECTION dir
)
1759 if (descr
->top_item
) index
= descr
->top_item
- 1;
1763 if (descr
->top_item
) index
-= descr
->page_size
;
1766 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1767 if (index
== descr
->focus_item
) index
++;
1768 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1770 case LB_TIMER_RIGHT
:
1771 if (index
+ descr
->page_size
< descr
->nb_items
)
1772 index
+= descr
->page_size
;
1777 if (index
== descr
->focus_item
) return FALSE
;
1778 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1783 /***********************************************************************
1784 * LISTBOX_HandleSystemTimer
1786 * WM_SYSTIMER handler.
1788 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1790 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1792 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1793 LISTBOX_Timer
= LB_TIMER_NONE
;
1799 /***********************************************************************
1800 * LISTBOX_HandleMouseMove
1802 * WM_MOUSEMOVE handler.
1804 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1808 TIMER_DIRECTION dir
;
1810 if (descr
->style
& LBS_MULTICOLUMN
)
1813 else if (y
>= descr
->item_height
* descr
->page_size
)
1814 y
= descr
->item_height
* descr
->page_size
- 1;
1818 dir
= LB_TIMER_LEFT
;
1821 else if (x
>= descr
->width
)
1823 dir
= LB_TIMER_RIGHT
;
1824 x
= descr
->width
- 1;
1826 else dir
= LB_TIMER_NONE
; /* inside */
1830 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1831 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1832 else dir
= LB_TIMER_NONE
; /* inside */
1835 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1836 if (index
== -1) index
= descr
->focus_item
;
1837 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1839 /* Start/stop the system timer */
1841 if (dir
!= LB_TIMER_NONE
)
1842 SetSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1843 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1844 KillSystemTimer32( wnd
->hwndSelf
, LB_TIMER_ID
);
1845 LISTBOX_Timer
= dir
;
1849 /***********************************************************************
1850 * LISTBOX_HandleKeyDown
1852 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM32 wParam
)
1855 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1857 caret
= SendMessage32A( descr
->owner
, WM_VKEYTOITEM
,
1858 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1860 if (caret
== -2) return 0;
1862 if (caret
== -1) switch(wParam
)
1865 if (descr
->style
& LBS_MULTICOLUMN
)
1867 if (descr
->focus_item
>= descr
->page_size
)
1868 caret
= descr
->focus_item
- descr
->page_size
;
1873 caret
= descr
->focus_item
- 1;
1874 if (caret
< 0) caret
= 0;
1877 if (descr
->style
& LBS_MULTICOLUMN
)
1879 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1880 caret
= descr
->focus_item
+ descr
->page_size
;
1885 caret
= descr
->focus_item
+ 1;
1886 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1889 if (descr
->style
& LBS_MULTICOLUMN
)
1891 INT32 page
= descr
->width
/ descr
->column_width
;
1892 if (page
< 1) page
= 1;
1893 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1895 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1896 if (caret
< 0) caret
= 0;
1899 if (descr
->style
& LBS_MULTICOLUMN
)
1901 INT32 page
= descr
->width
/ descr
->column_width
;
1902 if (page
< 1) page
= 1;
1903 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1905 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1906 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1912 caret
= descr
->nb_items
- 1;
1915 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1916 else if (descr
->style
& LBS_MULTIPLESEL
)
1918 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1919 !descr
->items
[descr
->focus_item
].selected
,
1920 (descr
->style
& LBS_NOTIFY
) != 0 );
1922 else if (descr
->selected_item
== -1)
1924 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1925 (descr
->style
& LBS_NOTIFY
) != 0 );
1931 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1932 !(GetKeyState32( VK_SHIFT
) & 0x8000))
1933 descr
->anchor_item
= caret
;
1934 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1935 if (descr
->style
& LBS_NOTIFY
)
1937 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1939 /* make sure that combo parent doesn't hide us */
1940 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1942 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1949 /***********************************************************************
1950 * LISTBOX_HandleChar
1952 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
1956 char str
[2] = { wParam
& 0xff, '\0' };
1958 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1960 caret
= SendMessage32A( descr
->owner
, WM_CHARTOITEM
,
1961 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1963 if (caret
== -2) return 0;
1966 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
1969 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1970 if (descr
->style
& LBS_NOTIFY
)
1971 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1977 /***********************************************************************
1980 static BOOL32
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
1983 MEASUREITEMSTRUCT32 mis
;
1986 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
1988 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
1990 HeapFree( GetProcessHeap(), 0, descr
);
1993 GetClientRect32( wnd
->hwndSelf
, &rect
);
1994 descr
->owner
= GetParent32( wnd
->hwndSelf
);
1995 descr
->style
= wnd
->dwStyle
;
1996 descr
->width
= rect
.right
- rect
.left
;
1997 descr
->height
= rect
.bottom
- rect
.top
;
1998 descr
->items
= NULL
;
1999 descr
->nb_items
= 0;
2000 descr
->top_item
= 0;
2001 descr
->selected_item
= -1;
2002 descr
->focus_item
= 0;
2003 descr
->anchor_item
= -1;
2004 descr
->item_height
= 1;
2005 descr
->page_size
= 1;
2006 descr
->column_width
= 150;
2007 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2008 descr
->horz_pos
= 0;
2011 descr
->caret_on
= TRUE
;
2013 descr
->locale
= 0; /* FIXME */
2018 dprintf_combo(stddeb
,"ComboLBox [%04x]: resetting owner %04x -> %04x\n",
2019 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2020 descr
->owner
= lphc
->self
->hwndSelf
;
2023 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2025 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2027 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2028 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2029 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2030 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2032 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2034 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2036 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2037 descr
->item_height
= lphc
->RectButton
.bottom
- lphc
->RectButton
.top
- 6;
2041 UINT32 id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2043 mis
.CtlType
= ODT_LISTBOX
;
2048 mis
.itemHeight
= descr
->item_height
;
2049 SendMessage32A( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2050 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2058 /***********************************************************************
2061 static BOOL32
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2063 LISTBOX_ResetContent( wnd
, descr
);
2064 HeapDestroy( descr
->heap
);
2065 HeapFree( GetProcessHeap(), 0, descr
);
2071 /***********************************************************************
2074 LRESULT WINAPI
ListBoxWndProc( HWND32 hwnd
, UINT32 msg
,
2075 WPARAM32 wParam
, LPARAM lParam
)
2079 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2082 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2084 if (msg
== WM_CREATE
)
2086 if (!LISTBOX_Create( wnd
, NULL
)) return -1;
2087 dprintf_listbox( stddeb
, "Listbox: creating wnd=%04x descr=%p\n",
2088 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2091 /* Ignore all other messages before we get a WM_CREATE */
2092 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2095 dprintf_listbox( stddeb
, "Listbox %04x: msg %s wp %08x lp %08lx\n",
2096 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2099 case LB_RESETCONTENT16
:
2100 case LB_RESETCONTENT32
:
2101 LISTBOX_ResetContent( wnd
, descr
);
2104 case LB_ADDSTRING16
:
2105 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2107 case LB_ADDSTRING32
:
2108 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2109 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2111 case LB_INSERTSTRING16
:
2112 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2113 wParam
= (INT32
)(INT16
)wParam
;
2115 case LB_INSERTSTRING32
:
2116 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2119 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2122 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2123 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2125 case LB_DELETESTRING16
:
2126 case LB_DELETESTRING32
:
2127 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2129 case LB_GETITEMDATA16
:
2130 case LB_GETITEMDATA32
:
2131 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2133 return descr
->items
[wParam
].data
;
2135 case LB_SETITEMDATA16
:
2136 case LB_SETITEMDATA32
:
2137 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2139 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2144 return descr
->nb_items
;
2147 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2150 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2152 case LB_GETTEXTLEN16
:
2154 case LB_GETTEXTLEN32
:
2155 if (wParam
>= descr
->nb_items
) return LB_ERR
;
2156 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2159 case LB_GETCURSEL16
:
2160 case LB_GETCURSEL32
:
2161 return descr
->selected_item
;
2163 case LB_GETTOPINDEX16
:
2164 case LB_GETTOPINDEX32
:
2165 return descr
->top_item
;
2167 case LB_GETITEMHEIGHT16
:
2168 case LB_GETITEMHEIGHT32
:
2169 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2171 case LB_SETITEMHEIGHT16
:
2172 lParam
= LOWORD(lParam
);
2174 case LB_SETITEMHEIGHT32
:
2175 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2177 case LB_ITEMFROMPOINT32
:
2179 POINT32 pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2180 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2181 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2182 PtInRect32( &rect
, pt
) );
2185 case LB_SETCARETINDEX16
:
2186 case LB_SETCARETINDEX32
:
2187 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2189 case LB_GETCARETINDEX16
:
2190 case LB_GETCARETINDEX32
:
2191 return descr
->focus_item
;
2193 case LB_SETTOPINDEX16
:
2194 case LB_SETTOPINDEX32
:
2195 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2197 case LB_SETCOLUMNWIDTH16
:
2198 case LB_SETCOLUMNWIDTH32
:
2199 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2201 case LB_GETITEMRECT16
:
2204 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2205 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2209 case LB_GETITEMRECT32
:
2210 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT32
*)lParam
);
2212 case LB_FINDSTRING16
:
2213 wParam
= (INT32
)(INT16
)wParam
;
2214 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2216 case LB_FINDSTRING32
:
2217 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2219 case LB_FINDSTRINGEXACT16
:
2220 wParam
= (INT32
)(INT16
)wParam
;
2221 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2223 case LB_FINDSTRINGEXACT32
:
2224 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2226 case LB_SELECTSTRING16
:
2227 wParam
= (INT32
)(INT16
)wParam
;
2228 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2230 case LB_SELECTSTRING32
:
2232 INT32 index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2233 (LPCSTR
)lParam
, FALSE
);
2234 if (index
== LB_ERR
) return LB_ERR
;
2235 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2240 wParam
= (INT32
)(INT16
)wParam
;
2243 if (((INT32
)wParam
< 0) || ((INT32
)wParam
>= descr
->nb_items
))
2245 return descr
->items
[wParam
].selected
;
2248 lParam
= (INT32
)(INT16
)lParam
;
2251 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2253 case LB_SETCURSEL16
:
2254 wParam
= (INT32
)(INT16
)wParam
;
2256 case LB_SETCURSEL32
:
2257 if (wParam
!= -1) LISTBOX_MakeItemVisible( wnd
, descr
, wParam
, TRUE
);
2258 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2260 case LB_GETSELCOUNT16
:
2261 case LB_GETSELCOUNT32
:
2262 return LISTBOX_GetSelCount( wnd
, descr
);
2264 case LB_GETSELITEMS16
:
2265 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2266 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2268 case LB_GETSELITEMS32
:
2269 return LISTBOX_GetSelItems32( wnd
, descr
, wParam
, (LPINT32
)lParam
);
2271 case LB_SELITEMRANGE16
:
2272 case LB_SELITEMRANGE32
:
2273 if (LOWORD(lParam
) <= HIWORD(lParam
))
2274 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2275 HIWORD(lParam
), wParam
);
2277 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2278 LOWORD(lParam
), wParam
);
2280 case LB_SELITEMRANGEEX16
:
2281 case LB_SELITEMRANGEEX32
:
2282 if ((INT32
)lParam
>= (INT32
)wParam
)
2283 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2285 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2287 case LB_GETHORIZONTALEXTENT16
:
2288 case LB_GETHORIZONTALEXTENT32
:
2289 return descr
->horz_extent
;
2291 case LB_SETHORIZONTALEXTENT16
:
2292 case LB_SETHORIZONTALEXTENT32
:
2293 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2295 case LB_GETANCHORINDEX16
:
2296 case LB_GETANCHORINDEX32
:
2297 return descr
->anchor_item
;
2299 case LB_SETANCHORINDEX16
:
2300 wParam
= (INT32
)(INT16
)wParam
;
2302 case LB_SETANCHORINDEX32
:
2303 if (((INT32
)wParam
< -1) || ((INT32
)wParam
>= descr
->nb_items
))
2305 descr
->anchor_item
= (INT32
)wParam
;
2309 return LISTBOX_Directory( wnd
, descr
, wParam
,
2310 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2313 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2315 case LB_GETLOCALE32
:
2316 return descr
->locale
;
2318 case LB_SETLOCALE32
:
2319 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2322 case LB_INITSTORAGE32
:
2323 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2326 return LISTBOX_SetCount( wnd
, descr
, (INT32
)wParam
);
2328 case LB_SETTABSTOPS16
:
2329 return LISTBOX_SetTabStops( wnd
, descr
, (INT32
)(INT16
)wParam
,
2330 (LPINT32
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2332 case LB_SETTABSTOPS32
:
2333 return LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2334 (LPINT32
)lParam
, FALSE
);
2338 if (descr
->caret_on
) return LB_OKAY
;
2339 descr
->caret_on
= TRUE
;
2340 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2341 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2346 if (!descr
->caret_on
) return LB_OKAY
;
2347 descr
->caret_on
= FALSE
;
2348 if ((descr
->focus_item
!= -1) && (GetFocus32() == wnd
->hwndSelf
))
2349 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2353 return LISTBOX_Destroy( wnd
, descr
);
2356 InvalidateRect32( hwnd
, NULL
, TRUE
);
2360 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2364 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2369 HDC32 hdc
= ( wParam
) ? ((HDC32
)wParam
)
2370 : BeginPaint32( hwnd
, &ps
);
2371 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2372 if( !wParam
) EndPaint32( hwnd
, &ps
);
2377 LISTBOX_UpdateSize( wnd
, descr
);
2384 LISTBOX_SetFont( wnd
, descr
, (HFONT32
)wParam
);
2385 if (lParam
) InvalidateRect32( wnd
->hwndSelf
, 0, TRUE
);
2389 descr
->caret_on
= TRUE
;
2390 if (descr
->focus_item
!= -1)
2391 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2392 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2396 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2397 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2398 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2402 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2405 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2407 case WM_LBUTTONDOWN
:
2408 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2409 (INT16
)LOWORD(lParam
),
2410 (INT16
)HIWORD(lParam
) );
2412 case WM_LBUTTONDBLCLK
:
2413 if (descr
->style
& LBS_NOTIFY
)
2414 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2418 if (GetCapture32() == hwnd
)
2419 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2420 (INT16
)HIWORD(lParam
) );
2424 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2427 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2430 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2433 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2436 if (IS_OWNERDRAW(descr
))
2438 RECT32 rect
= { 0, 0, descr
->width
, descr
->height
};
2439 HBRUSH32 hbrush
= SendMessage32A( descr
->owner
, WM_CTLCOLORLISTBOX
,
2440 wParam
, (LPARAM
)wnd
->hwndSelf
);
2441 if (hbrush
) FillRect32( (HDC32
)wParam
, &rect
, hbrush
);
2447 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2451 case WM_QUERYDROPOBJECT
:
2456 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2457 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2459 return SendMessage32A( descr
->owner
, msg
, wParam
, lParam
);
2464 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2465 fprintf(stderr
,"Listbox %04x: unknown msg %04x wp %08x lp %08lx\n",
2466 hwnd
, msg
, wParam
, lParam
);
2467 return DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2472 /***********************************************************************
2475 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT32 attrib
, LPSTR dir
, BOOL32 bLong
)
2477 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2481 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2484 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2486 RedrawWindow32( lphc
->self
->hwndSelf
, NULL
, 0,
2487 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2494 /***********************************************************************
2497 * NOTE: in Windows, winproc address of the ComboLBox is the same
2498 * as that of the Listbox.
2500 LRESULT WINAPI
ComboLBWndProc( HWND32 hwnd
, UINT32 msg
,
2501 WPARAM32 wParam
, LPARAM lParam
)
2504 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2508 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2510 dprintf_combo( stddeb
, "ComboLBox [%04x]: msg %s wp %08x lp %08lx\n",
2511 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2513 if( descr
|| msg
== WM_CREATE
)
2515 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2520 #define lpcs ((LPCREATESTRUCT32A)lParam)
2521 dprintf_combo(stddeb
, "\tpassed parent handle = 0x%08x\n",
2522 (UINT32
)lpcs
->lpCreateParams
);
2524 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2526 return LISTBOX_Create( wnd
, lphc
);
2528 case WM_LBUTTONDOWN
:
2529 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2530 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2532 /* avoid activation at all costs */
2534 case WM_MOUSEACTIVATE
:
2535 return MA_NOACTIVATE
;
2541 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2543 /* for some reason(?) Windows makes it possible to
2544 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2546 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2547 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2548 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2550 COMBO_FlipListbox( lphc
, FALSE
);
2554 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2557 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2560 lRet
= DefWindowProc32A( hwnd
, msg
, wParam
, lParam
);
2562 dprintf_combo(stddeb
,"\tComboLBox: default on msg [%04x]\n", (UINT16
)msg
);