4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
17 #include "debugtools.h"
20 DEFAULT_DEBUG_CHANNEL(listbox
)
21 DECLARE_DEBUG_CHANNEL(combo
)
30 /* Items array granularity */
31 #define LB_ARRAY_GRANULARITY 16
33 /* Scrolling timeout in ms */
34 #define LB_SCROLL_TIMEOUT 50
36 /* Listbox system timer id */
42 LPSTR str
; /* Item text */
43 BOOL selected
; /* Is item selected? */
44 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
45 DWORD data
; /* User data */
48 /* Listbox structure */
51 HANDLE heap
; /* Heap for this listbox */
52 HWND owner
; /* Owner window to send notifications to */
53 UINT style
; /* Window style */
54 INT width
; /* Window width */
55 INT height
; /* Window height */
56 LB_ITEMDATA
*items
; /* Array of items */
57 INT nb_items
; /* Number of items */
58 INT top_item
; /* Top visible item */
59 INT selected_item
; /* Selected item */
60 INT focus_item
; /* Item that has the focus */
61 INT anchor_item
; /* Anchor item for extended selection */
62 INT item_height
; /* Default item height */
63 INT page_size
; /* Items per listbox page */
64 INT column_width
; /* Column width for multi-column listboxes */
65 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
66 INT horz_pos
; /* Horizontal position */
67 INT nb_tabs
; /* Number of tabs in array */
68 INT
*tabs
; /* Array of tabs */
69 BOOL caret_on
; /* Is caret on? */
70 BOOL captured
; /* Is mouse captured? */
71 HFONT font
; /* Current font */
72 LCID locale
; /* Current locale for string comparisons */
73 LPHEADCOMBO lphc
; /* ComboLBox */
77 #define IS_OWNERDRAW(descr) \
78 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
80 #define HAS_STRINGS(descr) \
81 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
83 #define SEND_NOTIFICATION(wnd,descr,code) \
84 (SendMessageA( (descr)->owner, WM_COMMAND, \
85 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
87 /* Current timer status */
97 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
100 /***********************************************************************
103 void LISTBOX_Dump( WND
*wnd
)
107 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
109 TRACE( "Listbox:\n" );
110 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
111 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
113 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
115 TRACE( "%4d: %-40s %d %08lx %3d\n",
116 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
121 /***********************************************************************
122 * LISTBOX_GetCurrentPageSize
124 * Return the current page size
126 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
129 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
130 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
132 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
134 if (i
== descr
->top_item
) return 1;
135 else return i
- descr
->top_item
;
139 /***********************************************************************
140 * LISTBOX_GetMaxTopIndex
142 * Return the maximum possible index for the top of the listbox.
144 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
148 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
150 page
= descr
->height
;
151 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
152 if ((page
-= descr
->items
[max
].height
) < 0) break;
153 if (max
< descr
->nb_items
- 1) max
++;
155 else if (descr
->style
& LBS_MULTICOLUMN
)
157 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
158 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
159 max
= (max
- page
) * descr
->page_size
;
163 max
= descr
->nb_items
- descr
->page_size
;
165 if (max
< 0) max
= 0;
170 /***********************************************************************
171 * LISTBOX_UpdateScroll
173 * Update the scrollbars. Should be called whenever the content
174 * of the listbox changes.
176 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
180 if (!(descr
->style
& WS_VSCROLL
)) return;
181 /* It is important that we check descr->style, and not wnd->dwStyle,
182 for WS_VSCROLL, as the former is exactly the one passed in
183 argument to CreateWindow.
184 In Windows (and from now on in Wine :) a listbox created
185 with such a style (no WS_SCROLL) does not update
186 the scrollbar with listbox-related data, thus letting
187 the programmer use it for his/her own purposes. */
189 if (descr
->style
& LBS_NOREDRAW
) return;
190 info
.cbSize
= sizeof(info
);
192 if (descr
->style
& LBS_MULTICOLUMN
)
195 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
196 info
.nPos
= descr
->top_item
/ descr
->page_size
;
197 info
.nPage
= descr
->width
/ descr
->column_width
;
198 if (info
.nPage
< 1) info
.nPage
= 1;
199 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
200 if (descr
->style
& LBS_DISABLENOSCROLL
)
201 info
.fMask
|= SIF_DISABLENOSCROLL
;
202 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
204 info
.fMask
= SIF_RANGE
;
205 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
210 info
.nMax
= descr
->nb_items
- 1;
211 info
.nPos
= descr
->top_item
;
212 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
213 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
214 if (descr
->style
& LBS_DISABLENOSCROLL
)
215 info
.fMask
|= SIF_DISABLENOSCROLL
;
216 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
218 if (descr
->horz_extent
)
221 info
.nMax
= descr
->horz_extent
- 1;
222 info
.nPos
= descr
->horz_pos
;
223 info
.nPage
= descr
->width
;
224 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
225 if (descr
->style
& LBS_DISABLENOSCROLL
)
226 info
.fMask
|= SIF_DISABLENOSCROLL
;
227 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
233 /***********************************************************************
236 * Set the top item of the listbox, scrolling up or down if necessary.
238 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
241 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
242 if (index
> max
) index
= max
;
243 if (index
< 0) index
= 0;
244 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
245 if (descr
->top_item
== index
) return LB_OKAY
;
246 if (descr
->style
& LBS_MULTICOLUMN
)
248 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
249 if (scroll
&& (abs(diff
) < descr
->width
))
250 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
251 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
259 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
263 if (index
> descr
->top_item
)
265 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
266 diff
-= descr
->items
[i
].height
;
270 for (i
= index
; i
< descr
->top_item
; i
++)
271 diff
+= descr
->items
[i
].height
;
275 diff
= (descr
->top_item
- index
) * descr
->item_height
;
277 if (abs(diff
) < descr
->height
)
278 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
279 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
283 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
284 descr
->top_item
= index
;
285 LISTBOX_UpdateScroll( wnd
, descr
);
290 /***********************************************************************
293 * Update the page size. Should be called when the size of
294 * the client area or the item height changes.
296 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
300 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
302 if (page_size
== descr
->page_size
) return;
303 descr
->page_size
= page_size
;
304 if (descr
->style
& LBS_MULTICOLUMN
)
305 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
306 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
310 /***********************************************************************
313 * Update the size of the listbox. Should be called when the size of
314 * the client area changes.
316 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
320 GetClientRect( wnd
->hwndSelf
, &rect
);
321 descr
->width
= rect
.right
- rect
.left
;
322 descr
->height
= rect
.bottom
- rect
.top
;
323 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
325 if ((descr
->height
> descr
->item_height
) &&
326 (descr
->height
% descr
->item_height
))
328 TRACE("[%04x]: changing height %d -> %d\n",
329 wnd
->hwndSelf
, descr
->height
,
330 descr
->height
- descr
->height
%descr
->item_height
);
331 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
332 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
333 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
334 (descr
->height
% descr
->item_height
),
335 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
339 TRACE("[%04x]: new size = %d,%d\n",
340 wnd
->hwndSelf
, descr
->width
, descr
->height
);
341 LISTBOX_UpdatePage( wnd
, descr
);
342 LISTBOX_UpdateScroll( wnd
, descr
);
346 /***********************************************************************
347 * LISTBOX_GetItemRect
349 * Get the rectangle enclosing an item, in listbox client coordinates.
350 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
352 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
355 /* Index <= 0 is legal even on empty listboxes */
356 if (index
&& (index
>= descr
->nb_items
)) return -1;
357 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
358 if (descr
->style
& LBS_MULTICOLUMN
)
360 INT col
= (index
/ descr
->page_size
) -
361 (descr
->top_item
/ descr
->page_size
);
362 rect
->left
+= col
* descr
->column_width
;
363 rect
->right
= rect
->left
+ descr
->column_width
;
364 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
365 rect
->bottom
= rect
->top
+ descr
->item_height
;
367 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
370 rect
->right
+= descr
->horz_pos
;
371 if ((index
>= 0) && (index
< descr
->nb_items
))
373 if (index
< descr
->top_item
)
375 for (i
= descr
->top_item
-1; i
>= index
; i
--)
376 rect
->top
-= descr
->items
[i
].height
;
380 for (i
= descr
->top_item
; i
< index
; i
++)
381 rect
->top
+= descr
->items
[i
].height
;
383 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
389 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
390 rect
->bottom
= rect
->top
+ descr
->item_height
;
391 rect
->right
+= descr
->horz_pos
;
394 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
395 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
399 /***********************************************************************
400 * LISTBOX_GetItemFromPoint
402 * Return the item nearest from point (x,y) (in client coordinates).
404 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
407 INT index
= descr
->top_item
;
409 if (!descr
->nb_items
) return -1; /* No items */
410 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
415 while (index
< descr
->nb_items
)
417 if ((pos
+= descr
->items
[index
].height
) > y
) break;
426 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
430 else if (descr
->style
& LBS_MULTICOLUMN
)
432 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
433 if (y
>= 0) index
+= y
/ descr
->item_height
;
434 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
435 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
439 index
+= (y
/ descr
->item_height
);
441 if (index
< 0) return 0;
442 if (index
>= descr
->nb_items
) return -1;
447 /***********************************************************************
452 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
453 const RECT
*rect
, INT index
, UINT action
)
455 LB_ITEMDATA
*item
= NULL
;
456 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
458 if (IS_OWNERDRAW(descr
))
461 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
465 if (action
== ODA_FOCUS
)
466 DrawFocusRect( hdc
, rect
);
468 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
471 dis
.CtlType
= ODT_LISTBOX
;
473 dis
.hwndItem
= wnd
->hwndSelf
;
474 dis
.itemAction
= action
;
478 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
479 if ((descr
->focus_item
== index
) &&
481 (GetFocus() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
482 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
483 dis
.itemData
= item
? item
->data
: 0;
485 TRACE("[%04x]: drawitem %d (%s) action=%02x "
486 "state=%02x rect=%d,%d-%d,%d\n",
487 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
488 dis
.itemState
, rect
->left
, rect
->top
,
489 rect
->right
, rect
->bottom
);
490 SendMessageA(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
494 COLORREF oldText
= 0, oldBk
= 0;
496 if (action
== ODA_FOCUS
)
498 DrawFocusRect( hdc
, rect
);
501 if (item
&& item
->selected
)
503 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
504 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
507 TRACE("[%04x]: painting %d (%s) action=%02x "
508 "rect=%d,%d-%d,%d\n",
509 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
510 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
512 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
513 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
514 else if (!(descr
->style
& LBS_USETABSTOPS
))
515 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
516 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
517 strlen(item
->str
), NULL
);
520 /* Output empty string to paint background in the full width. */
521 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
522 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
523 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
524 item
->str
, strlen(item
->str
),
525 descr
->nb_tabs
, descr
->tabs
, 0);
527 if (item
&& item
->selected
)
529 SetBkColor( hdc
, oldBk
);
530 SetTextColor( hdc
, oldText
);
532 if ((descr
->focus_item
== index
) &&
534 (GetFocus() == wnd
->hwndSelf
)) DrawFocusRect( hdc
, rect
);
539 /***********************************************************************
542 * Change the redraw flag.
544 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
548 if (!(descr
->style
& LBS_NOREDRAW
)) return;
549 descr
->style
&= ~LBS_NOREDRAW
;
550 LISTBOX_UpdateScroll( wnd
, descr
);
552 else descr
->style
|= LBS_NOREDRAW
;
556 /***********************************************************************
557 * LISTBOX_RepaintItem
559 * Repaint a single item synchronously.
561 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
567 HBRUSH hbrush
, oldBrush
= 0;
569 /* Do not repaint the item if the item is not visible */
570 if ((descr
->style
& LBS_NOREDRAW
) || !IsWindowVisible(wnd
->hwndSelf
)) return;
572 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
573 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
574 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
575 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
576 hdc
, (LPARAM
)wnd
->hwndSelf
);
577 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
578 if (wnd
->dwStyle
& WS_DISABLED
)
579 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
580 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
581 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
582 if (oldFont
) SelectObject( hdc
, oldFont
);
583 if (oldBrush
) SelectObject( hdc
, oldBrush
);
584 ReleaseDC( wnd
->hwndSelf
, hdc
);
588 /***********************************************************************
589 * LISTBOX_InitStorage
591 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
596 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
597 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
599 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
600 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
601 nb_items
* sizeof(LB_ITEMDATA
) )))
603 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
611 /***********************************************************************
612 * LISTBOX_SetTabStops
614 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
615 LPINT tabs
, BOOL short_ints
)
617 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
618 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
619 if (!(descr
->nb_tabs
= count
))
624 /* FIXME: count = 1 */
625 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
626 descr
->nb_tabs
* sizeof(INT
) )))
631 LPINT16 p
= (LPINT16
)tabs
;
632 dbg_decl_str(listbox
, 256);
634 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
635 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
636 if(TRACE_ON(listbox
))
637 dsprintf(listbox
, "%hd ", descr
->tabs
[i
]);
639 TRACE("[%04x]: settabstops %s\n",
640 wnd
->hwndSelf
, dbg_str(listbox
));
642 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
643 /* FIXME: repaint the window? */
648 /***********************************************************************
651 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
654 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
655 if (HAS_STRINGS(descr
))
658 return strlen(descr
->items
[index
].str
);
659 lstrcpyA( buffer
, descr
->items
[index
].str
);
660 return strlen(buffer
);
663 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
664 return sizeof(DWORD
);
669 /***********************************************************************
670 * LISTBOX_FindStringPos
672 * Find the nearest string located before a given string in sort order.
673 * If 'exact' is TRUE, return an error if we don't get an exact match.
675 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
678 INT index
, min
, max
, res
= -1;
680 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
682 max
= descr
->nb_items
;
685 index
= (min
+ max
) / 2;
686 if (HAS_STRINGS(descr
))
687 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
690 COMPAREITEMSTRUCT cis
;
691 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
693 cis
.CtlType
= ODT_LISTBOX
;
695 cis
.hwndItem
= wnd
->hwndSelf
;
697 cis
.itemData1
= descr
->items
[index
].data
;
699 cis
.itemData2
= (DWORD
)str
;
700 cis
.dwLocaleId
= descr
->locale
;
701 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
704 if (!res
) return index
;
705 if (res
> 0) max
= index
;
706 else min
= index
+ 1;
708 return exact
? -1 : max
;
712 /***********************************************************************
713 * LISTBOX_FindFileStrPos
715 * Find the nearest string located before a given string in directory
716 * sort order (i.e. first files, then directories, then drives).
718 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
720 INT min
, max
, res
= -1;
722 if (!HAS_STRINGS(descr
))
723 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
725 max
= descr
->nb_items
;
728 INT index
= (min
+ max
) / 2;
729 const char *p
= descr
->items
[index
].str
;
730 if (*p
== '[') /* drive or directory */
732 if (*str
!= '[') res
= -1;
733 else if (p
[1] == '-') /* drive */
735 if (str
[1] == '-') res
= str
[2] - p
[2];
740 if (str
[1] == '-') res
= 1;
741 else res
= lstrcmpiA( str
, p
);
746 if (*str
== '[') res
= 1;
747 else res
= lstrcmpiA( str
, p
);
749 if (!res
) return index
;
750 if (res
< 0) max
= index
;
751 else min
= index
+ 1;
757 /***********************************************************************
760 * Find the item beginning with a given string.
762 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
763 LPCSTR str
, BOOL exact
)
768 if (start
>= descr
->nb_items
) start
= -1;
769 item
= descr
->items
+ start
+ 1;
770 if (HAS_STRINGS(descr
))
772 if (!str
) return LB_ERR
;
775 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
776 if (!lstrcmpiA( str
, item
->str
)) return i
;
777 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
778 if (!lstrcmpiA( str
, item
->str
)) return i
;
782 /* Special case for drives and directories: ignore prefix */
783 #define CHECK_DRIVE(item) \
784 if ((item)->str[0] == '[') \
786 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
787 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
791 INT len
= strlen(str
);
792 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
794 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
797 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
799 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
807 if (exact
&& (descr
->style
& LBS_SORT
))
808 /* If sorted, use a WM_COMPAREITEM binary search */
809 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
811 /* Otherwise use a linear search */
812 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
813 if (item
->data
== (DWORD
)str
) return i
;
814 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
815 if (item
->data
== (DWORD
)str
) return i
;
821 /***********************************************************************
822 * LISTBOX_GetSelCount
824 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
827 LB_ITEMDATA
*item
= descr
->items
;
829 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
830 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
831 if (item
->selected
) count
++;
836 /***********************************************************************
837 * LISTBOX_GetSelItems16
839 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
843 LB_ITEMDATA
*item
= descr
->items
;
845 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
846 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
847 if (item
->selected
) array
[count
++] = (INT16
)i
;
852 /***********************************************************************
853 * LISTBOX_GetSelItems32
855 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
859 LB_ITEMDATA
*item
= descr
->items
;
861 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
862 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
863 if (item
->selected
) array
[count
++] = i
;
868 /***********************************************************************
871 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
873 INT i
, col_pos
= descr
->page_size
- 1;
876 HBRUSH hbrush
, oldBrush
= 0;
878 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
879 if (descr
->style
& LBS_NOREDRAW
) return 0;
880 if (descr
->style
& LBS_MULTICOLUMN
)
881 rect
.right
= rect
.left
+ descr
->column_width
;
882 else if (descr
->horz_pos
)
884 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
885 rect
.right
+= descr
->horz_pos
;
888 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
889 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
890 hdc
, (LPARAM
)wnd
->hwndSelf
);
891 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
892 if (wnd
->dwStyle
& WS_DISABLED
)
893 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
895 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
896 (GetFocus() == wnd
->hwndSelf
))
898 /* Special case for empty listbox: paint focus rect */
899 rect
.bottom
= rect
.top
+ descr
->item_height
;
900 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
902 rect
.top
= rect
.bottom
;
905 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
907 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
908 rect
.bottom
= rect
.top
+ descr
->item_height
;
910 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
912 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
913 rect
.top
= rect
.bottom
;
915 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
917 if (!IS_OWNERDRAW(descr
))
919 /* Clear the bottom of the column */
920 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
921 if (rect
.top
< descr
->height
)
923 rect
.bottom
= descr
->height
;
924 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
925 &rect
, NULL
, 0, NULL
);
929 /* Go to the next column */
930 rect
.left
+= descr
->column_width
;
931 rect
.right
+= descr
->column_width
;
933 col_pos
= descr
->page_size
- 1;
938 if (rect
.top
>= descr
->height
) break;
942 if (!IS_OWNERDRAW(descr
))
944 /* Clear the remainder of the client area */
945 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
946 if (rect
.top
< descr
->height
)
948 rect
.bottom
= descr
->height
;
949 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
950 &rect
, NULL
, 0, NULL
);
952 if (rect
.right
< descr
->width
)
954 rect
.left
= rect
.right
;
955 rect
.right
= descr
->width
;
957 rect
.bottom
= descr
->height
;
958 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
959 &rect
, NULL
, 0, NULL
);
962 if (oldFont
) SelectObject( hdc
, oldFont
);
963 if (oldBrush
) SelectObject( hdc
, oldBrush
);
968 /***********************************************************************
969 * LISTBOX_InvalidateItems
971 * Invalidate all items from a given item. If the specified item is not
972 * visible, nothing happens.
974 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
978 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
980 rect
.bottom
= descr
->height
;
981 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
982 if (descr
->style
& LBS_MULTICOLUMN
)
984 /* Repaint the other columns */
985 rect
.left
= rect
.right
;
986 rect
.right
= descr
->width
;
988 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
994 /***********************************************************************
995 * LISTBOX_GetItemHeight
997 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
999 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1001 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1002 return descr
->items
[index
].height
;
1004 else return descr
->item_height
;
1008 /***********************************************************************
1009 * LISTBOX_SetItemHeight
1011 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1014 if (!height
) height
= 1;
1016 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1018 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1019 TRACE("[%04x]: item %d height = %d\n",
1020 wnd
->hwndSelf
, index
, height
);
1021 descr
->items
[index
].height
= height
;
1022 LISTBOX_UpdateScroll( wnd
, descr
);
1023 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1025 else if (height
!= descr
->item_height
)
1027 TRACE("[%04x]: new height = %d\n",
1028 wnd
->hwndSelf
, height
);
1029 descr
->item_height
= height
;
1030 LISTBOX_UpdatePage( wnd
, descr
);
1031 LISTBOX_UpdateScroll( wnd
, descr
);
1032 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1038 /***********************************************************************
1039 * LISTBOX_SetHorizontalPos
1041 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1045 if (pos
> descr
->horz_extent
- descr
->width
)
1046 pos
= descr
->horz_extent
- descr
->width
;
1047 if (pos
< 0) pos
= 0;
1048 if (!(diff
= descr
->horz_pos
- pos
)) return;
1049 TRACE("[%04x]: new horz pos = %d\n",
1050 wnd
->hwndSelf
, pos
);
1051 descr
->horz_pos
= pos
;
1052 LISTBOX_UpdateScroll( wnd
, descr
);
1053 if (abs(diff
) < descr
->width
)
1054 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1055 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1057 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1061 /***********************************************************************
1062 * LISTBOX_SetHorizontalExtent
1064 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1067 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1069 if (extent
<= 0) extent
= 1;
1070 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1071 TRACE("[%04x]: new horz extent = %d\n",
1072 wnd
->hwndSelf
, extent
);
1073 descr
->horz_extent
= extent
;
1074 if (descr
->horz_pos
> extent
- descr
->width
)
1075 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1077 LISTBOX_UpdateScroll( wnd
, descr
);
1082 /***********************************************************************
1083 * LISTBOX_SetColumnWidth
1085 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1087 width
+= 2; /* For left and right margin */
1088 if (width
== descr
->column_width
) return LB_OKAY
;
1089 TRACE("[%04x]: new column width = %d\n",
1090 wnd
->hwndSelf
, width
);
1091 descr
->column_width
= width
;
1092 LISTBOX_UpdatePage( wnd
, descr
);
1097 /***********************************************************************
1100 * Returns the item height.
1102 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1110 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1112 ERR("unable to get DC.\n" );
1115 if (font
) oldFont
= SelectObject( hdc
, font
);
1116 GetTextMetricsA( hdc
, &tm
);
1117 if (oldFont
) SelectObject( hdc
, oldFont
);
1118 ReleaseDC( wnd
->hwndSelf
, hdc
);
1119 if (!IS_OWNERDRAW(descr
))
1120 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1121 return tm
.tmHeight
;
1125 /***********************************************************************
1126 * LISTBOX_MakeItemVisible
1128 * Make sure that a given item is partially or fully visible.
1130 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1135 if (index
<= descr
->top_item
) top
= index
;
1136 else if (descr
->style
& LBS_MULTICOLUMN
)
1138 INT cols
= descr
->width
;
1139 if (!fully
) cols
+= descr
->column_width
- 1;
1140 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1142 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1143 top
= index
- descr
->page_size
* (cols
- 1);
1145 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1147 INT height
= fully
? descr
->items
[index
].height
: 1;
1148 for (top
= index
; top
> descr
->top_item
; top
--)
1149 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1153 if (index
< descr
->top_item
+ descr
->page_size
) return;
1154 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1155 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1156 top
= index
- descr
->page_size
+ 1;
1158 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1162 /***********************************************************************
1163 * LISTBOX_SelectItemRange
1165 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1167 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1172 /* A few sanity checks */
1174 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1175 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1176 if (last
== -1) last
= descr
->nb_items
- 1;
1177 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1178 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1179 /* selected_item reflects last selected/unselected item on multiple sel */
1180 descr
->selected_item
= last
;
1182 if (on
) /* Turn selection on */
1184 for (i
= first
; i
<= last
; i
++)
1186 if (descr
->items
[i
].selected
) continue;
1187 descr
->items
[i
].selected
= TRUE
;
1188 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1191 else /* Turn selection off */
1193 for (i
= first
; i
<= last
; i
++)
1195 if (!descr
->items
[i
].selected
) continue;
1196 descr
->items
[i
].selected
= FALSE
;
1197 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1204 /***********************************************************************
1205 * LISTBOX_SetCaretIndex
1208 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1211 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1212 BOOL fully_visible
)
1214 INT oldfocus
= descr
->focus_item
;
1216 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1217 if (index
== oldfocus
) return LB_OKAY
;
1218 descr
->focus_item
= index
;
1219 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1220 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1222 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1223 if (descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1224 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1230 /***********************************************************************
1231 * LISTBOX_SetSelection
1233 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1234 BOOL on
, BOOL send_notify
)
1236 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1237 if (descr
->style
& LBS_MULTIPLESEL
)
1239 if (index
== -1) /* Select all items */
1240 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1241 else /* Only one item */
1242 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1246 INT oldsel
= descr
->selected_item
;
1247 if (index
== oldsel
) return LB_OKAY
;
1248 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1249 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1250 descr
->selected_item
= index
;
1251 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1252 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1253 if (send_notify
) SEND_NOTIFICATION( wnd
, descr
,
1254 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1256 if( descr
->lphc
) /* set selection change flag for parent combo */
1257 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1263 /***********************************************************************
1266 * Change the caret position and extend the selection to the new caret.
1268 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1269 BOOL fully_visible
)
1271 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1272 if (descr
->style
& LBS_EXTENDEDSEL
)
1274 if (descr
->anchor_item
!= -1)
1276 INT first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1277 INT last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1279 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1280 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1281 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1284 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1286 /* Set selection to new caret item */
1287 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1292 /***********************************************************************
1293 * LISTBOX_InsertItem
1295 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1296 LPSTR str
, DWORD data
)
1301 if (index
== -1) index
= descr
->nb_items
;
1302 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1303 if (!descr
->items
) max_items
= 0;
1304 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1305 if (descr
->nb_items
== max_items
)
1307 /* We need to grow the array */
1308 max_items
+= LB_ARRAY_GRANULARITY
;
1309 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1310 max_items
* sizeof(LB_ITEMDATA
) )))
1312 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1315 descr
->items
= item
;
1318 /* Insert the item structure */
1320 item
= &descr
->items
[index
];
1321 if (index
< descr
->nb_items
)
1322 RtlMoveMemory( item
+ 1, item
,
1323 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1327 item
->selected
= FALSE
;
1330 /* Get item height */
1332 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1334 MEASUREITEMSTRUCT mis
;
1335 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1337 mis
.CtlType
= ODT_LISTBOX
;
1340 mis
.itemData
= descr
->items
[index
].data
;
1341 mis
.itemHeight
= descr
->item_height
;
1342 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1343 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1344 TRACE("[%04x]: measure item %d (%s) = %d\n",
1345 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1348 /* Repaint the items */
1350 LISTBOX_UpdateScroll( wnd
, descr
);
1351 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1353 /* Move selection and focused item */
1355 if (index
<= descr
->selected_item
) descr
->selected_item
++;
1356 if (index
<= descr
->focus_item
)
1358 descr
->focus_item
++;
1359 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1362 /* If listbox was empty, set focus to the first item */
1364 if (descr
->nb_items
== 1) LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1369 /***********************************************************************
1370 * LISTBOX_InsertString
1372 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1375 LPSTR new_str
= NULL
;
1379 if (HAS_STRINGS(descr
))
1381 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1383 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1387 else data
= (DWORD
)str
;
1389 if (index
== -1) index
= descr
->nb_items
;
1390 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1392 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1396 TRACE("[%04x]: added item %d '%s'\n",
1397 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1402 /***********************************************************************
1403 * LISTBOX_DeleteItem
1405 * Delete the content of an item. 'index' must be a valid index.
1407 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1409 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1410 * while Win95 sends it for all items with user data.
1411 * It's probably better to send it too often than not
1412 * often enough, so this is what we do here.
1414 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1416 DELETEITEMSTRUCT dis
;
1417 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1419 dis
.CtlType
= ODT_LISTBOX
;
1422 dis
.hwndItem
= wnd
->hwndSelf
;
1423 dis
.itemData
= descr
->items
[index
].data
;
1424 SendMessageA( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1426 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1427 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1431 /***********************************************************************
1432 * LISTBOX_RemoveItem
1434 * Remove an item from the listbox and delete its content.
1436 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1441 if (index
== -1) index
= descr
->nb_items
- 1;
1442 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1443 LISTBOX_DeleteItem( wnd
, descr
, index
);
1445 /* Remove the item */
1447 item
= &descr
->items
[index
];
1448 if (index
< descr
->nb_items
-1)
1449 RtlMoveMemory( item
, item
+ 1,
1450 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1452 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1454 /* Shrink the item array if possible */
1456 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1457 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1459 max_items
-= LB_ARRAY_GRANULARITY
;
1460 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1461 max_items
* sizeof(LB_ITEMDATA
) );
1462 if (item
) descr
->items
= item
;
1465 /* Repaint the items */
1467 LISTBOX_UpdateScroll( wnd
, descr
);
1468 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1470 /* Move selection and focused item */
1472 if (index
<= descr
->selected_item
) descr
->selected_item
--;
1473 if (index
<= descr
->focus_item
)
1475 descr
->focus_item
--;
1476 LISTBOX_MoveCaret( wnd
, descr
, descr
->focus_item
, FALSE
);
1482 /***********************************************************************
1483 * LISTBOX_ResetContent
1485 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1489 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1490 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1491 descr
->nb_items
= 0;
1492 descr
->top_item
= 0;
1493 descr
->selected_item
= -1;
1494 descr
->focus_item
= 0;
1495 descr
->anchor_item
= -1;
1496 descr
->items
= NULL
;
1497 LISTBOX_UpdateScroll( wnd
, descr
);
1498 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1502 /***********************************************************************
1505 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1509 if (HAS_STRINGS(descr
)) return LB_ERR
;
1510 /* FIXME: this is far from optimal... */
1511 if (count
> descr
->nb_items
)
1513 while (count
> descr
->nb_items
)
1514 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1517 else if (count
< descr
->nb_items
)
1519 while (count
< descr
->nb_items
)
1520 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1527 /***********************************************************************
1530 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1531 LPCSTR filespec
, BOOL long_names
)
1534 LRESULT ret
= LB_OKAY
;
1535 WIN32_FIND_DATAA entry
;
1538 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1540 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1547 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1549 if (!(attrib
& DDL_DIRECTORY
) ||
1550 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1551 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1552 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1554 else /* not a directory */
1556 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1557 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1559 if ((attrib
& DDL_EXCLUSIVE
) &&
1560 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1563 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1564 else strcpy( buffer
, entry
.cAlternateFileName
);
1566 if (!long_names
) CharLowerA( buffer
);
1567 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1568 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1570 } while (FindNextFileA( handle
, &entry
));
1571 FindClose( handle
);
1574 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1576 char buffer
[] = "[-a-]";
1578 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1580 if (!DRIVE_IsValid(drive
)) continue;
1581 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1589 /***********************************************************************
1590 * LISTBOX_HandleVScroll
1592 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1593 WPARAM wParam
, LPARAM lParam
)
1597 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1598 switch(LOWORD(wParam
))
1601 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1604 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1607 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1608 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1611 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1612 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1614 case SB_THUMBPOSITION
:
1615 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1618 info
.cbSize
= sizeof(info
);
1619 info
.fMask
= SIF_TRACKPOS
;
1620 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1621 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1624 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1627 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1634 /***********************************************************************
1635 * LISTBOX_HandleHScroll
1637 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1638 WPARAM wParam
, LPARAM lParam
)
1643 if (descr
->style
& LBS_MULTICOLUMN
)
1645 switch(LOWORD(wParam
))
1648 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1652 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
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
);
1662 page
= descr
->width
/ descr
->column_width
;
1663 if (page
< 1) page
= 1;
1664 LISTBOX_SetTopItem( wnd
, descr
,
1665 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1667 case SB_THUMBPOSITION
:
1668 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1672 info
.cbSize
= sizeof(info
);
1673 info
.fMask
= SIF_TRACKPOS
;
1674 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1675 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1679 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1682 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1686 else if (descr
->horz_extent
)
1688 switch(LOWORD(wParam
))
1691 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1694 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1697 LISTBOX_SetHorizontalPos( wnd
, descr
,
1698 descr
->horz_pos
- descr
->width
);
1701 LISTBOX_SetHorizontalPos( wnd
, descr
,
1702 descr
->horz_pos
+ descr
->width
);
1704 case SB_THUMBPOSITION
:
1705 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1708 info
.cbSize
= sizeof(info
);
1709 info
.fMask
= SIF_TRACKPOS
;
1710 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1711 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1714 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1717 LISTBOX_SetHorizontalPos( wnd
, descr
,
1718 descr
->horz_extent
- descr
->width
);
1726 /***********************************************************************
1727 * LISTBOX_HandleLButtonDown
1729 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1730 WPARAM wParam
, INT x
, INT y
)
1732 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1733 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1734 wnd
->hwndSelf
, x
, y
, index
);
1735 if (!descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
)) return 0;
1738 if (descr
->style
& LBS_EXTENDEDSEL
)
1740 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1741 if (wParam
& MK_CONTROL
)
1743 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1744 LISTBOX_SetSelection( wnd
, descr
, index
,
1745 !descr
->items
[index
].selected
, FALSE
);
1747 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1751 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1752 LISTBOX_SetSelection( wnd
, descr
, index
,
1753 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1754 !descr
->items
[index
].selected
), FALSE
);
1758 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1759 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1760 : descr
->lphc
->self
->hwndSelf
) ;
1762 descr
->captured
= TRUE
;
1763 SetCapture( wnd
->hwndSelf
);
1764 if (index
!= -1 && !descr
->lphc
)
1766 if (descr
->style
& LBS_NOTIFY
)
1767 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1768 MAKELPARAM( x
, y
) );
1769 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1776 if (DragDetect( wnd
->hwndSelf
, pt
))
1777 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1784 /***********************************************************************
1785 * LISTBOX_HandleLButtonUp
1787 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1789 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1790 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1791 LISTBOX_Timer
= LB_TIMER_NONE
;
1792 if (descr
->captured
)
1794 descr
->captured
= FALSE
;
1795 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
1796 if (descr
->style
& LBS_NOTIFY
)
1797 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1803 /***********************************************************************
1804 * LISTBOX_HandleTimer
1806 * Handle scrolling upon a timer event.
1807 * Return TRUE if scrolling should continue.
1809 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1810 INT index
, TIMER_DIRECTION dir
)
1815 if (descr
->top_item
) index
= descr
->top_item
- 1;
1819 if (descr
->top_item
) index
-= descr
->page_size
;
1822 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1823 if (index
== descr
->focus_item
) index
++;
1824 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1826 case LB_TIMER_RIGHT
:
1827 if (index
+ descr
->page_size
< descr
->nb_items
)
1828 index
+= descr
->page_size
;
1833 if (index
== descr
->focus_item
) return FALSE
;
1834 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1839 /***********************************************************************
1840 * LISTBOX_HandleSystemTimer
1842 * WM_SYSTIMER handler.
1844 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1846 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1848 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1849 LISTBOX_Timer
= LB_TIMER_NONE
;
1855 /***********************************************************************
1856 * LISTBOX_HandleMouseMove
1858 * WM_MOUSEMOVE handler.
1860 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1864 TIMER_DIRECTION dir
;
1866 if (!descr
->captured
) return;
1868 if (descr
->style
& LBS_MULTICOLUMN
)
1871 else if (y
>= descr
->item_height
* descr
->page_size
)
1872 y
= descr
->item_height
* descr
->page_size
- 1;
1876 dir
= LB_TIMER_LEFT
;
1879 else if (x
>= descr
->width
)
1881 dir
= LB_TIMER_RIGHT
;
1882 x
= descr
->width
- 1;
1884 else dir
= LB_TIMER_NONE
; /* inside */
1888 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1889 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1890 else dir
= LB_TIMER_NONE
; /* inside */
1893 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1894 if (index
== -1) index
= descr
->focus_item
;
1895 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1897 /* Start/stop the system timer */
1899 if (dir
!= LB_TIMER_NONE
)
1900 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1901 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1902 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1903 LISTBOX_Timer
= dir
;
1907 /***********************************************************************
1908 * LISTBOX_HandleKeyDown
1910 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1913 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1915 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
1916 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1918 if (caret
== -2) return 0;
1920 if (caret
== -1) switch(wParam
)
1923 if (descr
->style
& LBS_MULTICOLUMN
)
1925 if (descr
->focus_item
>= descr
->page_size
)
1926 caret
= descr
->focus_item
- descr
->page_size
;
1931 caret
= descr
->focus_item
- 1;
1932 if (caret
< 0) caret
= 0;
1935 if (descr
->style
& LBS_MULTICOLUMN
)
1937 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1938 caret
= descr
->focus_item
+ descr
->page_size
;
1943 caret
= descr
->focus_item
+ 1;
1944 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
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
< 0) caret
= 0;
1957 if (descr
->style
& LBS_MULTICOLUMN
)
1959 INT page
= descr
->width
/ descr
->column_width
;
1960 if (page
< 1) page
= 1;
1961 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1963 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1964 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1970 caret
= descr
->nb_items
- 1;
1973 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
1974 else if (descr
->style
& LBS_MULTIPLESEL
)
1976 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
1977 !descr
->items
[descr
->focus_item
].selected
,
1978 (descr
->style
& LBS_NOTIFY
) != 0 );
1980 else if (descr
->selected_item
== -1)
1982 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
, TRUE
,
1983 (descr
->style
& LBS_NOTIFY
) != 0 );
1989 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
1990 !(GetKeyState( VK_SHIFT
) & 0x8000))
1991 descr
->anchor_item
= caret
;
1992 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
1993 if (descr
->style
& LBS_NOTIFY
)
1995 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
1997 /* make sure that combo parent doesn't hide us */
1998 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2000 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2007 /***********************************************************************
2008 * LISTBOX_HandleChar
2010 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2016 str
[0] = wParam
& 0xff;
2019 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2021 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2022 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2024 if (caret
== -2) return 0;
2027 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2030 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2031 if (descr
->style
& LBS_NOTIFY
)
2032 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2038 /***********************************************************************
2041 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2044 MEASUREITEMSTRUCT mis
;
2047 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2049 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2051 HeapFree( GetProcessHeap(), 0, descr
);
2054 GetClientRect( wnd
->hwndSelf
, &rect
);
2055 descr
->owner
= GetParent( wnd
->hwndSelf
);
2056 descr
->style
= wnd
->dwStyle
;
2057 descr
->width
= rect
.right
- rect
.left
;
2058 descr
->height
= rect
.bottom
- rect
.top
;
2059 descr
->items
= NULL
;
2060 descr
->nb_items
= 0;
2061 descr
->top_item
= 0;
2062 descr
->selected_item
= -1;
2063 descr
->focus_item
= 0;
2064 descr
->anchor_item
= -1;
2065 descr
->item_height
= 1;
2066 descr
->page_size
= 1;
2067 descr
->column_width
= 150;
2068 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2069 descr
->horz_pos
= 0;
2072 descr
->caret_on
= TRUE
;
2073 descr
->captured
= FALSE
;
2075 descr
->locale
= 0; /* FIXME */
2080 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2081 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2082 descr
->owner
= lphc
->self
->hwndSelf
;
2085 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2087 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2089 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2090 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2091 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2092 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2094 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2096 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2098 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2099 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2103 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2105 mis
.CtlType
= ODT_LISTBOX
;
2110 mis
.itemHeight
= descr
->item_height
;
2111 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2112 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2120 /***********************************************************************
2123 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2125 LISTBOX_ResetContent( wnd
, descr
);
2126 HeapDestroy( descr
->heap
);
2127 HeapFree( GetProcessHeap(), 0, descr
);
2133 /***********************************************************************
2136 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2137 WPARAM wParam
, LPARAM lParam
)
2141 HWND hwnd
= wnd
->hwndSelf
;
2144 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2146 if (msg
== WM_CREATE
)
2148 if (!LISTBOX_Create( wnd
, NULL
))
2150 TRACE("creating wnd=%04x descr=%p\n",
2151 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2154 /* Ignore all other messages before we get a WM_CREATE */
2155 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2158 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2159 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2162 case LB_RESETCONTENT16
:
2163 case LB_RESETCONTENT
:
2164 LISTBOX_ResetContent( wnd
, descr
);
2167 case LB_ADDSTRING16
:
2168 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2171 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2172 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2174 case LB_INSERTSTRING16
:
2175 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2176 wParam
= (INT
)(INT16
)wParam
;
2178 case LB_INSERTSTRING
:
2179 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2182 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2185 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2186 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2188 case LB_DELETESTRING16
:
2189 case LB_DELETESTRING
:
2190 return LISTBOX_RemoveItem( wnd
, descr
, wParam
);
2192 case LB_GETITEMDATA16
:
2193 case LB_GETITEMDATA
:
2194 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2196 return descr
->items
[wParam
].data
;
2198 case LB_SETITEMDATA16
:
2199 case LB_SETITEMDATA
:
2200 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2202 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2207 return descr
->nb_items
;
2210 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2213 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2215 case LB_GETTEXTLEN16
:
2218 if (wParam
>= descr
->nb_items
)
2220 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2223 case LB_GETCURSEL16
:
2225 if (descr
->nb_items
==0)
2228 if (descr
->selected_item
!=-1)
2229 return descr
->selected_item
;
2231 return descr
->focus_item
;
2232 /* otherwise, if the user tries to move the selection with the */
2233 /* arrow keys, we will give the application something to choke on */
2234 case LB_GETTOPINDEX16
:
2235 case LB_GETTOPINDEX
:
2236 return descr
->top_item
;
2238 case LB_GETITEMHEIGHT16
:
2239 case LB_GETITEMHEIGHT
:
2240 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2242 case LB_SETITEMHEIGHT16
:
2243 lParam
= LOWORD(lParam
);
2245 case LB_SETITEMHEIGHT
:
2246 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2248 case LB_ITEMFROMPOINT
:
2253 pt
.x
= LOWORD(lParam
);
2254 pt
.y
= HIWORD(lParam
);
2257 rect
.right
= descr
->width
;
2258 rect
.bottom
= descr
->height
;
2260 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2261 !PtInRect( &rect
, pt
) );
2264 case LB_SETCARETINDEX16
:
2265 case LB_SETCARETINDEX
:
2266 return LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
);
2268 case LB_GETCARETINDEX16
:
2269 case LB_GETCARETINDEX
:
2270 return descr
->focus_item
;
2272 case LB_SETTOPINDEX16
:
2273 case LB_SETTOPINDEX
:
2274 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2276 case LB_SETCOLUMNWIDTH16
:
2277 case LB_SETCOLUMNWIDTH
:
2278 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2280 case LB_GETITEMRECT16
:
2283 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2284 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2288 case LB_GETITEMRECT
:
2289 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2291 case LB_FINDSTRING16
:
2292 wParam
= (INT
)(INT16
)wParam
;
2293 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2296 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2298 case LB_FINDSTRINGEXACT16
:
2299 wParam
= (INT
)(INT16
)wParam
;
2300 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2302 case LB_FINDSTRINGEXACT
:
2303 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2305 case LB_SELECTSTRING16
:
2306 wParam
= (INT
)(INT16
)wParam
;
2307 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2309 case LB_SELECTSTRING
:
2311 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2312 (LPCSTR
)lParam
, FALSE
);
2313 if (index
== LB_ERR
)
2315 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2320 wParam
= (INT
)(INT16
)wParam
;
2323 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2325 return descr
->items
[wParam
].selected
;
2328 lParam
= (INT
)(INT16
)lParam
;
2331 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2333 case LB_SETCURSEL16
:
2334 wParam
= (INT
)(INT16
)wParam
;
2337 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2338 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2340 case LB_GETSELCOUNT16
:
2341 case LB_GETSELCOUNT
:
2342 return LISTBOX_GetSelCount( wnd
, descr
);
2344 case LB_GETSELITEMS16
:
2345 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2346 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2348 case LB_GETSELITEMS
:
2349 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2351 case LB_SELITEMRANGE16
:
2352 case LB_SELITEMRANGE
:
2353 if (LOWORD(lParam
) <= HIWORD(lParam
))
2354 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2355 HIWORD(lParam
), wParam
);
2357 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2358 LOWORD(lParam
), wParam
);
2360 case LB_SELITEMRANGEEX16
:
2361 case LB_SELITEMRANGEEX
:
2362 if ((INT
)lParam
>= (INT
)wParam
)
2363 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2365 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2367 case LB_GETHORIZONTALEXTENT16
:
2368 case LB_GETHORIZONTALEXTENT
:
2369 return descr
->horz_extent
;
2371 case LB_SETHORIZONTALEXTENT16
:
2372 case LB_SETHORIZONTALEXTENT
:
2373 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2375 case LB_GETANCHORINDEX16
:
2376 case LB_GETANCHORINDEX
:
2377 return descr
->anchor_item
;
2379 case LB_SETANCHORINDEX16
:
2380 wParam
= (INT
)(INT16
)wParam
;
2382 case LB_SETANCHORINDEX
:
2383 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2385 descr
->anchor_item
= (INT
)wParam
;
2389 return LISTBOX_Directory( wnd
, descr
, wParam
,
2390 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2393 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2396 return descr
->locale
;
2399 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2402 case LB_INITSTORAGE
:
2403 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2406 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2408 case LB_SETTABSTOPS16
:
2409 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2410 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2412 case LB_SETTABSTOPS
:
2413 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2417 if (descr
->caret_on
)
2419 descr
->caret_on
= TRUE
;
2420 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2421 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2426 if (!descr
->caret_on
)
2428 descr
->caret_on
= FALSE
;
2429 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2430 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2434 return LISTBOX_Destroy( wnd
, descr
);
2437 InvalidateRect( hwnd
, NULL
, TRUE
);
2441 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2445 return DLGC_WANTARROWS
| DLGC_WANTCHARS
;
2450 HDC hdc
= ( wParam
) ? ((HDC
)wParam
)
2451 : BeginPaint( hwnd
, &ps
);
2452 ret
= LISTBOX_Paint( wnd
, descr
, hdc
);
2453 if( !wParam
) EndPaint( hwnd
, &ps
);
2457 LISTBOX_UpdateSize( wnd
, descr
);
2462 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2463 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2466 descr
->caret_on
= TRUE
;
2467 if (descr
->focus_item
!= -1)
2468 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2469 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2472 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2473 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2474 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2477 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2479 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2480 case WM_LBUTTONDOWN
:
2481 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2482 (INT16
)LOWORD(lParam
),
2483 (INT16
)HIWORD(lParam
) );
2484 case WM_LBUTTONDBLCLK
:
2485 if (descr
->style
& LBS_NOTIFY
)
2486 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2489 if (GetCapture() == hwnd
)
2490 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2491 (INT16
)HIWORD(lParam
) );
2494 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2496 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2498 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2500 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2502 if (IS_OWNERDRAW(descr
))
2505 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2506 wParam
, (LPARAM
)wnd
->hwndSelf
);
2507 GetClientRect(hwnd
, &rect
);
2508 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2513 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2517 case WM_QUERYDROPOBJECT
:
2522 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2523 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2525 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2530 if (TWEAK_WineLook
> WIN31_LOOK
)
2531 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2532 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2534 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2535 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2536 hwnd
, msg
, wParam
, lParam
);
2537 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2542 /***********************************************************************
2545 * This is just a wrapper for the real wndproc, it only does window locking
2548 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2549 WPARAM wParam
, LPARAM lParam
)
2551 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2552 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2554 WIN_ReleaseWndPtr(wndPtr
);
2558 /***********************************************************************
2561 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2563 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2567 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2570 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2572 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2573 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2574 WIN_ReleaseWndPtr(wnd
);
2577 WIN_ReleaseWndPtr(wnd
);
2582 /***********************************************************************
2583 * ComboLBWndProc_locked
2585 * The real combo listbox wndproc, but called with locked WND struct.
2587 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2588 WPARAM wParam
, LPARAM lParam
)
2591 HWND hwnd
= wnd
->hwndSelf
;
2595 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2597 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2598 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2600 if( descr
|| msg
== WM_CREATE
)
2602 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2607 #define lpcs ((LPCREATESTRUCTA)lParam)
2608 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2609 (UINT
)lpcs
->lpCreateParams
);
2611 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2613 return LISTBOX_Create( wnd
, lphc
);
2615 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2616 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2622 mousePos
.x
= (INT16
)LOWORD(lParam
);
2623 mousePos
.y
= (INT16
)HIWORD(lParam
);
2626 * If we are in a dropdown combobox, we simulate that
2627 * the mouse is captured to show the tracking of the item.
2629 captured
= descr
->captured
;
2630 descr
->captured
= TRUE
;
2632 LISTBOX_HandleMouseMove( wnd
,
2634 mousePos
.x
, mousePos
.y
);
2636 descr
->captured
= captured
;
2639 * However, when tracking, it is important that we do not
2640 * perform a selection if the cursor is outside the list.
2642 GetClientRect(hwnd
, &clientRect
);
2644 if (!PtInRect( &clientRect
, mousePos
))
2646 LISTBOX_MoveCaret( wnd
, descr
, -1, FALSE
);
2654 * If we are in Win3.1 look, go with the default behavior.
2656 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2659 if (TWEAK_WineLook
> WIN31_LOOK
)
2665 * If the mouse button "up" is not in the listbox,
2666 * we make sure there is no selection by re-selecting the
2667 * item that was selected when the listbox was made visible.
2669 mousePos
.x
= (INT16
)LOWORD(lParam
);
2670 mousePos
.y
= (INT16
)HIWORD(lParam
);
2672 GetClientRect(hwnd
, &clientRect
);
2675 * When the user clicks outside the combobox and the focus
2676 * is lost, the owning combobox will send a fake buttonup with
2677 * 0xFFFFFFF as the mouse location, we must also revert the
2678 * selection to the original selection.
2680 if ( (lParam
== 0xFFFFFFFF) ||
2681 (!PtInRect( &clientRect
, mousePos
)) )
2683 LISTBOX_MoveCaret( wnd
,
2689 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2690 case WM_LBUTTONDOWN
:
2691 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2692 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2693 /* avoid activation at all costs */
2695 case WM_MOUSEACTIVATE
:
2696 return MA_NOACTIVATE
;
2700 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2702 /* for some reason(?) Windows makes it possible to
2703 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2705 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2706 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2707 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2709 COMBO_FlipListbox( lphc
, FALSE
);
2713 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2715 case LB_SETCURSEL16
:
2717 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2718 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2721 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2726 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2729 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2731 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
2736 /***********************************************************************
2739 * NOTE: in Windows, winproc address of the ComboLBox is the same
2740 * as that of the Listbox.
2742 * This is just a wrapper for the real wndproc, it only does window locking
2745 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
2746 WPARAM wParam
, LPARAM lParam
)
2748 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2749 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
2751 WIN_ReleaseWndPtr(wnd
);