4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
27 /* Items array granularity */
28 #define LB_ARRAY_GRANULARITY 16
30 /* Scrolling timeout in ms */
31 #define LB_SCROLL_TIMEOUT 50
33 /* Listbox system timer id */
39 LPSTR str
; /* Item text */
40 BOOL selected
; /* Is item selected? */
41 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
42 DWORD data
; /* User data */
45 /* Listbox structure */
48 HANDLE heap
; /* Heap for this listbox */
49 HWND owner
; /* Owner window to send notifications to */
50 UINT style
; /* Window style */
51 INT width
; /* Window width */
52 INT height
; /* Window height */
53 LB_ITEMDATA
*items
; /* Array of items */
54 INT nb_items
; /* Number of items */
55 INT top_item
; /* Top visible item */
56 INT selected_item
; /* Selected item */
57 INT focus_item
; /* Item that has the focus */
58 INT anchor_item
; /* Anchor item for extended selection */
59 INT item_height
; /* Default item height */
60 INT page_size
; /* Items per listbox page */
61 INT column_width
; /* Column width for multi-column listboxes */
62 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
63 INT horz_pos
; /* Horizontal position */
64 INT nb_tabs
; /* Number of tabs in array */
65 INT
*tabs
; /* Array of tabs */
66 BOOL caret_on
; /* Is caret on? */
67 BOOL captured
; /* Is mouse captured? */
68 HFONT font
; /* Current font */
69 LCID locale
; /* Current locale for string comparisons */
70 LPHEADCOMBO lphc
; /* ComboLBox */
74 #define IS_OWNERDRAW(descr) \
75 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
77 #define HAS_STRINGS(descr) \
78 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
80 #define SEND_NOTIFICATION(wnd,descr,code) \
81 (SendMessageA( (descr)->owner, WM_COMMAND, \
82 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
84 /* Current timer status */
94 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
97 /***********************************************************************
100 void LISTBOX_Dump( WND
*wnd
)
104 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
106 DUMP( "Listbox:\n" );
107 DUMP( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
108 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
110 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
112 DUMP( "%4d: %-40s %d %08lx %3d\n",
113 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
118 /***********************************************************************
119 * LISTBOX_GetCurrentPageSize
121 * Return the current page size
123 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
126 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
127 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
129 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
131 if (i
== descr
->top_item
) return 1;
132 else return i
- descr
->top_item
;
136 /***********************************************************************
137 * LISTBOX_GetMaxTopIndex
139 * Return the maximum possible index for the top of the listbox.
141 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
145 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
147 page
= descr
->height
;
148 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
149 if ((page
-= descr
->items
[max
].height
) < 0) break;
150 if (max
< descr
->nb_items
- 1) max
++;
152 else if (descr
->style
& LBS_MULTICOLUMN
)
154 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
155 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
156 max
= (max
- page
) * descr
->page_size
;
160 max
= descr
->nb_items
- descr
->page_size
;
162 if (max
< 0) max
= 0;
167 /***********************************************************************
168 * LISTBOX_UpdateScroll
170 * Update the scrollbars. Should be called whenever the content
171 * of the listbox changes.
173 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
177 if (!(descr
->style
& WS_VSCROLL
)) return;
178 /* It is important that we check descr->style, and not wnd->dwStyle,
179 for WS_VSCROLL, as the former is exactly the one passed in
180 argument to CreateWindow.
181 In Windows (and from now on in Wine :) a listbox created
182 with such a style (no WS_SCROLL) does not update
183 the scrollbar with listbox-related data, thus letting
184 the programmer use it for his/her own purposes. */
186 if (descr
->style
& LBS_NOREDRAW
) return;
187 info
.cbSize
= sizeof(info
);
189 if (descr
->style
& LBS_MULTICOLUMN
)
192 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
193 info
.nPos
= descr
->top_item
/ descr
->page_size
;
194 info
.nPage
= descr
->width
/ descr
->column_width
;
195 if (info
.nPage
< 1) info
.nPage
= 1;
196 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
197 if (descr
->style
& LBS_DISABLENOSCROLL
)
198 info
.fMask
|= SIF_DISABLENOSCROLL
;
199 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
201 info
.fMask
= SIF_RANGE
;
202 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
207 info
.nMax
= descr
->nb_items
- 1;
208 info
.nPos
= descr
->top_item
;
209 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
210 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
211 if (descr
->style
& LBS_DISABLENOSCROLL
)
212 info
.fMask
|= SIF_DISABLENOSCROLL
;
213 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
215 if (descr
->horz_extent
)
218 info
.nMax
= descr
->horz_extent
- 1;
219 info
.nPos
= descr
->horz_pos
;
220 info
.nPage
= descr
->width
;
221 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
222 if (descr
->style
& LBS_DISABLENOSCROLL
)
223 info
.fMask
|= SIF_DISABLENOSCROLL
;
224 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
230 /***********************************************************************
233 * Set the top item of the listbox, scrolling up or down if necessary.
235 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
238 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
239 if (index
> max
) index
= max
;
240 if (index
< 0) index
= 0;
241 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
242 if (descr
->top_item
== index
) return LB_OKAY
;
243 if (descr
->style
& LBS_MULTICOLUMN
)
245 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
246 if (scroll
&& (abs(diff
) < descr
->width
))
247 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
248 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
256 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
260 if (index
> descr
->top_item
)
262 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
263 diff
-= descr
->items
[i
].height
;
267 for (i
= index
; i
< descr
->top_item
; i
++)
268 diff
+= descr
->items
[i
].height
;
272 diff
= (descr
->top_item
- index
) * descr
->item_height
;
274 if (abs(diff
) < descr
->height
)
275 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
276 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
280 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
281 descr
->top_item
= index
;
282 LISTBOX_UpdateScroll( wnd
, descr
);
287 /***********************************************************************
290 * Update the page size. Should be called when the size of
291 * the client area or the item height changes.
293 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
297 if ((page_size
= descr
->height
/ descr
->item_height
) < 1) page_size
= 1;
298 if (page_size
== descr
->page_size
) return;
299 descr
->page_size
= page_size
;
300 if (descr
->style
& LBS_MULTICOLUMN
)
301 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
302 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
306 /***********************************************************************
309 * Update the size of the listbox. Should be called when the size of
310 * the client area changes.
312 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
316 GetClientRect( wnd
->hwndSelf
, &rect
);
317 descr
->width
= rect
.right
- rect
.left
;
318 descr
->height
= rect
.bottom
- rect
.top
;
319 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
321 if ((descr
->height
> descr
->item_height
) &&
322 (descr
->height
% descr
->item_height
))
324 TRACE(listbox
, "[%04x]: changing height %d -> %d\n",
325 wnd
->hwndSelf
, descr
->height
,
326 descr
->height
- descr
->height
%descr
->item_height
);
327 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
328 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
329 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
330 (descr
->height
% descr
->item_height
),
331 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
335 TRACE(listbox
, "[%04x]: new size = %d,%d\n",
336 wnd
->hwndSelf
, descr
->width
, descr
->height
);
337 LISTBOX_UpdatePage( wnd
, descr
);
338 LISTBOX_UpdateScroll( wnd
, descr
);
342 /***********************************************************************
343 * LISTBOX_GetItemRect
345 * Get the rectangle enclosing an item, in listbox client coordinates.
346 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
348 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
351 /* Index <= 0 is legal even on empty listboxes */
352 if (index
&& (index
>= descr
->nb_items
)) return -1;
353 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
354 if (descr
->style
& LBS_MULTICOLUMN
)
356 INT col
= (index
/ descr
->page_size
) -
357 (descr
->top_item
/ descr
->page_size
);
358 rect
->left
+= col
* descr
->column_width
;
359 rect
->right
= rect
->left
+ descr
->column_width
;
360 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
361 rect
->bottom
= rect
->top
+ descr
->item_height
;
363 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
366 rect
->right
+= descr
->horz_pos
;
367 if ((index
>= 0) && (index
< descr
->nb_items
))
369 if (index
< descr
->top_item
)
371 for (i
= descr
->top_item
-1; i
>= index
; i
--)
372 rect
->top
-= descr
->items
[i
].height
;
376 for (i
= descr
->top_item
; i
< index
; i
++)
377 rect
->top
+= descr
->items
[i
].height
;
379 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
385 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
386 rect
->bottom
= rect
->top
+ descr
->item_height
;
387 rect
->right
+= descr
->horz_pos
;
390 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
391 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
395 /***********************************************************************
396 * LISTBOX_GetItemFromPoint
398 * Return the item nearest from point (x,y) (in client coordinates).
400 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
403 INT index
= descr
->top_item
;
405 if (!descr
->nb_items
) return -1; /* No items */
406 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
411 while (index
< descr
->nb_items
)
413 if ((pos
+= descr
->items
[index
].height
) > y
) break;
422 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
426 else if (descr
->style
& LBS_MULTICOLUMN
)
428 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
429 if (y
>= 0) index
+= y
/ descr
->item_height
;
430 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
431 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
435 index
+= (y
/ descr
->item_height
);
437 if (index
< 0) return 0;
438 if (index
>= descr
->nb_items
) return -1;
443 /***********************************************************************
448 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
449 const RECT
*rect
, INT index
, UINT action
)
451 LB_ITEMDATA
*item
= NULL
;
452 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
454 if (IS_OWNERDRAW(descr
))
457 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
461 if (action
== ODA_FOCUS
)
462 DrawFocusRect( hdc
, rect
);
464 FIXME(listbox
,"called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
467 dis
.CtlType
= ODT_LISTBOX
;
469 dis
.hwndItem
= wnd
->hwndSelf
;
470 dis
.itemAction
= action
;
474 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
475 if ((descr
->focus_item
== index
) &&
477 (GetFocus() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
478 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
479 dis
.itemData
= item
? item
->data
: 0;
481 TRACE(listbox
, "[%04x]: drawitem %d (%s) action=%02x "
482 "state=%02x rect=%d,%d-%d,%d\n",
483 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
484 dis
.itemState
, rect
->left
, rect
->top
,
485 rect
->right
, rect
->bottom
);
486 SendMessageA(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
490 COLORREF oldText
= 0, oldBk
= 0;
492 if (action
== ODA_FOCUS
)
494 DrawFocusRect( hdc
, rect
);
497 if (item
&& item
->selected
)
499 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
500 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
503 TRACE(listbox
, "[%04x]: painting %d (%s) action=%02x "
504 "rect=%d,%d-%d,%d\n",
505 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
506 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
508 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
509 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
510 else if (!(descr
->style
& LBS_USETABSTOPS
))
511 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
512 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
513 strlen(item
->str
), NULL
);
516 /* Output empty string to paint background in the full width. */
517 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
518 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
519 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
520 item
->str
, strlen(item
->str
),
521 descr
->nb_tabs
, descr
->tabs
, 0);
523 if (item
&& item
->selected
)
525 SetBkColor( hdc
, oldBk
);
526 SetTextColor( hdc
, oldText
);
528 if ((descr
->focus_item
== index
) &&
530 (GetFocus() == wnd
->hwndSelf
)) DrawFocusRect( hdc
, rect
);
535 /***********************************************************************
538 * Change the redraw flag.
540 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
544 if (!(descr
->style
& LBS_NOREDRAW
)) return;
545 descr
->style
&= ~LBS_NOREDRAW
;
546 LISTBOX_UpdateScroll( wnd
, descr
);
548 else descr
->style
|= LBS_NOREDRAW
;
552 /***********************************************************************
553 * LISTBOX_RepaintItem
555 * Repaint a single item synchronously.
557 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
563 HBRUSH hbrush
, oldBrush
= 0;
565 if (descr
->style
& LBS_NOREDRAW
) return;
566 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
567 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
568 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
569 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
570 hdc
, (LPARAM
)wnd
->hwndSelf
);
571 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
572 if (wnd
->dwStyle
& WS_DISABLED
)
573 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
574 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
575 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
576 if (oldFont
) SelectObject( hdc
, oldFont
);
577 if (oldBrush
) SelectObject( hdc
, oldBrush
);
578 ReleaseDC( wnd
->hwndSelf
, hdc
);
582 /***********************************************************************
583 * LISTBOX_InitStorage
585 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
590 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
591 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
593 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
594 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
595 nb_items
* sizeof(LB_ITEMDATA
) )))
597 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
605 /***********************************************************************
606 * LISTBOX_SetTabStops
608 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
609 LPINT tabs
, BOOL short_ints
)
611 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
612 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
613 if (!(descr
->nb_tabs
= count
))
618 /* FIXME: count = 1 */
619 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
620 descr
->nb_tabs
* sizeof(INT
) )))
625 LPINT16 p
= (LPINT16
)tabs
;
626 dbg_decl_str(listbox
, 256);
628 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
629 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
630 if(TRACE_ON(listbox
))
631 dsprintf(listbox
, "%hd ", descr
->tabs
[i
]);
633 TRACE(listbox
, "[%04x]: settabstops %s\n",
634 wnd
->hwndSelf
, dbg_str(listbox
));
636 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
637 /* FIXME: repaint the window? */
642 /***********************************************************************
645 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
648 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
649 if (HAS_STRINGS(descr
))
652 return strlen(descr
->items
[index
].str
);
653 lstrcpyA( buffer
, descr
->items
[index
].str
);
654 return strlen(buffer
);
657 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
658 return sizeof(DWORD
);
663 /***********************************************************************
664 * LISTBOX_FindStringPos
666 * Find the nearest string located before a given string in sort order.
667 * If 'exact' is TRUE, return an error if we don't get an exact match.
669 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
672 INT index
, min
, max
, res
= -1;
674 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
676 max
= descr
->nb_items
;
679 index
= (min
+ max
) / 2;
680 if (HAS_STRINGS(descr
))
681 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
684 COMPAREITEMSTRUCT cis
;
685 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
687 cis
.CtlType
= ODT_LISTBOX
;
689 cis
.hwndItem
= wnd
->hwndSelf
;
691 cis
.itemData1
= descr
->items
[index
].data
;
693 cis
.itemData2
= (DWORD
)str
;
694 cis
.dwLocaleId
= descr
->locale
;
695 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
698 if (!res
) return index
;
699 if (res
> 0) max
= index
;
700 else min
= index
+ 1;
702 return exact
? -1 : max
;
706 /***********************************************************************
707 * LISTBOX_FindFileStrPos
709 * Find the nearest string located before a given string in directory
710 * sort order (i.e. first files, then directories, then drives).
712 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
714 INT min
, max
, res
= -1;
716 if (!HAS_STRINGS(descr
))
717 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
719 max
= descr
->nb_items
;
722 INT index
= (min
+ max
) / 2;
723 const char *p
= descr
->items
[index
].str
;
724 if (*p
== '[') /* drive or directory */
726 if (*str
!= '[') res
= -1;
727 else if (p
[1] == '-') /* drive */
729 if (str
[1] == '-') res
= str
[2] - p
[2];
734 if (str
[1] == '-') res
= 1;
735 else res
= lstrcmpiA( str
, p
);
740 if (*str
== '[') res
= 1;
741 else res
= lstrcmpiA( str
, p
);
743 if (!res
) return index
;
744 if (res
< 0) max
= index
;
745 else min
= index
+ 1;
751 /***********************************************************************
754 * Find the item beginning with a given string.
756 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
757 LPCSTR str
, BOOL exact
)
762 if (start
>= descr
->nb_items
) start
= -1;
763 item
= descr
->items
+ start
+ 1;
764 if (HAS_STRINGS(descr
))
766 if (!str
) return LB_ERR
;
769 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
770 if (!lstrcmpiA( str
, item
->str
)) return i
;
771 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
772 if (!lstrcmpiA( str
, item
->str
)) return i
;
776 /* Special case for drives and directories: ignore prefix */
777 #define CHECK_DRIVE(item) \
778 if ((item)->str[0] == '[') \
780 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
781 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
785 INT len
= strlen(str
);
786 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
788 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
791 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
793 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
801 if (exact
&& (descr
->style
& LBS_SORT
))
802 /* If sorted, use a WM_COMPAREITEM binary search */
803 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
805 /* Otherwise use a linear search */
806 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
807 if (item
->data
== (DWORD
)str
) return i
;
808 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
809 if (item
->data
== (DWORD
)str
) return i
;
815 /***********************************************************************
816 * LISTBOX_GetSelCount
818 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
821 LB_ITEMDATA
*item
= descr
->items
;
823 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
824 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
825 if (item
->selected
) count
++;
830 /***********************************************************************
831 * LISTBOX_GetSelItems16
833 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
837 LB_ITEMDATA
*item
= descr
->items
;
839 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
840 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
841 if (item
->selected
) array
[count
++] = (INT16
)i
;
846 /***********************************************************************
847 * LISTBOX_GetSelItems32
849 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
853 LB_ITEMDATA
*item
= descr
->items
;
855 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
856 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
857 if (item
->selected
) array
[count
++] = i
;
862 /***********************************************************************
865 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
867 INT i
, col_pos
= descr
->page_size
- 1;
870 HBRUSH hbrush
, oldBrush
= 0;
872 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
873 if (descr
->style
& LBS_NOREDRAW
) return 0;
874 if (descr
->style
& LBS_MULTICOLUMN
)
875 rect
.right
= rect
.left
+ descr
->column_width
;
876 else if (descr
->horz_pos
)
878 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
879 rect
.right
+= descr
->horz_pos
;
882 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
883 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
884 hdc
, (LPARAM
)wnd
->hwndSelf
);
885 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
886 if (wnd
->dwStyle
& WS_DISABLED
)
887 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
889 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
890 (GetFocus() == wnd
->hwndSelf
))
892 /* Special case for empty listbox: paint focus rect */
893 rect
.bottom
= rect
.top
+ descr
->item_height
;
894 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
896 rect
.top
= rect
.bottom
;
899 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
901 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
902 rect
.bottom
= rect
.top
+ descr
->item_height
;
904 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
906 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
907 rect
.top
= rect
.bottom
;
909 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
911 if (!IS_OWNERDRAW(descr
))
913 /* Clear the bottom of the column */
914 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
915 if (rect
.top
< descr
->height
)
917 rect
.bottom
= descr
->height
;
918 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
919 &rect
, NULL
, 0, NULL
);
923 /* Go to the next column */
924 rect
.left
+= descr
->column_width
;
925 rect
.right
+= descr
->column_width
;
927 col_pos
= descr
->page_size
- 1;
932 if (rect
.top
>= descr
->height
) break;
936 if (!IS_OWNERDRAW(descr
))
938 /* Clear the remainder of the client area */
939 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
940 if (rect
.top
< descr
->height
)
942 rect
.bottom
= descr
->height
;
943 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
944 &rect
, NULL
, 0, NULL
);
946 if (rect
.right
< descr
->width
)
948 rect
.left
= rect
.right
;
949 rect
.right
= descr
->width
;
951 rect
.bottom
= descr
->height
;
952 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
953 &rect
, NULL
, 0, NULL
);
956 if (oldFont
) SelectObject( hdc
, oldFont
);
957 if (oldBrush
) SelectObject( hdc
, oldBrush
);
962 /***********************************************************************
963 * LISTBOX_InvalidateItems
965 * Invalidate all items from a given item. If the specified item is not
966 * visible, nothing happens.
968 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
972 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
974 rect
.bottom
= descr
->height
;
975 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
976 if (descr
->style
& LBS_MULTICOLUMN
)
978 /* Repaint the other columns */
979 rect
.left
= rect
.right
;
980 rect
.right
= descr
->width
;
982 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
988 /***********************************************************************
989 * LISTBOX_GetItemHeight
991 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
993 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
995 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
996 return descr
->items
[index
].height
;
998 else return descr
->item_height
;
1002 /***********************************************************************
1003 * LISTBOX_SetItemHeight
1005 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1008 if (!height
) height
= 1;
1010 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1012 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1013 TRACE(listbox
, "[%04x]: item %d height = %d\n",
1014 wnd
->hwndSelf
, index
, height
);
1015 descr
->items
[index
].height
= height
;
1016 LISTBOX_UpdateScroll( wnd
, descr
);
1017 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1019 else if (height
!= descr
->item_height
)
1021 TRACE(listbox
, "[%04x]: new height = %d\n",
1022 wnd
->hwndSelf
, height
);
1023 descr
->item_height
= height
;
1024 LISTBOX_UpdatePage( wnd
, descr
);
1025 LISTBOX_UpdateScroll( wnd
, descr
);
1026 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1032 /***********************************************************************
1033 * LISTBOX_SetHorizontalPos
1035 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1039 if (pos
> descr
->horz_extent
- descr
->width
)
1040 pos
= descr
->horz_extent
- descr
->width
;
1041 if (pos
< 0) pos
= 0;
1042 if (!(diff
= descr
->horz_pos
- pos
)) return;
1043 TRACE(listbox
, "[%04x]: new horz pos = %d\n",
1044 wnd
->hwndSelf
, pos
);
1045 descr
->horz_pos
= pos
;
1046 LISTBOX_UpdateScroll( wnd
, descr
);
1047 if (abs(diff
) < descr
->width
)
1048 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1049 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1051 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1055 /***********************************************************************
1056 * LISTBOX_SetHorizontalExtent
1058 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1061 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1063 if (extent
<= 0) extent
= 1;
1064 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1065 TRACE(listbox
, "[%04x]: new horz extent = %d\n",
1066 wnd
->hwndSelf
, extent
);
1067 descr
->horz_extent
= extent
;
1068 if (descr
->horz_pos
> extent
- descr
->width
)
1069 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1071 LISTBOX_UpdateScroll( wnd
, descr
);
1076 /***********************************************************************
1077 * LISTBOX_SetColumnWidth
1079 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1081 width
+= 2; /* For left and right margin */
1082 if (width
== descr
->column_width
) return LB_OKAY
;
1083 TRACE(listbox
, "[%04x]: new column width = %d\n",
1084 wnd
->hwndSelf
, width
);
1085 descr
->column_width
= width
;
1086 LISTBOX_UpdatePage( wnd
, descr
);
1091 /***********************************************************************
1094 * Returns the item height.
1096 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1104 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1106 ERR(listbox
, "unable to get DC.\n" );
1109 if (font
) oldFont
= SelectObject( hdc
, font
);
1110 GetTextMetricsA( hdc
, &tm
);
1111 if (oldFont
) SelectObject( hdc
, oldFont
);
1112 ReleaseDC( wnd
->hwndSelf
, hdc
);
1113 if (!IS_OWNERDRAW(descr
))
1114 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1115 return tm
.tmHeight
;
1119 /***********************************************************************
1120 * LISTBOX_MakeItemVisible
1122 * Make sure that a given item is partially or fully visible.
1124 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1129 if (index
<= descr
->top_item
) top
= index
;
1130 else if (descr
->style
& LBS_MULTICOLUMN
)
1132 INT cols
= descr
->width
;
1133 if (!fully
) cols
+= descr
->column_width
- 1;
1134 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1136 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1137 top
= index
- descr
->page_size
* (cols
- 1);
1139 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1141 INT height
= fully
? descr
->items
[index
].height
: 1;
1142 for (top
= index
; top
> descr
->top_item
; top
--)
1143 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1147 if (index
< descr
->top_item
+ descr
->page_size
) return;
1148 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1149 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1150 top
= index
- descr
->page_size
+ 1;
1152 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1156 /***********************************************************************
1157 * LISTBOX_SelectItemRange
1159 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1161 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1166 /* A few sanity checks */
1168 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1169 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1170 if (last
== -1) last
= descr
->nb_items
- 1;
1171 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1172 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1173 /* selected_item reflects last selected/unselected item on multiple sel */
1174 descr
->selected_item
= last
;
1176 if (on
) /* Turn selection on */
1178 for (i
= first
; i
<= last
; i
++)
1180 if (descr
->items
[i
].selected
) continue;
1181 descr
->items
[i
].selected
= TRUE
;
1182 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1185 else /* Turn selection off */
1187 for (i
= first
; i
<= last
; i
++)
1189 if (!descr
->items
[i
].selected
) continue;
1190 descr
->items
[i
].selected
= FALSE
;
1191 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1198 /***********************************************************************
1199 * LISTBOX_SetCaretIndex
1202 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1205 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1206 BOOL fully_visible
)
1208 INT oldfocus
= descr
->focus_item
;
1210 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1211 if (index
== oldfocus
) return LB_OKAY
;
1212 descr
->focus_item
= index
;
1213 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1214 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1216 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1217 if (descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1218 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1224 /***********************************************************************
1225 * LISTBOX_SetSelection
1227 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1228 BOOL on
, BOOL send_notify
)
1230 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1231 if (descr
->style
& LBS_MULTIPLESEL
)
1233 if (index
== -1) /* Select all items */
1234 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1235 else /* Only one item */
1236 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1240 INT oldsel
= descr
->selected_item
;
1241 if (index
== oldsel
) return LB_OKAY
;
1242 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1243 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1244 descr
->selected_item
= index
;
1245 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1246 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1247 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1248 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1250 if( descr
->lphc
) /* set selection change flag for parent combo */
1251 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1257 /***********************************************************************
1260 * Change the caret position and extend the selection to the new caret.
1262 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1263 BOOL fully_visible
)
1265 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1266 if (descr
->style
& LBS_EXTENDEDSEL
)
1268 if (descr
->anchor_item
!= -1)
1270 INT first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1271 INT last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1273 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1274 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1275 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1278 else if (!(descr
->style
& LBS_MULTIPLESEL
) && (descr
->selected_item
!= -1))
1280 /* Set selection to new caret item */
1281 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1286 /***********************************************************************
1287 * LISTBOX_InsertItem
1289 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1290 LPSTR str
, DWORD data
)
1295 if (index
== -1) index
= descr
->nb_items
;
1296 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1297 if (!descr
->items
) max_items
= 0;
1298 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1299 if (descr
->nb_items
== max_items
)
1301 /* We need to grow the array */
1302 max_items
+= LB_ARRAY_GRANULARITY
;
1303 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1304 max_items
* sizeof(LB_ITEMDATA
) )))
1306 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1309 descr
->items
= item
;
1312 /* Insert the item structure */
1314 item
= &descr
->items
[index
];
1315 if (index
< descr
->nb_items
)
1316 RtlMoveMemory( item
+ 1, item
,
1317 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1321 item
->selected
= FALSE
;
1324 /* Get item height */
1326 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1328 MEASUREITEMSTRUCT mis
;
1329 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1331 mis
.CtlType
= ODT_LISTBOX
;
1334 mis
.itemData
= descr
->items
[index
].data
;
1335 mis
.itemHeight
= descr
->item_height
;
1336 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1337 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1338 TRACE(listbox
, "[%04x]: measure item %d (%s) = %d\n",
1339 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1342 /* Repaint the items */
1344 LISTBOX_UpdateScroll( wnd
, descr
);
1345 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1347 /* Move selection and focused item */
1349 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1350 if (index
<= descr
->focus_item
)
1352 descr
->focus_item
++;
1353 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1356 /* If listbox was empty, set focus to the first item */
1358 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1363 /***********************************************************************
1364 * LISTBOX_InsertString
1366 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1369 LPSTR new_str
= NULL
;
1373 if (HAS_STRINGS(descr
))
1375 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1377 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1381 else data
= (DWORD
)str
;
1383 if (index
== -1) index
= descr
->nb_items
;
1384 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1386 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1390 TRACE(listbox
, "[%04x]: added item %d '%s'\n",
1391 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1396 /***********************************************************************
1397 * LISTBOX_DeleteItem
1399 * Delete the content of an item. 'index' must be a valid index.
1401 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1403 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1404 * while Win95 sends it for all items with user data.
1405 * It's probably better to send it too often than not
1406 * often enough, so this is what we do here.
1408 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1410 DELETEITEMSTRUCT dis
;
1411 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1413 dis
.CtlType
= ODT_LISTBOX
;
1416 dis
.hwndItem
= wnd
->hwndSelf
;
1417 dis
.itemData
= descr
->items
[index
].data
;
1418 SendMessageA( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1420 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1421 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1425 /***********************************************************************
1426 * LISTBOX_RemoveItem
1428 * Remove an item from the listbox and delete its content.
1430 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1435 if (index
== -1) index
= descr
->nb_items
- 1;
1436 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1437 LISTBOX_DeleteItem( wnd
, descr
, index
);
1439 /* Remove the item */
1441 item
= &descr
->items
[index
];
1442 if (index
< descr
->nb_items
-1)
1443 RtlMoveMemory( item
, item
+ 1,
1444 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1446 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1448 /* Shrink the item array if possible */
1450 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1451 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1453 max_items
-= LB_ARRAY_GRANULARITY
;
1454 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1455 max_items
* sizeof(LB_ITEMDATA
) );
1456 if (item
) descr
->items
= item
;
1459 /* Repaint the items */
1461 LISTBOX_UpdateScroll( wnd
, descr
);
1462 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1464 /* Move selection and focused item */
1466 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1467 if (index
<= descr
->focus_item
)
1469 descr
->focus_item
--;
1470 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1476 /***********************************************************************
1477 * LISTBOX_ResetContent
1479 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1483 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1484 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1485 descr
->nb_items
= 0;
1486 descr
->top_item
= 0;
1487 descr
->selected_item
= -1;
1488 descr
->focus_item
= 0;
1489 descr
->anchor_item
= -1;
1490 descr
->items
= NULL
;
1491 LISTBOX_UpdateScroll( wnd
, descr
);
1492 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1496 /***********************************************************************
1499 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1503 if (HAS_STRINGS(descr
)) return LB_ERR
;
1504 /* FIXME: this is far from optimal... */
1505 if (count
> descr
->nb_items
)
1507 while (count
> descr
->nb_items
)
1508 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1511 else if (count
< descr
->nb_items
)
1513 while (count
< descr
->nb_items
)
1514 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1521 /***********************************************************************
1524 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1525 LPCSTR filespec
, BOOL long_names
)
1528 LRESULT ret
= LB_OKAY
;
1529 WIN32_FIND_DATAA entry
;
1532 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1534 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1541 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1543 if (!(attrib
& DDL_DIRECTORY
) ||
1544 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1545 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1546 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1548 else /* not a directory */
1550 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1551 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1553 if ((attrib
& DDL_EXCLUSIVE
) &&
1554 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1557 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1558 else strcpy( buffer
, entry
.cAlternateFileName
);
1560 if (!long_names
) CharLowerA( buffer
);
1561 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1562 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1564 } while (FindNextFileA( handle
, &entry
));
1565 FindClose( handle
);
1568 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1570 char buffer
[] = "[-a-]";
1572 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1574 if (!DRIVE_IsValid(drive
)) continue;
1575 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1583 /***********************************************************************
1584 * LISTBOX_HandleVScroll
1586 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1587 WPARAM wParam
, LPARAM lParam
)
1591 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1592 switch(LOWORD(wParam
))
1595 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1598 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1601 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1602 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1605 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1606 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1608 case SB_THUMBPOSITION
:
1609 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1612 info
.cbSize
= sizeof(info
);
1613 info
.fMask
= SIF_TRACKPOS
;
1614 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1615 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1618 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1621 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1628 /***********************************************************************
1629 * LISTBOX_HandleHScroll
1631 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1632 WPARAM wParam
, LPARAM lParam
)
1637 if (descr
->style
& LBS_MULTICOLUMN
)
1639 switch(LOWORD(wParam
))
1642 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1646 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1650 page
= descr
->width
/ descr
->column_width
;
1651 if (page
< 1) page
= 1;
1652 LISTBOX_SetTopItem( wnd
, descr
,
1653 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1656 page
= descr
->width
/ descr
->column_width
;
1657 if (page
< 1) page
= 1;
1658 LISTBOX_SetTopItem( wnd
, descr
,
1659 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1661 case SB_THUMBPOSITION
:
1662 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1666 info
.cbSize
= sizeof(info
);
1667 info
.fMask
= SIF_TRACKPOS
;
1668 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1669 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1673 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1676 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1680 else if (descr
->horz_extent
)
1682 switch(LOWORD(wParam
))
1685 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1688 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1691 LISTBOX_SetHorizontalPos( wnd
, descr
,
1692 descr
->horz_pos
- descr
->width
);
1695 LISTBOX_SetHorizontalPos( wnd
, descr
,
1696 descr
->horz_pos
+ descr
->width
);
1698 case SB_THUMBPOSITION
:
1699 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1702 info
.cbSize
= sizeof(info
);
1703 info
.fMask
= SIF_TRACKPOS
;
1704 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1705 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1708 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1711 LISTBOX_SetHorizontalPos( wnd
, descr
,
1712 descr
->horz_extent
- descr
->width
);
1720 /***********************************************************************
1721 * LISTBOX_HandleLButtonDown
1723 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1724 WPARAM wParam
, INT x
, INT y
)
1726 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1727 TRACE(listbox
, "[%04x]: lbuttondown %d,%d item %d\n",
1728 wnd
->hwndSelf
, x
, y
, index
);
1729 if (!descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
)) return 0;
1732 if (descr
->style
& LBS_EXTENDEDSEL
)
1734 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1735 if (wParam
& MK_CONTROL
)
1737 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1738 LISTBOX_SetSelection( wnd
, descr
, index
,
1739 !descr
->items
[index
].selected
, FALSE
);
1741 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1745 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1746 LISTBOX_SetSelection( wnd
, descr
, index
,
1747 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1748 !descr
->items
[index
].selected
), FALSE
);
1752 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1753 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1754 : descr
->lphc
->self
->hwndSelf
) ;
1756 descr
->captured
= TRUE
;
1757 SetCapture( wnd
->hwndSelf
);
1758 if (index
!= -1 && !descr
->lphc
)
1760 if (descr
->style
& LBS_NOTIFY
)
1761 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1762 MAKELPARAM( x
, y
) );
1763 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1765 POINT pt
= { x
, y
};
1766 if (DragDetect( wnd
->hwndSelf
, pt
))
1767 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1774 /***********************************************************************
1775 * LISTBOX_HandleLButtonUp
1777 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1779 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1780 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1781 LISTBOX_Timer
= LB_TIMER_NONE
;
1782 if (descr
->captured
)
1784 descr
->captured
= FALSE
;
1785 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
1786 if (descr
->style
& LBS_NOTIFY
)
1787 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1793 /***********************************************************************
1794 * LISTBOX_HandleTimer
1796 * Handle scrolling upon a timer event.
1797 * Return TRUE if scrolling should continue.
1799 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1800 INT index
, TIMER_DIRECTION dir
)
1805 if (descr
->top_item
) index
= descr
->top_item
- 1;
1809 if (descr
->top_item
) index
-= descr
->page_size
;
1812 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1813 if (index
== descr
->focus_item
) index
++;
1814 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1816 case LB_TIMER_RIGHT
:
1817 if (index
+ descr
->page_size
< descr
->nb_items
)
1818 index
+= descr
->page_size
;
1823 if (index
== descr
->focus_item
) return FALSE
;
1824 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1829 /***********************************************************************
1830 * LISTBOX_HandleSystemTimer
1832 * WM_SYSTIMER handler.
1834 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1836 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1838 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1839 LISTBOX_Timer
= LB_TIMER_NONE
;
1845 /***********************************************************************
1846 * LISTBOX_HandleMouseMove
1848 * WM_MOUSEMOVE handler.
1850 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1854 TIMER_DIRECTION dir
;
1856 if (!descr
->captured
) return;
1858 if (descr
->style
& LBS_MULTICOLUMN
)
1861 else if (y
>= descr
->item_height
* descr
->page_size
)
1862 y
= descr
->item_height
* descr
->page_size
- 1;
1866 dir
= LB_TIMER_LEFT
;
1869 else if (x
>= descr
->width
)
1871 dir
= LB_TIMER_RIGHT
;
1872 x
= descr
->width
- 1;
1874 else dir
= LB_TIMER_NONE
; /* inside */
1878 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1879 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1880 else dir
= LB_TIMER_NONE
; /* inside */
1883 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1884 if (index
== -1) index
= descr
->focus_item
;
1885 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1887 /* Start/stop the system timer */
1889 if (dir
!= LB_TIMER_NONE
)
1890 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1891 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1892 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1893 LISTBOX_Timer
= dir
;
1897 /***********************************************************************
1898 * LISTBOX_HandleKeyDown
1900 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1903 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1905 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
1906 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1908 if (caret
== -2) return 0;
1910 if (caret
== -1) switch(wParam
)
1913 if (descr
->style
& LBS_MULTICOLUMN
)
1915 if (descr
->focus_item
>= descr
->page_size
)
1916 caret
= descr
->focus_item
- descr
->page_size
;
1921 caret
= descr
->focus_item
- 1;
1922 if (caret
< 0) caret
= 0;
1925 if (descr
->style
& LBS_MULTICOLUMN
)
1927 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1928 caret
= descr
->focus_item
+ descr
->page_size
;
1933 caret
= descr
->focus_item
+ 1;
1934 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1937 if (descr
->style
& LBS_MULTICOLUMN
)
1939 INT page
= descr
->width
/ descr
->column_width
;
1940 if (page
< 1) page
= 1;
1941 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1943 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1944 if (caret
< 0) caret
= 0;
1947 if (descr
->style
& LBS_MULTICOLUMN
)
1949 INT page
= descr
->width
/ descr
->column_width
;
1950 if (page
< 1) page
= 1;
1951 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1953 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1954 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1960 caret
= descr
->nb_items
- 1;
1963 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1964 else if (descr
->style
& LBS_MULTIPLESEL
)
1966 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1967 !descr
->items
[descr
->focus_item
].selected
,
1968 (descr
->style
& LBS_NOTIFY
) != 0 );
1970 else if (descr
->selected_item
== -1)
1972 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1973 (descr
->style
& LBS_NOTIFY
) != 0 );
1979 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1980 !(GetKeyState( VK_SHIFT
) & 0x8000))
1981 descr
->anchor_item
= caret
;
1982 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1983 if (descr
->style
& LBS_NOTIFY
)
1985 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1987 /* make sure that combo parent doesn't hide us */
1988 descr
->lphc
->wState
|= CBF_NOROLLUP
;
1990 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1997 /***********************************************************************
1998 * LISTBOX_HandleChar
2000 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2004 char str
[2] = { wParam
& 0xff, '\0' };
2006 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2008 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2009 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2011 if (caret
== -2) return 0;
2014 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2017 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2018 if (descr
->style
& LBS_NOTIFY
)
2019 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2025 /***********************************************************************
2028 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2031 MEASUREITEMSTRUCT mis
;
2034 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2036 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2038 HeapFree( GetProcessHeap(), 0, descr
);
2041 GetClientRect( wnd
->hwndSelf
, &rect
);
2042 descr
->owner
= GetParent( wnd
->hwndSelf
);
2043 descr
->style
= wnd
->dwStyle
;
2044 descr
->width
= rect
.right
- rect
.left
;
2045 descr
->height
= rect
.bottom
- rect
.top
;
2046 descr
->items
= NULL
;
2047 descr
->nb_items
= 0;
2048 descr
->top_item
= 0;
2049 descr
->selected_item
= -1;
2050 descr
->focus_item
= 0;
2051 descr
->anchor_item
= -1;
2052 descr
->item_height
= 1;
2053 descr
->page_size
= 1;
2054 descr
->column_width
= 150;
2055 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2056 descr
->horz_pos
= 0;
2059 descr
->caret_on
= TRUE
;
2060 descr
->captured
= FALSE
;
2062 descr
->locale
= 0; /* FIXME */
2067 TRACE(combo
,"[%04x]: resetting owner %04x -> %04x\n",
2068 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2069 descr
->owner
= lphc
->self
->hwndSelf
;
2072 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2074 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2076 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2077 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2078 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2079 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2081 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2083 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2085 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2086 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2090 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2092 mis
.CtlType
= ODT_LISTBOX
;
2097 mis
.itemHeight
= descr
->item_height
;
2098 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2099 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2107 /***********************************************************************
2110 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2112 LISTBOX_ResetContent( wnd
, descr
);
2113 HeapDestroy( descr
->heap
);
2114 HeapFree( GetProcessHeap(), 0, descr
);
2120 /***********************************************************************
2123 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2124 WPARAM wParam
, LPARAM lParam
)
2128 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2132 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2134 if (msg
== WM_CREATE
)
2136 if (!LISTBOX_Create( wnd
, NULL
))
2141 TRACE(listbox
, "creating wnd=%04x descr=%p\n",
2142 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2146 /* Ignore all other messages before we get a WM_CREATE */
2147 retvalue
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2151 TRACE(listbox
, "[%04x]: msg %s wp %08x lp %08lx\n",
2152 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2155 case LB_RESETCONTENT16
:
2156 case LB_RESETCONTENT
:
2157 LISTBOX_ResetContent( wnd
, descr
);
2161 case LB_ADDSTRING16
:
2162 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2165 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2166 retvalue
= LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2169 case LB_INSERTSTRING16
:
2170 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2171 wParam
= (INT
)(INT16
)wParam
;
2173 case LB_INSERTSTRING
:
2174 retvalue
= LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2178 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2181 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2182 retvalue
= LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2185 case LB_DELETESTRING16
:
2186 case LB_DELETESTRING
:
2187 retvalue
= LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2190 case LB_GETITEMDATA16
:
2191 case LB_GETITEMDATA
:
2192 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2197 retvalue
= descr
->items
[wParam
].data
;
2200 case LB_SETITEMDATA16
:
2201 case LB_SETITEMDATA
:
2202 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2207 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2213 retvalue
= descr
->nb_items
;
2217 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2220 retvalue
= LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2223 case LB_GETTEXTLEN16
:
2226 if (wParam
>= descr
->nb_items
)
2231 retvalue
= (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2235 case LB_GETCURSEL16
:
2237 if (descr
->nb_items
==0)
2241 retvalue
= descr
->selected_item
;
2242 if (retvalue
== -1) retvalue
= descr
->focus_item
;
2244 /* otherwise, if the user tries to move the selection with the */
2245 /* arrow keys, we will give the application something to choke on */
2248 case LB_GETTOPINDEX16
:
2249 case LB_GETTOPINDEX
:
2250 retvalue
= descr
->top_item
;
2253 case LB_GETITEMHEIGHT16
:
2254 case LB_GETITEMHEIGHT
:
2255 retvalue
= LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2258 case LB_SETITEMHEIGHT16
:
2259 lParam
= LOWORD(lParam
);
2261 case LB_SETITEMHEIGHT
:
2262 retvalue
= LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2265 case LB_ITEMFROMPOINT
:
2267 POINT pt
= { LOWORD(lParam
), HIWORD(lParam
) };
2268 RECT rect
= { 0, 0, descr
->width
, descr
->height
};
2269 retvalue
= MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2270 PtInRect( &rect
, pt
) );
2274 case LB_SETCARETINDEX16
:
2275 case LB_SETCARETINDEX
:
2276 retvalue
= LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2279 case LB_GETCARETINDEX16
:
2280 case LB_GETCARETINDEX
:
2281 retvalue
= descr
->focus_item
;
2284 case LB_SETTOPINDEX16
:
2285 case LB_SETTOPINDEX
:
2286 retvalue
= LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2289 case LB_SETCOLUMNWIDTH16
:
2290 case LB_SETCOLUMNWIDTH
:
2291 retvalue
= LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2294 case LB_GETITEMRECT16
:
2297 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2298 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2303 case LB_GETITEMRECT
:
2304 retvalue
= LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2307 case LB_FINDSTRING16
:
2308 wParam
= (INT
)(INT16
)wParam
;
2309 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2312 retvalue
= LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2315 case LB_FINDSTRINGEXACT16
:
2316 wParam
= (INT
)(INT16
)wParam
;
2317 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2319 case LB_FINDSTRINGEXACT
:
2320 retvalue
= LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2323 case LB_SELECTSTRING16
:
2324 wParam
= (INT
)(INT16
)wParam
;
2325 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2327 case LB_SELECTSTRING
:
2329 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2330 (LPCSTR
)lParam
, FALSE
);
2331 if (index
== LB_ERR
)
2336 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2342 wParam
= (INT
)(INT16
)wParam
;
2345 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2350 retvalue
= descr
->items
[wParam
].selected
;
2354 lParam
= (INT
)(INT16
)lParam
;
2357 retvalue
= LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2360 case LB_SETCURSEL16
:
2361 wParam
= (INT
)(INT16
)wParam
;
2364 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2365 retvalue
= LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2368 case LB_GETSELCOUNT16
:
2369 case LB_GETSELCOUNT
:
2370 retvalue
= LISTBOX_GetSelCount( wnd
, descr
);
2373 case LB_GETSELITEMS16
:
2374 retvalue
= LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2375 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2378 case LB_GETSELITEMS
:
2379 retvalue
= LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2382 case LB_SELITEMRANGE16
:
2383 case LB_SELITEMRANGE
:
2384 if (LOWORD(lParam
) <= HIWORD(lParam
))
2386 retvalue
= LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2387 HIWORD(lParam
), wParam
);
2391 retvalue
= LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2392 LOWORD(lParam
), wParam
);
2396 case LB_SELITEMRANGEEX16
:
2397 case LB_SELITEMRANGEEX
:
2398 if ((INT
)lParam
>= (INT
)wParam
)
2399 retvalue
= LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2401 retvalue
= LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2404 case LB_GETHORIZONTALEXTENT16
:
2405 case LB_GETHORIZONTALEXTENT
:
2406 retvalue
= descr
->horz_extent
;
2409 case LB_SETHORIZONTALEXTENT16
:
2410 case LB_SETHORIZONTALEXTENT
:
2411 retvalue
= LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2414 case LB_GETANCHORINDEX16
:
2415 case LB_GETANCHORINDEX
:
2416 retvalue
= descr
->anchor_item
;
2419 case LB_SETANCHORINDEX16
:
2420 wParam
= (INT
)(INT16
)wParam
;
2422 case LB_SETANCHORINDEX
:
2423 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2428 descr
->anchor_item
= (INT
)wParam
;
2433 retvalue
= LISTBOX_Directory( wnd
, descr
, wParam
,
2434 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2438 retvalue
= LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2442 retvalue
= descr
->locale
;
2446 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2450 case LB_INITSTORAGE
:
2451 retvalue
= LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2455 retvalue
= LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2458 case LB_SETTABSTOPS16
:
2459 retvalue
= LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2460 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2463 case LB_SETTABSTOPS
:
2464 retvalue
= LISTBOX_SetTabStops( wnd
, descr
, wParam
,
2465 (LPINT
)lParam
, FALSE
);
2470 if (descr
->caret_on
)
2475 descr
->caret_on
= TRUE
;
2476 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2477 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2483 if (!descr
->caret_on
)
2488 descr
->caret_on
= FALSE
;
2489 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2490 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2495 retvalue
= LISTBOX_Destroy( wnd
, descr
);
2499 InvalidateRect( hwnd
, NULL
, TRUE
);
2504 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2509 retvalue
=DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2514 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2515 : BeginPaint( hwnd
, &ps
);
2516 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2517 if( !wParam
) EndPaint( hwnd
, &ps
);
2522 LISTBOX_UpdateSize( wnd
, descr
);
2526 retvalue
=descr
->font
;
2529 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2530 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2534 descr
->caret_on
= TRUE
;
2535 if (descr
->focus_item
!= -1)
2536 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2537 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2541 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2542 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2543 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2547 retvalue
=LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2550 retvalue
=LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2552 case WM_LBUTTONDOWN
:
2553 retvalue
=LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2554 (INT16
)LOWORD(lParam
),
2555 (INT16
)HIWORD(lParam
) );
2557 case WM_LBUTTONDBLCLK
:
2558 if (descr
->style
& LBS_NOTIFY
)
2559 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2563 if (GetCapture() == hwnd
)
2564 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2565 (INT16
)HIWORD(lParam
) );
2569 retvalue
=LISTBOX_HandleLButtonUp( wnd
, descr
);
2572 retvalue
=LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2575 retvalue
=LISTBOX_HandleChar( wnd
, descr
, wParam
);
2578 retvalue
=LISTBOX_HandleSystemTimer( wnd
, descr
);
2581 if (IS_OWNERDRAW(descr
))
2584 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2585 wParam
, (LPARAM
)wnd
->hwndSelf
);
2586 GetClientRect(hwnd
, &rect
);
2587 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2594 retvalue
=SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2600 case WM_QUERYDROPOBJECT
:
2605 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2606 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2608 retvalue
=SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2614 if (TWEAK_WineLook
> WIN31_LOOK
)
2615 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2616 retvalue
=DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2619 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2620 WARN(listbox
, "[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2621 hwnd
, msg
, wParam
, lParam
);
2622 retvalue
=DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2627 WIN_ReleaseWndPtr(wnd
);
2631 /***********************************************************************
2634 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2636 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2640 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2643 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2645 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2646 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2647 WIN_ReleaseWndPtr(wnd
);
2650 WIN_ReleaseWndPtr(wnd
);
2655 /***********************************************************************
2658 * NOTE: in Windows, winproc address of the ComboLBox is the same
2659 * as that of the Listbox.
2661 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
2662 WPARAM wParam
, LPARAM lParam
)
2665 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2669 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2671 TRACE(combo
, "[%04x]: msg %s wp %08x lp %08lx\n",
2672 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2674 if( descr
|| msg
== WM_CREATE
)
2676 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2681 #define lpcs ((LPCREATESTRUCTA)lParam)
2682 TRACE(combo
, "\tpassed parent handle = 0x%08x\n",
2683 (UINT
)lpcs
->lpCreateParams
);
2685 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2687 lRet
=LISTBOX_Create( wnd
, lphc
);
2689 case WM_LBUTTONDOWN
:
2690 lRet
=LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2691 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2693 /* avoid activation at all costs */
2695 case WM_MOUSEACTIVATE
:
2696 lRet
=MA_NOACTIVATE
;
2702 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2704 /* for some reason(?) Windows makes it possible to
2705 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2707 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2708 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2709 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2711 COMBO_FlipListbox( lphc
, FALSE
);
2716 lRet
=LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2719 case LB_SETCURSEL16
:
2721 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2722 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2725 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2730 lRet
=ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2734 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2736 TRACE(combo
,"\t default on msg [%04x]\n", (UINT16
)msg
);
2739 WIN_ReleaseWndPtr(wnd
);