4 * Copyright 1996 Alexandre Julliard
9 #include "wine/winuser16.h"
17 #include "debugtools.h"
19 #include "winversion.h"
21 DEFAULT_DEBUG_CHANNEL(listbox
)
22 DECLARE_DEBUG_CHANNEL(combo
)
31 /* Items array granularity */
32 #define LB_ARRAY_GRANULARITY 16
34 /* Scrolling timeout in ms */
35 #define LB_SCROLL_TIMEOUT 50
37 /* Listbox system timer id */
43 LPSTR str
; /* Item text */
44 BOOL selected
; /* Is item selected? */
45 UINT height
; /* Item height (only for OWNERDRAWVARIABLE) */
46 DWORD data
; /* User data */
49 /* Listbox structure */
52 HANDLE heap
; /* Heap for this listbox */
53 HWND owner
; /* Owner window to send notifications to */
54 UINT style
; /* Window style */
55 INT width
; /* Window width */
56 INT height
; /* Window height */
57 LB_ITEMDATA
*items
; /* Array of items */
58 INT nb_items
; /* Number of items */
59 INT top_item
; /* Top visible item */
60 INT selected_item
; /* Selected item */
61 INT focus_item
; /* Item that has the focus */
62 INT anchor_item
; /* Anchor item for extended selection */
63 INT item_height
; /* Default item height */
64 INT page_size
; /* Items per listbox page */
65 INT column_width
; /* Column width for multi-column listboxes */
66 INT horz_extent
; /* Horizontal extent (0 if no hscroll) */
67 INT horz_pos
; /* Horizontal position */
68 INT nb_tabs
; /* Number of tabs in array */
69 INT
*tabs
; /* Array of tabs */
70 BOOL caret_on
; /* Is caret on? */
71 BOOL captured
; /* Is mouse captured? */
72 HFONT font
; /* Current font */
73 LCID locale
; /* Current locale for string comparisons */
74 LPHEADCOMBO lphc
; /* ComboLBox */
78 #define IS_OWNERDRAW(descr) \
79 ((descr)->style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE))
81 #define HAS_STRINGS(descr) \
82 (!IS_OWNERDRAW(descr) || ((descr)->style & LBS_HASSTRINGS))
85 #define IS_MULTISELECT(descr) \
86 ((descr)->style & LBS_MULTIPLESEL || ((descr)->style & LBS_EXTENDEDSEL))
88 #define SEND_NOTIFICATION(wnd,descr,code) \
89 (SendMessageA( (descr)->owner, WM_COMMAND, \
90 MAKEWPARAM((((descr)->lphc)?ID_CB_LISTBOX:(wnd)->wIDmenu), (code) ), (wnd)->hwndSelf ))
92 #define ISWIN31 (LOWORD(GetVersion()) == 0x0a03)
94 /* Current timer status */
104 static TIMER_DIRECTION LISTBOX_Timer
= LB_TIMER_NONE
;
107 /***********************************************************************
110 void LISTBOX_Dump( WND
*wnd
)
114 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
116 TRACE( "Listbox:\n" );
117 TRACE( "hwnd=%04x descr=%08x heap=%08x items=%d top=%d\n",
118 wnd
->hwndSelf
, (UINT
)descr
, descr
->heap
, descr
->nb_items
,
120 for (i
= 0, item
= descr
->items
; i
< descr
->nb_items
; i
++, item
++)
122 TRACE( "%4d: %-40s %d %08lx %3d\n",
123 i
, item
->str
, item
->selected
, item
->data
, item
->height
);
128 /***********************************************************************
129 * LISTBOX_GetCurrentPageSize
131 * Return the current page size
133 static INT
LISTBOX_GetCurrentPageSize( WND
*wnd
, LB_DESCR
*descr
)
136 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
)) return descr
->page_size
;
137 for (i
= descr
->top_item
, height
= 0; i
< descr
->nb_items
; i
++)
139 if ((height
+= descr
->items
[i
].height
) > descr
->height
) break;
141 if (i
== descr
->top_item
) return 1;
142 else return i
- descr
->top_item
;
146 /***********************************************************************
147 * LISTBOX_GetMaxTopIndex
149 * Return the maximum possible index for the top of the listbox.
151 static INT
LISTBOX_GetMaxTopIndex( WND
*wnd
, LB_DESCR
*descr
)
155 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
157 page
= descr
->height
;
158 for (max
= descr
->nb_items
- 1; max
>= 0; max
--)
159 if ((page
-= descr
->items
[max
].height
) < 0) break;
160 if (max
< descr
->nb_items
- 1) max
++;
162 else if (descr
->style
& LBS_MULTICOLUMN
)
164 if ((page
= descr
->width
/ descr
->column_width
) < 1) page
= 1;
165 max
= (descr
->nb_items
+ descr
->page_size
- 1) / descr
->page_size
;
166 max
= (max
- page
) * descr
->page_size
;
170 max
= descr
->nb_items
- descr
->page_size
;
172 if (max
< 0) max
= 0;
177 /***********************************************************************
178 * LISTBOX_UpdateScroll
180 * Update the scrollbars. Should be called whenever the content
181 * of the listbox changes.
183 static void LISTBOX_UpdateScroll( WND
*wnd
, LB_DESCR
*descr
)
187 if (!(descr
->style
& WS_VSCROLL
)) return;
188 /* It is important that we check descr->style, and not wnd->dwStyle,
189 for WS_VSCROLL, as the former is exactly the one passed in
190 argument to CreateWindow.
191 In Windows (and from now on in Wine :) a listbox created
192 with such a style (no WS_SCROLL) does not update
193 the scrollbar with listbox-related data, thus letting
194 the programmer use it for his/her own purposes. */
196 if (descr
->style
& LBS_NOREDRAW
) return;
197 info
.cbSize
= sizeof(info
);
199 if (descr
->style
& LBS_MULTICOLUMN
)
202 info
.nMax
= (descr
->nb_items
- 1) / descr
->page_size
;
203 info
.nPos
= descr
->top_item
/ descr
->page_size
;
204 info
.nPage
= descr
->width
/ descr
->column_width
;
205 if (info
.nPage
< 1) info
.nPage
= 1;
206 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
207 if (descr
->style
& LBS_DISABLENOSCROLL
)
208 info
.fMask
|= SIF_DISABLENOSCROLL
;
209 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
211 info
.fMask
= SIF_RANGE
;
212 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
217 info
.nMax
= descr
->nb_items
- 1;
218 info
.nPos
= descr
->top_item
;
219 info
.nPage
= LISTBOX_GetCurrentPageSize( wnd
, descr
);
220 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
221 if (descr
->style
& LBS_DISABLENOSCROLL
)
222 info
.fMask
|= SIF_DISABLENOSCROLL
;
223 SetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
, TRUE
);
225 if (descr
->horz_extent
)
228 info
.nMax
= descr
->horz_extent
- 1;
229 info
.nPos
= descr
->horz_pos
;
230 info
.nPage
= descr
->width
;
231 info
.fMask
= SIF_RANGE
| SIF_POS
| SIF_PAGE
;
232 if (descr
->style
& LBS_DISABLENOSCROLL
)
233 info
.fMask
|= SIF_DISABLENOSCROLL
;
234 SetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
, TRUE
);
240 /***********************************************************************
243 * Set the top item of the listbox, scrolling up or down if necessary.
245 static LRESULT
LISTBOX_SetTopItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
248 INT max
= LISTBOX_GetMaxTopIndex( wnd
, descr
);
249 if (index
> max
) index
= max
;
250 if (index
< 0) index
= 0;
251 if (descr
->style
& LBS_MULTICOLUMN
) index
-= index
% descr
->page_size
;
252 if (descr
->top_item
== index
) return LB_OKAY
;
253 if (descr
->style
& LBS_MULTICOLUMN
)
255 INT diff
= (descr
->top_item
- index
) / descr
->page_size
* descr
->column_width
;
256 if (scroll
&& (abs(diff
) < descr
->width
))
257 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
258 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
266 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
270 if (index
> descr
->top_item
)
272 for (i
= index
- 1; i
>= descr
->top_item
; i
--)
273 diff
-= descr
->items
[i
].height
;
277 for (i
= index
; i
< descr
->top_item
; i
++)
278 diff
+= descr
->items
[i
].height
;
282 diff
= (descr
->top_item
- index
) * descr
->item_height
;
284 if (abs(diff
) < descr
->height
)
285 ScrollWindowEx( wnd
->hwndSelf
, 0, diff
, NULL
, NULL
, 0, NULL
,
286 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
290 if (!scroll
) InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
291 descr
->top_item
= index
;
292 LISTBOX_UpdateScroll( wnd
, descr
);
297 /***********************************************************************
300 * Update the page size. Should be called when the size of
301 * the client area or the item height changes.
303 static void LISTBOX_UpdatePage( WND
*wnd
, LB_DESCR
*descr
)
307 if ((descr
->item_height
== 0) || (page_size
= descr
->height
/ descr
->item_height
) < 1)
309 if (page_size
== descr
->page_size
) return;
310 descr
->page_size
= page_size
;
311 if (descr
->style
& LBS_MULTICOLUMN
)
312 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
313 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
, FALSE
);
317 /***********************************************************************
320 * Update the size of the listbox. Should be called when the size of
321 * the client area changes.
323 static void LISTBOX_UpdateSize( WND
*wnd
, LB_DESCR
*descr
)
327 GetClientRect( wnd
->hwndSelf
, &rect
);
328 descr
->width
= rect
.right
- rect
.left
;
329 descr
->height
= rect
.bottom
- rect
.top
;
330 if (!(descr
->style
& LBS_NOINTEGRALHEIGHT
) && !IS_OWNERDRAW(descr
))
332 if ((descr
->height
> descr
->item_height
) &&
333 (descr
->height
% descr
->item_height
))
335 TRACE("[%04x]: changing height %d -> %d\n",
336 wnd
->hwndSelf
, descr
->height
,
337 descr
->height
- descr
->height
%descr
->item_height
);
338 SetWindowPos( wnd
->hwndSelf
, 0, 0, 0,
339 wnd
->rectWindow
.right
- wnd
->rectWindow
.left
,
340 wnd
->rectWindow
.bottom
- wnd
->rectWindow
.top
-
341 (descr
->height
% descr
->item_height
),
342 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
);
346 TRACE("[%04x]: new size = %d,%d\n",
347 wnd
->hwndSelf
, descr
->width
, descr
->height
);
348 LISTBOX_UpdatePage( wnd
, descr
);
349 LISTBOX_UpdateScroll( wnd
, descr
);
353 /***********************************************************************
354 * LISTBOX_GetItemRect
356 * Get the rectangle enclosing an item, in listbox client coordinates.
357 * Return 1 if the rectangle is (partially) visible, 0 if hidden, -1 on error.
359 static LRESULT
LISTBOX_GetItemRect( WND
*wnd
, LB_DESCR
*descr
, INT index
,
362 /* Index <= 0 is legal even on empty listboxes */
363 if (index
&& (index
>= descr
->nb_items
)) return -1;
364 SetRect( rect
, 0, 0, descr
->width
, descr
->height
);
365 if (descr
->style
& LBS_MULTICOLUMN
)
367 INT col
= (index
/ descr
->page_size
) -
368 (descr
->top_item
/ descr
->page_size
);
369 rect
->left
+= col
* descr
->column_width
;
370 rect
->right
= rect
->left
+ descr
->column_width
;
371 rect
->top
+= (index
% descr
->page_size
) * descr
->item_height
;
372 rect
->bottom
= rect
->top
+ descr
->item_height
;
374 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
377 rect
->right
+= descr
->horz_pos
;
378 if ((index
>= 0) && (index
< descr
->nb_items
))
380 if (index
< descr
->top_item
)
382 for (i
= descr
->top_item
-1; i
>= index
; i
--)
383 rect
->top
-= descr
->items
[i
].height
;
387 for (i
= descr
->top_item
; i
< index
; i
++)
388 rect
->top
+= descr
->items
[i
].height
;
390 rect
->bottom
= rect
->top
+ descr
->items
[index
].height
;
396 rect
->top
+= (index
- descr
->top_item
) * descr
->item_height
;
397 rect
->bottom
= rect
->top
+ descr
->item_height
;
398 rect
->right
+= descr
->horz_pos
;
401 return ((rect
->left
< descr
->width
) && (rect
->right
> 0) &&
402 (rect
->top
< descr
->height
) && (rect
->bottom
> 0));
406 /***********************************************************************
407 * LISTBOX_GetItemFromPoint
409 * Return the item nearest from point (x,y) (in client coordinates).
411 static INT
LISTBOX_GetItemFromPoint( WND
*wnd
, LB_DESCR
*descr
,
414 INT index
= descr
->top_item
;
416 if (!descr
->nb_items
) return -1; /* No items */
417 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
422 while (index
< descr
->nb_items
)
424 if ((pos
+= descr
->items
[index
].height
) > y
) break;
433 if ((pos
-= descr
->items
[index
].height
) <= y
) break;
437 else if (descr
->style
& LBS_MULTICOLUMN
)
439 if (y
>= descr
->item_height
* descr
->page_size
) return -1;
440 if (y
>= 0) index
+= y
/ descr
->item_height
;
441 if (x
>= 0) index
+= (x
/ descr
->column_width
) * descr
->page_size
;
442 else index
-= (((x
+ 1) / descr
->column_width
) - 1) * descr
->page_size
;
446 index
+= (y
/ descr
->item_height
);
448 if (index
< 0) return 0;
449 if (index
>= descr
->nb_items
) return -1;
454 /***********************************************************************
459 static void LISTBOX_PaintItem( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
,
460 const RECT
*rect
, INT index
, UINT action
)
462 LB_ITEMDATA
*item
= NULL
;
463 if (index
< descr
->nb_items
) item
= &descr
->items
[index
];
465 if (IS_OWNERDRAW(descr
))
468 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
472 if (action
== ODA_FOCUS
)
473 DrawFocusRect( hdc
, rect
);
475 FIXME("called with an out of bounds index %d(%d) in owner draw, Not good.\n",index
,descr
->nb_items
);
478 dis
.CtlType
= ODT_LISTBOX
;
480 dis
.hwndItem
= wnd
->hwndSelf
;
481 dis
.itemAction
= action
;
485 if (item
&& item
->selected
) dis
.itemState
|= ODS_SELECTED
;
486 if ((descr
->focus_item
== index
) &&
488 (GetFocus() == wnd
->hwndSelf
)) dis
.itemState
|= ODS_FOCUS
;
489 if (wnd
->dwStyle
& WS_DISABLED
) dis
.itemState
|= ODS_DISABLED
;
490 dis
.itemData
= item
? item
->data
: 0;
492 TRACE("[%04x]: drawitem %d (%s) action=%02x "
493 "state=%02x rect=%d,%d-%d,%d\n",
494 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
495 dis
.itemState
, rect
->left
, rect
->top
,
496 rect
->right
, rect
->bottom
);
497 SendMessageA(descr
->owner
, WM_DRAWITEM
, id
, (LPARAM
)&dis
);
501 COLORREF oldText
= 0, oldBk
= 0;
503 if (action
== ODA_FOCUS
)
505 DrawFocusRect( hdc
, rect
);
508 if (item
&& item
->selected
)
510 oldBk
= SetBkColor( hdc
, GetSysColor( COLOR_HIGHLIGHT
) );
511 oldText
= SetTextColor( hdc
, GetSysColor(COLOR_HIGHLIGHTTEXT
));
514 TRACE("[%04x]: painting %d (%s) action=%02x "
515 "rect=%d,%d-%d,%d\n",
516 wnd
->hwndSelf
, index
, item
? item
->str
: "", action
,
517 rect
->left
, rect
->top
, rect
->right
, rect
->bottom
);
519 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
520 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
521 else if (!(descr
->style
& LBS_USETABSTOPS
))
522 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
523 ETO_OPAQUE
| ETO_CLIPPED
, rect
, item
->str
,
524 strlen(item
->str
), NULL
);
527 /* Output empty string to paint background in the full width. */
528 ExtTextOutA( hdc
, rect
->left
+ 1, rect
->top
+ 1,
529 ETO_OPAQUE
| ETO_CLIPPED
, rect
, NULL
, 0, NULL
);
530 TabbedTextOutA( hdc
, rect
->left
+ 1 , rect
->top
+ 1,
531 item
->str
, strlen(item
->str
),
532 descr
->nb_tabs
, descr
->tabs
, 0);
534 if (item
&& item
->selected
)
536 SetBkColor( hdc
, oldBk
);
537 SetTextColor( hdc
, oldText
);
539 if ((descr
->focus_item
== index
) &&
541 (GetFocus() == wnd
->hwndSelf
)) DrawFocusRect( hdc
, rect
);
546 /***********************************************************************
549 * Change the redraw flag.
551 static void LISTBOX_SetRedraw( WND
*wnd
, LB_DESCR
*descr
, BOOL on
)
555 if (!(descr
->style
& LBS_NOREDRAW
)) return;
556 descr
->style
&= ~LBS_NOREDRAW
;
557 LISTBOX_UpdateScroll( wnd
, descr
);
559 else descr
->style
|= LBS_NOREDRAW
;
563 /***********************************************************************
564 * LISTBOX_RepaintItem
566 * Repaint a single item synchronously.
568 static void LISTBOX_RepaintItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
574 HBRUSH hbrush
, oldBrush
= 0;
576 /* Do not repaint the item if the item is not visible */
577 if ((descr
->style
& LBS_NOREDRAW
) || !IsWindowVisible(wnd
->hwndSelf
)) return;
579 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) != 1) return;
580 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
))) return;
581 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
582 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
583 hdc
, (LPARAM
)wnd
->hwndSelf
);
584 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
585 if (wnd
->dwStyle
& WS_DISABLED
)
586 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
587 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
588 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, index
, action
);
589 if (oldFont
) SelectObject( hdc
, oldFont
);
590 if (oldBrush
) SelectObject( hdc
, oldBrush
);
591 ReleaseDC( wnd
->hwndSelf
, hdc
);
595 /***********************************************************************
596 * LISTBOX_InitStorage
598 static LRESULT
LISTBOX_InitStorage( WND
*wnd
, LB_DESCR
*descr
, INT nb_items
,
603 nb_items
+= LB_ARRAY_GRANULARITY
- 1;
604 nb_items
-= (nb_items
% LB_ARRAY_GRANULARITY
);
606 nb_items
+= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
607 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
608 nb_items
* sizeof(LB_ITEMDATA
) )))
610 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
618 /***********************************************************************
619 * LISTBOX_SetTabStops
621 static BOOL
LISTBOX_SetTabStops( WND
*wnd
, LB_DESCR
*descr
, INT count
,
622 LPINT tabs
, BOOL short_ints
)
624 if (!(descr
->style
& LBS_USETABSTOPS
)) return TRUE
;
625 if (descr
->tabs
) HeapFree( descr
->heap
, 0, descr
->tabs
);
626 if (!(descr
->nb_tabs
= count
))
631 /* FIXME: count = 1 */
632 if (!(descr
->tabs
= (INT
*)HeapAlloc( descr
->heap
, 0,
633 descr
->nb_tabs
* sizeof(INT
) )))
638 LPINT16 p
= (LPINT16
)tabs
;
640 TRACE("[%04x]: settabstops ", wnd
->hwndSelf
);
641 for (i
= 0; i
< descr
->nb_tabs
; i
++) {
642 descr
->tabs
[i
] = *p
++<<1; /* FIXME */
643 if (TRACE_ON(listbox
)) DPRINTF("%hd ", descr
->tabs
[i
]);
645 if (TRACE_ON(listbox
)) DPRINTF("\n");
647 else memcpy( descr
->tabs
, tabs
, descr
->nb_tabs
* sizeof(INT
) );
648 /* FIXME: repaint the window? */
653 /***********************************************************************
656 static LRESULT
LISTBOX_GetText( WND
*wnd
, LB_DESCR
*descr
, INT index
,
659 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
660 if (HAS_STRINGS(descr
))
663 return strlen(descr
->items
[index
].str
);
664 lstrcpyA( buffer
, descr
->items
[index
].str
);
665 return strlen(buffer
);
668 *((LPDWORD
)buffer
)=*(LPDWORD
)(&descr
->items
[index
].data
);
669 return sizeof(DWORD
);
674 /***********************************************************************
675 * LISTBOX_FindStringPos
677 * Find the nearest string located before a given string in sort order.
678 * If 'exact' is TRUE, return an error if we don't get an exact match.
680 static INT
LISTBOX_FindStringPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
,
683 INT index
, min
, max
, res
= -1;
685 if (!(descr
->style
& LBS_SORT
)) return -1; /* Add it at the end */
687 max
= descr
->nb_items
;
690 index
= (min
+ max
) / 2;
691 if (HAS_STRINGS(descr
))
692 res
= lstrcmpiA( descr
->items
[index
].str
, str
);
695 COMPAREITEMSTRUCT cis
;
696 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
698 cis
.CtlType
= ODT_LISTBOX
;
700 cis
.hwndItem
= wnd
->hwndSelf
;
702 cis
.itemData1
= descr
->items
[index
].data
;
704 cis
.itemData2
= (DWORD
)str
;
705 cis
.dwLocaleId
= descr
->locale
;
706 res
= SendMessageA( descr
->owner
, WM_COMPAREITEM
,
709 if (!res
) return index
;
710 if (res
> 0) max
= index
;
711 else min
= index
+ 1;
713 return exact
? -1 : max
;
717 /***********************************************************************
718 * LISTBOX_FindFileStrPos
720 * Find the nearest string located before a given string in directory
721 * sort order (i.e. first files, then directories, then drives).
723 static INT
LISTBOX_FindFileStrPos( WND
*wnd
, LB_DESCR
*descr
, LPCSTR str
)
725 INT min
, max
, res
= -1;
727 if (!HAS_STRINGS(descr
))
728 return LISTBOX_FindStringPos( wnd
, descr
, str
, FALSE
);
730 max
= descr
->nb_items
;
733 INT index
= (min
+ max
) / 2;
734 const char *p
= descr
->items
[index
].str
;
735 if (*p
== '[') /* drive or directory */
737 if (*str
!= '[') res
= -1;
738 else if (p
[1] == '-') /* drive */
740 if (str
[1] == '-') res
= str
[2] - p
[2];
745 if (str
[1] == '-') res
= 1;
746 else res
= lstrcmpiA( str
, p
);
751 if (*str
== '[') res
= 1;
752 else res
= lstrcmpiA( str
, p
);
754 if (!res
) return index
;
755 if (res
< 0) max
= index
;
756 else min
= index
+ 1;
762 /***********************************************************************
765 * Find the item beginning with a given string.
767 static INT
LISTBOX_FindString( WND
*wnd
, LB_DESCR
*descr
, INT start
,
768 LPCSTR str
, BOOL exact
)
773 if (start
>= descr
->nb_items
) start
= -1;
774 item
= descr
->items
+ start
+ 1;
775 if (HAS_STRINGS(descr
))
777 if (!str
|| ! str
[0] ) return LB_ERR
;
780 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
781 if (!lstrcmpiA( str
, item
->str
)) return i
;
782 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
783 if (!lstrcmpiA( str
, item
->str
)) return i
;
787 /* Special case for drives and directories: ignore prefix */
788 #define CHECK_DRIVE(item) \
789 if ((item)->str[0] == '[') \
791 if (!lstrncmpiA( str, (item)->str+1, len )) return i; \
792 if (((item)->str[1] == '-') && !lstrncmpiA(str,(item)->str+2,len)) \
796 INT len
= strlen(str
);
797 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
799 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
802 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
804 if (!lstrncmpiA( str
, item
->str
, len
)) return i
;
812 if (exact
&& (descr
->style
& LBS_SORT
))
813 /* If sorted, use a WM_COMPAREITEM binary search */
814 return LISTBOX_FindStringPos( wnd
, descr
, str
, TRUE
);
816 /* Otherwise use a linear search */
817 for (i
= start
+ 1; i
< descr
->nb_items
; i
++, item
++)
818 if (item
->data
== (DWORD
)str
) return i
;
819 for (i
= 0, item
= descr
->items
; i
<= start
; i
++, item
++)
820 if (item
->data
== (DWORD
)str
) return i
;
826 /***********************************************************************
827 * LISTBOX_GetSelCount
829 static LRESULT
LISTBOX_GetSelCount( WND
*wnd
, LB_DESCR
*descr
)
832 LB_ITEMDATA
*item
= descr
->items
;
834 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
835 for (i
= count
= 0; i
< descr
->nb_items
; i
++, item
++)
836 if (item
->selected
) count
++;
841 /***********************************************************************
842 * LISTBOX_GetSelItems16
844 static LRESULT
LISTBOX_GetSelItems16( WND
*wnd
, LB_DESCR
*descr
, INT16 max
,
848 LB_ITEMDATA
*item
= descr
->items
;
850 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
851 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
852 if (item
->selected
) array
[count
++] = (INT16
)i
;
857 /***********************************************************************
858 * LISTBOX_GetSelItems32
860 static LRESULT
LISTBOX_GetSelItems( WND
*wnd
, LB_DESCR
*descr
, INT max
,
864 LB_ITEMDATA
*item
= descr
->items
;
866 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
867 for (i
= count
= 0; (i
< descr
->nb_items
) && (count
< max
); i
++, item
++)
868 if (item
->selected
) array
[count
++] = i
;
873 /***********************************************************************
876 static LRESULT
LISTBOX_Paint( WND
*wnd
, LB_DESCR
*descr
, HDC hdc
)
878 INT i
, col_pos
= descr
->page_size
- 1;
881 HBRUSH hbrush
, oldBrush
= 0;
883 SetRect( &rect
, 0, 0, descr
->width
, descr
->height
);
884 if (descr
->style
& LBS_NOREDRAW
) return 0;
885 if (descr
->style
& LBS_MULTICOLUMN
)
886 rect
.right
= rect
.left
+ descr
->column_width
;
887 else if (descr
->horz_pos
)
889 SetWindowOrgEx( hdc
, descr
->horz_pos
, 0, NULL
);
890 rect
.right
+= descr
->horz_pos
;
893 if (descr
->font
) oldFont
= SelectObject( hdc
, descr
->font
);
894 hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
895 hdc
, (LPARAM
)wnd
->hwndSelf
);
896 if (hbrush
) oldBrush
= SelectObject( hdc
, hbrush
);
897 if (wnd
->dwStyle
& WS_DISABLED
)
898 SetTextColor( hdc
, GetSysColor( COLOR_GRAYTEXT
) );
900 if (!descr
->nb_items
&& (descr
->focus_item
!= -1) && descr
->caret_on
&&
901 (GetFocus() == wnd
->hwndSelf
))
903 /* Special case for empty listbox: paint focus rect */
904 rect
.bottom
= rect
.top
+ descr
->item_height
;
905 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, descr
->focus_item
,
907 rect
.top
= rect
.bottom
;
910 for (i
= descr
->top_item
; i
< descr
->nb_items
; i
++)
912 if (!(descr
->style
& LBS_OWNERDRAWVARIABLE
))
913 rect
.bottom
= rect
.top
+ descr
->item_height
;
915 rect
.bottom
= rect
.top
+ descr
->items
[i
].height
;
917 LISTBOX_PaintItem( wnd
, descr
, hdc
, &rect
, i
, ODA_DRAWENTIRE
);
918 rect
.top
= rect
.bottom
;
920 if ((descr
->style
& LBS_MULTICOLUMN
) && !col_pos
)
922 if (!IS_OWNERDRAW(descr
))
924 /* Clear the bottom of the column */
925 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
926 if (rect
.top
< descr
->height
)
928 rect
.bottom
= descr
->height
;
929 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
930 &rect
, NULL
, 0, NULL
);
934 /* Go to the next column */
935 rect
.left
+= descr
->column_width
;
936 rect
.right
+= descr
->column_width
;
938 col_pos
= descr
->page_size
- 1;
943 if (rect
.top
>= descr
->height
) break;
947 if (!IS_OWNERDRAW(descr
))
949 /* Clear the remainder of the client area */
950 SetBkColor( hdc
, GetSysColor( COLOR_WINDOW
) );
951 if (rect
.top
< descr
->height
)
953 rect
.bottom
= descr
->height
;
954 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
955 &rect
, NULL
, 0, NULL
);
957 if (rect
.right
< descr
->width
)
959 rect
.left
= rect
.right
;
960 rect
.right
= descr
->width
;
962 rect
.bottom
= descr
->height
;
963 ExtTextOutA( hdc
, 0, 0, ETO_OPAQUE
| ETO_CLIPPED
,
964 &rect
, NULL
, 0, NULL
);
967 if (oldFont
) SelectObject( hdc
, oldFont
);
968 if (oldBrush
) SelectObject( hdc
, oldBrush
);
973 /***********************************************************************
974 * LISTBOX_InvalidateItems
976 * Invalidate all items from a given item. If the specified item is not
977 * visible, nothing happens.
979 static void LISTBOX_InvalidateItems( WND
*wnd
, LB_DESCR
*descr
, INT index
)
983 if (LISTBOX_GetItemRect( wnd
, descr
, index
, &rect
) == 1)
985 rect
.bottom
= descr
->height
;
986 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
987 if (descr
->style
& LBS_MULTICOLUMN
)
989 /* Repaint the other columns */
990 rect
.left
= rect
.right
;
991 rect
.right
= descr
->width
;
993 InvalidateRect( wnd
->hwndSelf
, &rect
, TRUE
);
999 /***********************************************************************
1000 * LISTBOX_GetItemHeight
1002 static LRESULT
LISTBOX_GetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1004 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1006 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1007 return descr
->items
[index
].height
;
1009 else return descr
->item_height
;
1013 /***********************************************************************
1014 * LISTBOX_SetItemHeight
1016 static LRESULT
LISTBOX_SetItemHeight( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1019 if (!height
) height
= 1;
1021 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1023 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1024 TRACE("[%04x]: item %d height = %d\n",
1025 wnd
->hwndSelf
, index
, height
);
1026 descr
->items
[index
].height
= height
;
1027 LISTBOX_UpdateScroll( wnd
, descr
);
1028 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1030 else if (height
!= descr
->item_height
)
1032 TRACE("[%04x]: new height = %d\n",
1033 wnd
->hwndSelf
, height
);
1034 descr
->item_height
= height
;
1035 LISTBOX_UpdatePage( wnd
, descr
);
1036 LISTBOX_UpdateScroll( wnd
, descr
);
1037 InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
1043 /***********************************************************************
1044 * LISTBOX_SetHorizontalPos
1046 static void LISTBOX_SetHorizontalPos( WND
*wnd
, LB_DESCR
*descr
, INT pos
)
1050 if (pos
> descr
->horz_extent
- descr
->width
)
1051 pos
= descr
->horz_extent
- descr
->width
;
1052 if (pos
< 0) pos
= 0;
1053 if (!(diff
= descr
->horz_pos
- pos
)) return;
1054 TRACE("[%04x]: new horz pos = %d\n",
1055 wnd
->hwndSelf
, pos
);
1056 descr
->horz_pos
= pos
;
1057 LISTBOX_UpdateScroll( wnd
, descr
);
1058 if (abs(diff
) < descr
->width
)
1059 ScrollWindowEx( wnd
->hwndSelf
, diff
, 0, NULL
, NULL
, 0, NULL
,
1060 SW_INVALIDATE
| SW_ERASE
| SW_SCROLLCHILDREN
);
1062 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1066 /***********************************************************************
1067 * LISTBOX_SetHorizontalExtent
1069 static LRESULT
LISTBOX_SetHorizontalExtent( WND
*wnd
, LB_DESCR
*descr
,
1072 if (!descr
->horz_extent
|| (descr
->style
& LBS_MULTICOLUMN
))
1074 if (extent
<= 0) extent
= 1;
1075 if (extent
== descr
->horz_extent
) return LB_OKAY
;
1076 TRACE("[%04x]: new horz extent = %d\n",
1077 wnd
->hwndSelf
, extent
);
1078 descr
->horz_extent
= extent
;
1079 if (descr
->horz_pos
> extent
- descr
->width
)
1080 LISTBOX_SetHorizontalPos( wnd
, descr
, extent
- descr
->width
);
1082 LISTBOX_UpdateScroll( wnd
, descr
);
1087 /***********************************************************************
1088 * LISTBOX_SetColumnWidth
1090 static LRESULT
LISTBOX_SetColumnWidth( WND
*wnd
, LB_DESCR
*descr
, UINT width
)
1092 width
+= 2; /* For left and right margin */
1093 if (width
== descr
->column_width
) return LB_OKAY
;
1094 TRACE("[%04x]: new column width = %d\n",
1095 wnd
->hwndSelf
, width
);
1096 descr
->column_width
= width
;
1097 LISTBOX_UpdatePage( wnd
, descr
);
1102 /***********************************************************************
1105 * Returns the item height.
1107 static INT
LISTBOX_SetFont( WND
*wnd
, LB_DESCR
*descr
, HFONT font
)
1115 if (!(hdc
= GetDCEx( wnd
->hwndSelf
, 0, DCX_CACHE
)))
1117 ERR("unable to get DC.\n" );
1120 if (font
) oldFont
= SelectObject( hdc
, font
);
1121 GetTextMetricsA( hdc
, &tm
);
1122 if (oldFont
) SelectObject( hdc
, oldFont
);
1123 ReleaseDC( wnd
->hwndSelf
, hdc
);
1124 if (!IS_OWNERDRAW(descr
))
1125 LISTBOX_SetItemHeight( wnd
, descr
, 0, tm
.tmHeight
);
1126 return tm
.tmHeight
;
1130 /***********************************************************************
1131 * LISTBOX_MakeItemVisible
1133 * Make sure that a given item is partially or fully visible.
1135 static void LISTBOX_MakeItemVisible( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1140 if (index
<= descr
->top_item
) top
= index
;
1141 else if (descr
->style
& LBS_MULTICOLUMN
)
1143 INT cols
= descr
->width
;
1144 if (!fully
) cols
+= descr
->column_width
- 1;
1145 if (cols
>= descr
->column_width
) cols
/= descr
->column_width
;
1147 if (index
< descr
->top_item
+ (descr
->page_size
* cols
)) return;
1148 top
= index
- descr
->page_size
* (cols
- 1);
1150 else if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1152 INT height
= fully
? descr
->items
[index
].height
: 1;
1153 for (top
= index
; top
> descr
->top_item
; top
--)
1154 if ((height
+= descr
->items
[top
-1].height
) > descr
->height
) break;
1158 if (index
< descr
->top_item
+ descr
->page_size
) return;
1159 if (!fully
&& (index
== descr
->top_item
+ descr
->page_size
) &&
1160 (descr
->height
> (descr
->page_size
* descr
->item_height
))) return;
1161 top
= index
- descr
->page_size
+ 1;
1163 LISTBOX_SetTopItem( wnd
, descr
, top
, TRUE
);
1167 /***********************************************************************
1168 * LISTBOX_SelectItemRange
1170 * Select a range of items. Should only be used on a MULTIPLESEL listbox.
1172 static LRESULT
LISTBOX_SelectItemRange( WND
*wnd
, LB_DESCR
*descr
, INT first
,
1177 /* A few sanity checks */
1179 if ((last
== -1) && (descr
->nb_items
== 0)) return LB_OKAY
;
1180 if (!(descr
->style
& LBS_MULTIPLESEL
)) return LB_ERR
;
1181 if (last
== -1) last
= descr
->nb_items
- 1;
1182 if ((first
< 0) || (first
>= descr
->nb_items
)) return LB_ERR
;
1183 if ((last
< 0) || (last
>= descr
->nb_items
)) return LB_ERR
;
1184 /* selected_item reflects last selected/unselected item on multiple sel */
1185 descr
->selected_item
= last
;
1187 if (on
) /* Turn selection on */
1189 for (i
= first
; i
<= last
; i
++)
1191 if (descr
->items
[i
].selected
) continue;
1192 descr
->items
[i
].selected
= TRUE
;
1193 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1196 else /* Turn selection off */
1198 for (i
= first
; i
<= last
; i
++)
1200 if (!descr
->items
[i
].selected
) continue;
1201 descr
->items
[i
].selected
= FALSE
;
1202 LISTBOX_RepaintItem( wnd
, descr
, i
, ODA_SELECT
);
1209 /***********************************************************************
1210 * LISTBOX_SetCaretIndex
1213 * index must be between 0 and descr->nb_items-1, or LB_ERR is returned.
1216 static LRESULT
LISTBOX_SetCaretIndex( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1217 BOOL fully_visible
)
1219 INT oldfocus
= descr
->focus_item
;
1221 if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1222 if (index
== oldfocus
) return LB_OKAY
;
1223 descr
->focus_item
= index
;
1224 if ((oldfocus
!= -1) && descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1225 LISTBOX_RepaintItem( wnd
, descr
, oldfocus
, ODA_FOCUS
);
1227 LISTBOX_MakeItemVisible( wnd
, descr
, index
, fully_visible
);
1228 if (descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
))
1229 LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_FOCUS
);
1235 /***********************************************************************
1236 * LISTBOX_SetSelection
1238 static LRESULT
LISTBOX_SetSelection( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1239 BOOL on
, BOOL send_notify
)
1241 if ((index
< -1) || (index
>= descr
->nb_items
)) return LB_ERR
;
1242 if (descr
->style
& LBS_MULTIPLESEL
)
1244 if (index
== -1) /* Select all items */
1245 return LISTBOX_SelectItemRange( wnd
, descr
, 0, -1, on
);
1246 else /* Only one item */
1247 return LISTBOX_SelectItemRange( wnd
, descr
, index
, index
, on
);
1251 INT oldsel
= descr
->selected_item
;
1252 if (index
== oldsel
) return LB_OKAY
;
1253 if (oldsel
!= -1) descr
->items
[oldsel
].selected
= FALSE
;
1254 if (index
!= -1) descr
->items
[index
].selected
= TRUE
;
1255 descr
->selected_item
= index
;
1256 if (oldsel
!= -1) LISTBOX_RepaintItem( wnd
, descr
, oldsel
, ODA_SELECT
);
1257 if (index
!= -1) LISTBOX_RepaintItem( wnd
, descr
, index
, ODA_SELECT
);
1258 if (send_notify
&& descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
,
1259 (index
!= -1) ? LBN_SELCHANGE
: LBN_SELCANCEL
);
1261 if( descr
->lphc
) /* set selection change flag for parent combo */
1262 descr
->lphc
->wState
|= CBF_SELCHANGE
;
1268 /***********************************************************************
1271 * Change the caret position and extend the selection to the new caret.
1273 static void LISTBOX_MoveCaret( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1274 BOOL fully_visible
)
1276 LISTBOX_SetCaretIndex( wnd
, descr
, index
, fully_visible
);
1277 if (descr
->style
& LBS_EXTENDEDSEL
)
1279 if (descr
->anchor_item
!= -1)
1281 INT first
= MIN( descr
->focus_item
, descr
->anchor_item
);
1282 INT last
= MAX( descr
->focus_item
, descr
->anchor_item
);
1284 LISTBOX_SelectItemRange( wnd
, descr
, 0, first
- 1, FALSE
);
1285 LISTBOX_SelectItemRange( wnd
, descr
, last
+ 1, -1, FALSE
);
1286 LISTBOX_SelectItemRange( wnd
, descr
, first
, last
, TRUE
);
1289 else if (!(descr
->style
& LBS_MULTIPLESEL
))
1291 /* Set selection to new caret item */
1292 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
1297 /***********************************************************************
1298 * LISTBOX_InsertItem
1300 static LRESULT
LISTBOX_InsertItem( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1301 LPSTR str
, DWORD data
)
1305 INT oldfocus
= descr
->focus_item
;
1307 if (index
== -1) index
= descr
->nb_items
;
1308 else if ((index
< 0) || (index
> descr
->nb_items
)) return LB_ERR
;
1309 if (!descr
->items
) max_items
= 0;
1310 else max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(*item
);
1311 if (descr
->nb_items
== max_items
)
1313 /* We need to grow the array */
1314 max_items
+= LB_ARRAY_GRANULARITY
;
1315 if (!(item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1316 max_items
* sizeof(LB_ITEMDATA
) )))
1318 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1321 descr
->items
= item
;
1324 /* Insert the item structure */
1326 item
= &descr
->items
[index
];
1327 if (index
< descr
->nb_items
)
1328 RtlMoveMemory( item
+ 1, item
,
1329 (descr
->nb_items
- index
) * sizeof(LB_ITEMDATA
) );
1333 item
->selected
= FALSE
;
1336 /* Get item height */
1338 if (descr
->style
& LBS_OWNERDRAWVARIABLE
)
1340 MEASUREITEMSTRUCT mis
;
1341 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1343 mis
.CtlType
= ODT_LISTBOX
;
1346 mis
.itemData
= descr
->items
[index
].data
;
1347 mis
.itemHeight
= descr
->item_height
;
1348 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
1349 item
->height
= mis
.itemHeight
? mis
.itemHeight
: 1;
1350 TRACE("[%04x]: measure item %d (%s) = %d\n",
1351 wnd
->hwndSelf
, index
, str
? str
: "", item
->height
);
1354 /* Repaint the items */
1356 LISTBOX_UpdateScroll( wnd
, descr
);
1357 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1359 /* Move selection and focused item */
1360 /* If listbox was empty, set focus to the first item */
1361 if (descr
->nb_items
== 1)
1362 LISTBOX_SetCaretIndex( wnd
, descr
, 0, FALSE
);
1363 /* single select don't change selection index in win31 */
1364 else if ((ISWIN31
) && !(IS_MULTISELECT(descr
)))
1366 descr
->selected_item
++;
1367 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
-1, TRUE
, FALSE
);
1371 if (index
<= descr
->selected_item
)
1373 descr
->selected_item
++;
1374 descr
->focus_item
= oldfocus
; /* focus not changed */
1381 /***********************************************************************
1382 * LISTBOX_InsertString
1384 static LRESULT
LISTBOX_InsertString( WND
*wnd
, LB_DESCR
*descr
, INT index
,
1387 LPSTR new_str
= NULL
;
1391 if (HAS_STRINGS(descr
))
1394 if (!(new_str
= HEAP_strdupA( descr
->heap
, 0, str
)))
1396 SEND_NOTIFICATION( wnd
, descr
, LBN_ERRSPACE
);
1400 else data
= (DWORD
)str
;
1402 if (index
== -1) index
= descr
->nb_items
;
1403 if ((ret
= LISTBOX_InsertItem( wnd
, descr
, index
, new_str
, data
)) != 0)
1405 if (new_str
) HeapFree( descr
->heap
, 0, new_str
);
1409 TRACE("[%04x]: added item %d '%s'\n",
1410 wnd
->hwndSelf
, index
, HAS_STRINGS(descr
) ? new_str
: "" );
1415 /***********************************************************************
1416 * LISTBOX_DeleteItem
1418 * Delete the content of an item. 'index' must be a valid index.
1420 static void LISTBOX_DeleteItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1422 /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
1423 * while Win95 sends it for all items with user data.
1424 * It's probably better to send it too often than not
1425 * often enough, so this is what we do here.
1427 if (IS_OWNERDRAW(descr
) || descr
->items
[index
].data
)
1429 DELETEITEMSTRUCT dis
;
1430 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
1432 dis
.CtlType
= ODT_LISTBOX
;
1435 dis
.hwndItem
= wnd
->hwndSelf
;
1436 dis
.itemData
= descr
->items
[index
].data
;
1437 SendMessageA( descr
->owner
, WM_DELETEITEM
, id
, (LPARAM
)&dis
);
1439 if (HAS_STRINGS(descr
) && descr
->items
[index
].str
)
1440 HeapFree( descr
->heap
, 0, descr
->items
[index
].str
);
1444 /***********************************************************************
1445 * LISTBOX_RemoveItem
1447 * Remove an item from the listbox and delete its content.
1449 static LRESULT
LISTBOX_RemoveItem( WND
*wnd
, LB_DESCR
*descr
, INT index
)
1454 if (index
== -1) index
= descr
->nb_items
- 1;
1455 else if ((index
< 0) || (index
>= descr
->nb_items
)) return LB_ERR
;
1456 LISTBOX_DeleteItem( wnd
, descr
, index
);
1458 /* Remove the item */
1460 item
= &descr
->items
[index
];
1461 if (index
< descr
->nb_items
-1)
1462 RtlMoveMemory( item
, item
+ 1,
1463 (descr
->nb_items
- index
- 1) * sizeof(LB_ITEMDATA
) );
1465 if (descr
->anchor_item
== descr
->nb_items
) descr
->anchor_item
--;
1467 /* Shrink the item array if possible */
1469 max_items
= HeapSize( descr
->heap
, 0, descr
->items
) / sizeof(LB_ITEMDATA
);
1470 if (descr
->nb_items
< max_items
- 2*LB_ARRAY_GRANULARITY
)
1472 max_items
-= LB_ARRAY_GRANULARITY
;
1473 item
= HeapReAlloc( descr
->heap
, 0, descr
->items
,
1474 max_items
* sizeof(LB_ITEMDATA
) );
1475 if (item
) descr
->items
= item
;
1477 /* Repaint the items */
1479 LISTBOX_UpdateScroll( wnd
, descr
);
1480 /* if we removed the scrollbar, reset the top of the list
1481 (correct for owner-drawn ???) */
1482 if (descr
->nb_items
== descr
->page_size
)
1483 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1485 /* Move selection and focused item */
1486 if (!IS_MULTISELECT(descr
))
1488 if (index
== descr
->selected_item
)
1489 descr
->selected_item
= -1;
1490 else if (index
< descr
->selected_item
)
1492 descr
->selected_item
--;
1493 if (ISWIN31
) /* win 31 do not change the selected item number */
1494 LISTBOX_SetSelection( wnd
, descr
, descr
->selected_item
+ 1, TRUE
, FALSE
);
1497 LISTBOX_InvalidateItems( wnd
, descr
, index
);
1498 if (descr
->focus_item
>= descr
->nb_items
)
1500 descr
->focus_item
= descr
->nb_items
- 1;
1501 if (descr
->focus_item
< 0) descr
->focus_item
= 0;
1507 /***********************************************************************
1508 * LISTBOX_ResetContent
1510 static void LISTBOX_ResetContent( WND
*wnd
, LB_DESCR
*descr
)
1514 for (i
= 0; i
< descr
->nb_items
; i
++) LISTBOX_DeleteItem( wnd
, descr
, i
);
1515 if (descr
->items
) HeapFree( descr
->heap
, 0, descr
->items
);
1516 descr
->nb_items
= 0;
1517 descr
->top_item
= 0;
1518 descr
->selected_item
= -1;
1519 descr
->focus_item
= 0;
1520 descr
->anchor_item
= -1;
1521 descr
->items
= NULL
;
1522 LISTBOX_UpdateScroll( wnd
, descr
);
1523 InvalidateRect( wnd
->hwndSelf
, NULL
, TRUE
);
1527 /***********************************************************************
1530 static LRESULT
LISTBOX_SetCount( WND
*wnd
, LB_DESCR
*descr
, INT count
)
1534 if (HAS_STRINGS(descr
)) return LB_ERR
;
1535 /* FIXME: this is far from optimal... */
1536 if (count
> descr
->nb_items
)
1538 while (count
> descr
->nb_items
)
1539 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, 0 )) < 0)
1542 else if (count
< descr
->nb_items
)
1544 while (count
< descr
->nb_items
)
1545 if ((ret
= LISTBOX_RemoveItem( wnd
, descr
, -1 )) < 0)
1552 /***********************************************************************
1555 static LRESULT
LISTBOX_Directory( WND
*wnd
, LB_DESCR
*descr
, UINT attrib
,
1556 LPCSTR filespec
, BOOL long_names
)
1559 LRESULT ret
= LB_OKAY
;
1560 WIN32_FIND_DATAA entry
;
1563 if ((handle
= FindFirstFileA(filespec
,&entry
)) == INVALID_HANDLE_VALUE
)
1565 if (GetLastError() != ERROR_NO_MORE_FILES
) return LB_ERR
;
1572 if (entry
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1574 if (!(attrib
& DDL_DIRECTORY
) ||
1575 !strcmp( entry
.cAlternateFileName
, "." )) continue;
1576 if (long_names
) sprintf( buffer
, "[%s]", entry
.cFileName
);
1577 else sprintf( buffer
, "[%s]", entry
.cAlternateFileName
);
1579 else /* not a directory */
1581 #define ATTRIBS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
1582 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE)
1584 if ((attrib
& DDL_EXCLUSIVE
) &&
1585 ((attrib
& ATTRIBS
) != (entry
.dwFileAttributes
& ATTRIBS
)))
1588 if (long_names
) strcpy( buffer
, entry
.cFileName
);
1589 else strcpy( buffer
, entry
.cAlternateFileName
);
1591 if (!long_names
) CharLowerA( buffer
);
1592 pos
= LISTBOX_FindFileStrPos( wnd
, descr
, buffer
);
1593 if ((ret
= LISTBOX_InsertString( wnd
, descr
, pos
, buffer
)) < 0)
1595 } while (FindNextFileA( handle
, &entry
));
1596 FindClose( handle
);
1599 if ((ret
>= 0) && (attrib
& DDL_DRIVES
))
1601 char buffer
[] = "[-a-]";
1603 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++, buffer
[2]++)
1605 if (!DRIVE_IsValid(drive
)) continue;
1606 if ((ret
= LISTBOX_InsertString( wnd
, descr
, -1, buffer
)) < 0)
1614 /***********************************************************************
1615 * LISTBOX_HandleVScroll
1617 static LRESULT
LISTBOX_HandleVScroll( WND
*wnd
, LB_DESCR
*descr
,
1618 WPARAM wParam
, LPARAM lParam
)
1622 if (descr
->style
& LBS_MULTICOLUMN
) return 0;
1623 switch(LOWORD(wParam
))
1626 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
- 1, TRUE
);
1629 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+ 1, TRUE
);
1632 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-
1633 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1636 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+
1637 LISTBOX_GetCurrentPageSize( wnd
, descr
), TRUE
);
1639 case SB_THUMBPOSITION
:
1640 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
), TRUE
);
1643 info
.cbSize
= sizeof(info
);
1644 info
.fMask
= SIF_TRACKPOS
;
1645 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1646 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
, TRUE
);
1649 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1652 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1659 /***********************************************************************
1660 * LISTBOX_HandleHScroll
1662 static LRESULT
LISTBOX_HandleHScroll( WND
*wnd
, LB_DESCR
*descr
,
1663 WPARAM wParam
, LPARAM lParam
)
1668 if (descr
->style
& LBS_MULTICOLUMN
)
1670 switch(LOWORD(wParam
))
1673 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
-descr
->page_size
,
1677 LISTBOX_SetTopItem( wnd
, descr
, descr
->top_item
+descr
->page_size
,
1681 page
= descr
->width
/ descr
->column_width
;
1682 if (page
< 1) page
= 1;
1683 LISTBOX_SetTopItem( wnd
, descr
,
1684 descr
->top_item
- page
* descr
->page_size
, TRUE
);
1687 page
= descr
->width
/ descr
->column_width
;
1688 if (page
< 1) page
= 1;
1689 LISTBOX_SetTopItem( wnd
, descr
,
1690 descr
->top_item
+ page
* descr
->page_size
, TRUE
);
1692 case SB_THUMBPOSITION
:
1693 LISTBOX_SetTopItem( wnd
, descr
, HIWORD(wParam
)*descr
->page_size
,
1697 info
.cbSize
= sizeof(info
);
1698 info
.fMask
= SIF_TRACKPOS
;
1699 GetScrollInfo( wnd
->hwndSelf
, SB_VERT
, &info
);
1700 LISTBOX_SetTopItem( wnd
, descr
, info
.nTrackPos
*descr
->page_size
,
1704 LISTBOX_SetTopItem( wnd
, descr
, 0, TRUE
);
1707 LISTBOX_SetTopItem( wnd
, descr
, descr
->nb_items
, TRUE
);
1711 else if (descr
->horz_extent
)
1713 switch(LOWORD(wParam
))
1716 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
- 1 );
1719 LISTBOX_SetHorizontalPos( wnd
, descr
, descr
->horz_pos
+ 1 );
1722 LISTBOX_SetHorizontalPos( wnd
, descr
,
1723 descr
->horz_pos
- descr
->width
);
1726 LISTBOX_SetHorizontalPos( wnd
, descr
,
1727 descr
->horz_pos
+ descr
->width
);
1729 case SB_THUMBPOSITION
:
1730 LISTBOX_SetHorizontalPos( wnd
, descr
, HIWORD(wParam
) );
1733 info
.cbSize
= sizeof(info
);
1734 info
.fMask
= SIF_TRACKPOS
;
1735 GetScrollInfo( wnd
->hwndSelf
, SB_HORZ
, &info
);
1736 LISTBOX_SetHorizontalPos( wnd
, descr
, info
.nTrackPos
);
1739 LISTBOX_SetHorizontalPos( wnd
, descr
, 0 );
1742 LISTBOX_SetHorizontalPos( wnd
, descr
,
1743 descr
->horz_extent
- descr
->width
);
1751 /***********************************************************************
1752 * LISTBOX_HandleLButtonDown
1754 static LRESULT
LISTBOX_HandleLButtonDown( WND
*wnd
, LB_DESCR
*descr
,
1755 WPARAM wParam
, INT x
, INT y
)
1757 INT index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1758 TRACE("[%04x]: lbuttondown %d,%d item %d\n",
1759 wnd
->hwndSelf
, x
, y
, index
);
1760 if (!descr
->caret_on
&& (GetFocus() == wnd
->hwndSelf
)) return 0;
1763 if (descr
->style
& LBS_EXTENDEDSEL
)
1765 if (!(wParam
& MK_SHIFT
)) descr
->anchor_item
= index
;
1766 if (wParam
& MK_CONTROL
)
1768 LISTBOX_SetCaretIndex( wnd
, descr
, index
, FALSE
);
1769 LISTBOX_SetSelection( wnd
, descr
, index
,
1770 !descr
->items
[index
].selected
, FALSE
);
1772 else LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1776 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1777 LISTBOX_SetSelection( wnd
, descr
, index
,
1778 (!(descr
->style
& LBS_MULTIPLESEL
) ||
1779 !descr
->items
[index
].selected
), FALSE
);
1783 if( !descr
->lphc
) SetFocus( wnd
->hwndSelf
);
1784 else SetFocus( (descr
->lphc
->hWndEdit
) ? descr
->lphc
->hWndEdit
1785 : descr
->lphc
->self
->hwndSelf
) ;
1787 descr
->captured
= TRUE
;
1788 SetCapture( wnd
->hwndSelf
);
1789 if (index
!= -1 && !descr
->lphc
)
1791 if (descr
->style
& LBS_NOTIFY
)
1792 SendMessageA( descr
->owner
, WM_LBTRACKPOINT
, index
,
1793 MAKELPARAM( x
, y
) );
1794 if (wnd
->dwExStyle
& WS_EX_DRAGDETECT
)
1801 if (DragDetect( wnd
->hwndSelf
, pt
))
1802 SendMessageA( descr
->owner
, WM_BEGINDRAG
, 0, 0 );
1809 /***********************************************************************
1810 * LISTBOX_HandleLButtonUp
1812 static LRESULT
LISTBOX_HandleLButtonUp( WND
*wnd
, LB_DESCR
*descr
)
1814 if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1815 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1816 LISTBOX_Timer
= LB_TIMER_NONE
;
1817 if (descr
->captured
)
1819 descr
->captured
= FALSE
;
1820 if (GetCapture() == wnd
->hwndSelf
) ReleaseCapture();
1821 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
1822 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
1828 /***********************************************************************
1829 * LISTBOX_HandleTimer
1831 * Handle scrolling upon a timer event.
1832 * Return TRUE if scrolling should continue.
1834 static LRESULT
LISTBOX_HandleTimer( WND
*wnd
, LB_DESCR
*descr
,
1835 INT index
, TIMER_DIRECTION dir
)
1840 if (descr
->top_item
) index
= descr
->top_item
- 1;
1844 if (descr
->top_item
) index
-= descr
->page_size
;
1847 index
= descr
->top_item
+ LISTBOX_GetCurrentPageSize( wnd
, descr
);
1848 if (index
== descr
->focus_item
) index
++;
1849 if (index
>= descr
->nb_items
) index
= descr
->nb_items
- 1;
1851 case LB_TIMER_RIGHT
:
1852 if (index
+ descr
->page_size
< descr
->nb_items
)
1853 index
+= descr
->page_size
;
1858 if (index
== descr
->focus_item
) return FALSE
;
1859 LISTBOX_MoveCaret( wnd
, descr
, index
, FALSE
);
1864 /***********************************************************************
1865 * LISTBOX_HandleSystemTimer
1867 * WM_SYSTIMER handler.
1869 static LRESULT
LISTBOX_HandleSystemTimer( WND
*wnd
, LB_DESCR
*descr
)
1871 if (!LISTBOX_HandleTimer( wnd
, descr
, descr
->focus_item
, LISTBOX_Timer
))
1873 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1874 LISTBOX_Timer
= LB_TIMER_NONE
;
1880 /***********************************************************************
1881 * LISTBOX_HandleMouseMove
1883 * WM_MOUSEMOVE handler.
1885 static void LISTBOX_HandleMouseMove( WND
*wnd
, LB_DESCR
*descr
,
1889 TIMER_DIRECTION dir
;
1891 if (!descr
->captured
) return;
1893 if (descr
->style
& LBS_MULTICOLUMN
)
1896 else if (y
>= descr
->item_height
* descr
->page_size
)
1897 y
= descr
->item_height
* descr
->page_size
- 1;
1901 dir
= LB_TIMER_LEFT
;
1904 else if (x
>= descr
->width
)
1906 dir
= LB_TIMER_RIGHT
;
1907 x
= descr
->width
- 1;
1909 else dir
= LB_TIMER_NONE
; /* inside */
1913 if (y
< 0) dir
= LB_TIMER_UP
; /* above */
1914 else if (y
>= descr
->height
) dir
= LB_TIMER_DOWN
; /* below */
1915 else dir
= LB_TIMER_NONE
; /* inside */
1918 index
= LISTBOX_GetItemFromPoint( wnd
, descr
, x
, y
);
1919 if (index
== -1) index
= descr
->focus_item
;
1920 if (!LISTBOX_HandleTimer( wnd
, descr
, index
, dir
)) dir
= LB_TIMER_NONE
;
1922 /* Start/stop the system timer */
1924 if (dir
!= LB_TIMER_NONE
)
1925 SetSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
, LB_SCROLL_TIMEOUT
, NULL
);
1926 else if (LISTBOX_Timer
!= LB_TIMER_NONE
)
1927 KillSystemTimer( wnd
->hwndSelf
, LB_TIMER_ID
);
1928 LISTBOX_Timer
= dir
;
1932 /***********************************************************************
1933 * LISTBOX_HandleKeyDown
1935 static LRESULT
LISTBOX_HandleKeyDown( WND
*wnd
, LB_DESCR
*descr
, WPARAM wParam
)
1938 BOOL bForceSelection
= TRUE
; /* select item pointed to by focus_item */
1939 if ((IS_MULTISELECT(descr
)) || (descr
->selected_item
== descr
->focus_item
))
1940 bForceSelection
= FALSE
; /* only for single select list */
1942 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
1944 caret
= SendMessageA( descr
->owner
, WM_VKEYTOITEM
,
1945 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
1947 if (caret
== -2) return 0;
1949 if (caret
== -1) switch(wParam
)
1952 if (descr
->style
& LBS_MULTICOLUMN
)
1954 bForceSelection
= FALSE
;
1955 if (descr
->focus_item
>= descr
->page_size
)
1956 caret
= descr
->focus_item
- descr
->page_size
;
1961 caret
= descr
->focus_item
- 1;
1962 if (caret
< 0) caret
= 0;
1965 if (descr
->style
& LBS_MULTICOLUMN
)
1967 bForceSelection
= FALSE
;
1968 if (descr
->focus_item
+ descr
->page_size
< descr
->nb_items
)
1969 caret
= descr
->focus_item
+ descr
->page_size
;
1974 caret
= descr
->focus_item
+ 1;
1975 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
1979 if (descr
->style
& LBS_MULTICOLUMN
)
1981 INT page
= descr
->width
/ descr
->column_width
;
1982 if (page
< 1) page
= 1;
1983 caret
= descr
->focus_item
- (page
* descr
->page_size
) + 1;
1985 else caret
= descr
->focus_item
-LISTBOX_GetCurrentPageSize(wnd
,descr
)+1;
1986 if (caret
< 0) caret
= 0;
1989 if (descr
->style
& LBS_MULTICOLUMN
)
1991 INT page
= descr
->width
/ descr
->column_width
;
1992 if (page
< 1) page
= 1;
1993 caret
= descr
->focus_item
+ (page
* descr
->page_size
) - 1;
1995 else caret
= descr
->focus_item
+LISTBOX_GetCurrentPageSize(wnd
,descr
)-1;
1996 if (caret
>= descr
->nb_items
) caret
= descr
->nb_items
- 1;
2002 caret
= descr
->nb_items
- 1;
2005 if (descr
->style
& LBS_EXTENDEDSEL
) caret
= descr
->focus_item
;
2006 else if (descr
->style
& LBS_MULTIPLESEL
)
2008 LISTBOX_SetSelection( wnd
, descr
, descr
->focus_item
,
2009 !descr
->items
[descr
->focus_item
].selected
,
2010 (descr
->style
& LBS_NOTIFY
) != 0 );
2014 bForceSelection
= FALSE
;
2016 if (bForceSelection
) /* focused item is used instead of key */
2017 caret
= descr
->focus_item
;
2020 if ((descr
->style
& LBS_EXTENDEDSEL
) &&
2021 !(GetKeyState( VK_SHIFT
) & 0x8000))
2022 descr
->anchor_item
= caret
;
2023 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2024 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2025 if (descr
->style
& LBS_NOTIFY
)
2027 if( descr
->lphc
&& CB_GETTYPE(descr
->lphc
) != CBS_SIMPLE
)
2029 /* make sure that combo parent doesn't hide us */
2030 descr
->lphc
->wState
|= CBF_NOROLLUP
;
2032 if (descr
->nb_items
) SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2039 /***********************************************************************
2040 * LISTBOX_HandleChar
2042 static LRESULT
LISTBOX_HandleChar( WND
*wnd
, LB_DESCR
*descr
,
2048 str
[0] = wParam
& 0xff;
2051 if (descr
->style
& LBS_WANTKEYBOARDINPUT
)
2053 caret
= SendMessageA( descr
->owner
, WM_CHARTOITEM
,
2054 MAKEWPARAM(LOWORD(wParam
), descr
->focus_item
),
2056 if (caret
== -2) return 0;
2059 caret
= LISTBOX_FindString( wnd
, descr
, descr
->focus_item
, str
, FALSE
);
2062 if ((!IS_MULTISELECT(descr
)) && descr
->selected_item
== -1)
2063 LISTBOX_SetSelection( wnd
, descr
, caret
, TRUE
, FALSE
);
2064 LISTBOX_MoveCaret( wnd
, descr
, caret
, TRUE
);
2065 if ((descr
->style
& LBS_NOTIFY
) && descr
->nb_items
)
2066 SEND_NOTIFICATION( wnd
, descr
, LBN_SELCHANGE
);
2072 /***********************************************************************
2075 static BOOL
LISTBOX_Create( WND
*wnd
, LPHEADCOMBO lphc
)
2078 MEASUREITEMSTRUCT mis
;
2081 if (!(descr
= HeapAlloc( GetProcessHeap(), 0, sizeof(*descr
) )))
2083 if (!(descr
->heap
= HeapCreate( 0, 0x10000, 0 )))
2085 HeapFree( GetProcessHeap(), 0, descr
);
2088 GetClientRect( wnd
->hwndSelf
, &rect
);
2089 descr
->owner
= GetParent( wnd
->hwndSelf
);
2090 descr
->style
= wnd
->dwStyle
;
2091 descr
->width
= rect
.right
- rect
.left
;
2092 descr
->height
= rect
.bottom
- rect
.top
;
2093 descr
->items
= NULL
;
2094 descr
->nb_items
= 0;
2095 descr
->top_item
= 0;
2096 descr
->selected_item
= -1;
2097 descr
->focus_item
= 0;
2098 descr
->anchor_item
= -1;
2099 descr
->item_height
= 1;
2100 descr
->page_size
= 1;
2101 descr
->column_width
= 150;
2102 descr
->horz_extent
= (wnd
->dwStyle
& WS_HSCROLL
) ? 1 : 0;
2103 descr
->horz_pos
= 0;
2106 descr
->caret_on
= TRUE
;
2107 descr
->captured
= FALSE
;
2109 descr
->locale
= 0; /* FIXME */
2114 TRACE_(combo
)("[%04x]: resetting owner %04x -> %04x\n",
2115 wnd
->hwndSelf
, descr
->owner
, lphc
->self
->hwndSelf
);
2116 descr
->owner
= lphc
->self
->hwndSelf
;
2119 *(LB_DESCR
**)wnd
->wExtra
= descr
;
2121 /* if (wnd->dwExStyle & WS_EX_NOPARENTNOTIFY) descr->style &= ~LBS_NOTIFY;
2123 if (descr
->style
& LBS_EXTENDEDSEL
) descr
->style
|= LBS_MULTIPLESEL
;
2124 if (descr
->style
& LBS_MULTICOLUMN
) descr
->style
&= ~LBS_OWNERDRAWVARIABLE
;
2125 if (descr
->style
& LBS_OWNERDRAWVARIABLE
) descr
->style
|= LBS_NOINTEGRALHEIGHT
;
2126 descr
->item_height
= LISTBOX_SetFont( wnd
, descr
, 0 );
2128 if (descr
->style
& LBS_OWNERDRAWFIXED
)
2130 if( descr
->lphc
&& (descr
->lphc
->dwStyle
& CBS_DROPDOWN
))
2132 /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
2133 descr
->item_height
= lphc
->fixedOwnerDrawHeight
;
2137 UINT id
= (descr
->lphc
) ? ID_CB_LISTBOX
: wnd
->wIDmenu
;
2139 mis
.CtlType
= ODT_LISTBOX
;
2144 mis
.itemHeight
= descr
->item_height
;
2145 SendMessageA( descr
->owner
, WM_MEASUREITEM
, id
, (LPARAM
)&mis
);
2146 descr
->item_height
= mis
.itemHeight
? mis
.itemHeight
: 1;
2154 /***********************************************************************
2157 static BOOL
LISTBOX_Destroy( WND
*wnd
, LB_DESCR
*descr
)
2159 LISTBOX_ResetContent( wnd
, descr
);
2160 HeapDestroy( descr
->heap
);
2161 HeapFree( GetProcessHeap(), 0, descr
);
2167 /***********************************************************************
2170 static inline LRESULT WINAPI
ListBoxWndProc_locked( WND
* wnd
, UINT msg
,
2171 WPARAM wParam
, LPARAM lParam
)
2175 HWND hwnd
= wnd
->hwndSelf
;
2178 if (!(descr
= *(LB_DESCR
**)wnd
->wExtra
))
2184 if (!LISTBOX_Create( wnd
, NULL
))
2186 TRACE("creating wnd=%04x descr=%p\n",
2187 hwnd
, *(LB_DESCR
**)wnd
->wExtra
);
2193 * When a listbox is not in a combobox and the look
2194 * is win95, the WS_BORDER style is replaced with
2195 * the WS_EX_CLIENTEDGE style.
2197 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2198 (wnd
->dwStyle
& WS_BORDER
) )
2200 wnd
->dwExStyle
|= WS_EX_CLIENTEDGE
;
2201 wnd
->dwStyle
&= ~ WS_BORDER
;
2206 /* Ignore all other messages before we get a WM_CREATE */
2207 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2210 TRACE("[%04x]: msg %s wp %08x lp %08lx\n",
2211 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2214 case LB_RESETCONTENT16
:
2215 case LB_RESETCONTENT
:
2216 LISTBOX_ResetContent( wnd
, descr
);
2219 case LB_ADDSTRING16
:
2220 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2223 wParam
= LISTBOX_FindStringPos( wnd
, descr
, (LPCSTR
)lParam
, FALSE
);
2224 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2226 case LB_INSERTSTRING16
:
2227 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2228 wParam
= (INT
)(INT16
)wParam
;
2230 case LB_INSERTSTRING
:
2231 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2234 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2237 wParam
= LISTBOX_FindFileStrPos( wnd
, descr
, (LPCSTR
)lParam
);
2238 return LISTBOX_InsertString( wnd
, descr
, wParam
, (LPCSTR
)lParam
);
2240 case LB_DELETESTRING16
:
2241 case LB_DELETESTRING
:
2242 if (LISTBOX_RemoveItem( wnd
, descr
, wParam
) != LB_ERR
)
2243 return descr
->nb_items
;
2247 case LB_GETITEMDATA16
:
2248 case LB_GETITEMDATA
:
2249 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2251 return descr
->items
[wParam
].data
;
2253 case LB_SETITEMDATA16
:
2254 case LB_SETITEMDATA
:
2255 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2257 descr
->items
[wParam
].data
= (DWORD
)lParam
;
2262 return descr
->nb_items
;
2265 lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2268 return LISTBOX_GetText( wnd
, descr
, wParam
, (LPSTR
)lParam
);
2270 case LB_GETTEXTLEN16
:
2273 if (wParam
>= descr
->nb_items
)
2275 return (HAS_STRINGS(descr
) ? strlen(descr
->items
[wParam
].str
)
2278 case LB_GETCURSEL16
:
2280 if (descr
->nb_items
==0)
2282 if (!IS_MULTISELECT(descr
))
2283 return descr
->selected_item
;
2285 if (descr
->selected_item
!=-1)
2286 return descr
->selected_item
;
2288 return descr
->focus_item
;
2289 /* otherwise, if the user tries to move the selection with the */
2290 /* arrow keys, we will give the application something to choke on */
2291 case LB_GETTOPINDEX16
:
2292 case LB_GETTOPINDEX
:
2293 return descr
->top_item
;
2295 case LB_GETITEMHEIGHT16
:
2296 case LB_GETITEMHEIGHT
:
2297 return LISTBOX_GetItemHeight( wnd
, descr
, wParam
);
2299 case LB_SETITEMHEIGHT16
:
2300 lParam
= LOWORD(lParam
);
2302 case LB_SETITEMHEIGHT
:
2303 return LISTBOX_SetItemHeight( wnd
, descr
, wParam
, lParam
);
2305 case LB_ITEMFROMPOINT
:
2310 pt
.x
= LOWORD(lParam
);
2311 pt
.y
= HIWORD(lParam
);
2314 rect
.right
= descr
->width
;
2315 rect
.bottom
= descr
->height
;
2317 return MAKELONG( LISTBOX_GetItemFromPoint(wnd
, descr
, pt
.x
, pt
.y
),
2318 !PtInRect( &rect
, pt
) );
2321 case LB_SETCARETINDEX16
:
2322 case LB_SETCARETINDEX
:
2323 if ((!IS_MULTISELECT(descr
)) && (descr
->selected_item
!= -1)) return LB_ERR
;
2324 if (LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, !lParam
) == LB_ERR
)
2331 case LB_GETCARETINDEX16
:
2332 case LB_GETCARETINDEX
:
2333 return descr
->focus_item
;
2335 case LB_SETTOPINDEX16
:
2336 case LB_SETTOPINDEX
:
2337 return LISTBOX_SetTopItem( wnd
, descr
, wParam
, TRUE
);
2339 case LB_SETCOLUMNWIDTH16
:
2340 case LB_SETCOLUMNWIDTH
:
2341 return LISTBOX_SetColumnWidth( wnd
, descr
, wParam
);
2343 case LB_GETITEMRECT16
:
2346 ret
= LISTBOX_GetItemRect( wnd
, descr
, (INT16
)wParam
, &rect
);
2347 CONV_RECT32TO16( &rect
, (RECT16
*)PTR_SEG_TO_LIN(lParam
) );
2351 case LB_GETITEMRECT
:
2352 return LISTBOX_GetItemRect( wnd
, descr
, wParam
, (RECT
*)lParam
);
2354 case LB_FINDSTRING16
:
2355 wParam
= (INT
)(INT16
)wParam
;
2356 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2359 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, FALSE
);
2361 case LB_FINDSTRINGEXACT16
:
2362 wParam
= (INT
)(INT16
)wParam
;
2363 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2365 case LB_FINDSTRINGEXACT
:
2366 return LISTBOX_FindString( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2368 case LB_SELECTSTRING16
:
2369 wParam
= (INT
)(INT16
)wParam
;
2370 if (HAS_STRINGS(descr
)) lParam
= (LPARAM
)PTR_SEG_TO_LIN(lParam
);
2372 case LB_SELECTSTRING
:
2374 INT index
= LISTBOX_FindString( wnd
, descr
, wParam
,
2375 (LPCSTR
)lParam
, FALSE
);
2376 if (index
== LB_ERR
)
2378 LISTBOX_SetSelection( wnd
, descr
, index
, TRUE
, FALSE
);
2383 wParam
= (INT
)(INT16
)wParam
;
2386 if (((INT
)wParam
< 0) || ((INT
)wParam
>= descr
->nb_items
))
2388 return descr
->items
[wParam
].selected
;
2391 lParam
= (INT
)(INT16
)lParam
;
2394 return LISTBOX_SetSelection( wnd
, descr
, lParam
, wParam
, FALSE
);
2396 case LB_SETCURSEL16
:
2397 wParam
= (INT
)(INT16
)wParam
;
2400 if (IS_MULTISELECT(descr
)) return LB_ERR
;
2401 LISTBOX_SetCaretIndex( wnd
, descr
, wParam
, TRUE
);
2402 return LISTBOX_SetSelection( wnd
, descr
, wParam
, TRUE
, FALSE
);
2404 case LB_GETSELCOUNT16
:
2405 case LB_GETSELCOUNT
:
2406 return LISTBOX_GetSelCount( wnd
, descr
);
2408 case LB_GETSELITEMS16
:
2409 return LISTBOX_GetSelItems16( wnd
, descr
, wParam
,
2410 (LPINT16
)PTR_SEG_TO_LIN(lParam
) );
2412 case LB_GETSELITEMS
:
2413 return LISTBOX_GetSelItems( wnd
, descr
, wParam
, (LPINT
)lParam
);
2415 case LB_SELITEMRANGE16
:
2416 case LB_SELITEMRANGE
:
2417 if (LOWORD(lParam
) <= HIWORD(lParam
))
2418 return LISTBOX_SelectItemRange( wnd
, descr
, LOWORD(lParam
),
2419 HIWORD(lParam
), wParam
);
2421 return LISTBOX_SelectItemRange( wnd
, descr
, HIWORD(lParam
),
2422 LOWORD(lParam
), wParam
);
2424 case LB_SELITEMRANGEEX16
:
2425 case LB_SELITEMRANGEEX
:
2426 if ((INT
)lParam
>= (INT
)wParam
)
2427 return LISTBOX_SelectItemRange( wnd
, descr
, wParam
, lParam
, TRUE
);
2429 return LISTBOX_SelectItemRange( wnd
, descr
, lParam
, wParam
, FALSE
);
2431 case LB_GETHORIZONTALEXTENT16
:
2432 case LB_GETHORIZONTALEXTENT
:
2433 return descr
->horz_extent
;
2435 case LB_SETHORIZONTALEXTENT16
:
2436 case LB_SETHORIZONTALEXTENT
:
2437 return LISTBOX_SetHorizontalExtent( wnd
, descr
, wParam
);
2439 case LB_GETANCHORINDEX16
:
2440 case LB_GETANCHORINDEX
:
2441 return descr
->anchor_item
;
2443 case LB_SETANCHORINDEX16
:
2444 wParam
= (INT
)(INT16
)wParam
;
2446 case LB_SETANCHORINDEX
:
2447 if (((INT
)wParam
< -1) || ((INT
)wParam
>= descr
->nb_items
))
2449 descr
->anchor_item
= (INT
)wParam
;
2453 return LISTBOX_Directory( wnd
, descr
, wParam
,
2454 (LPCSTR
)PTR_SEG_TO_LIN(lParam
), FALSE
);
2457 return LISTBOX_Directory( wnd
, descr
, wParam
, (LPCSTR
)lParam
, TRUE
);
2460 return descr
->locale
;
2463 descr
->locale
= (LCID
)wParam
; /* FIXME: should check for valid lcid */
2466 case LB_INITSTORAGE
:
2467 return LISTBOX_InitStorage( wnd
, descr
, wParam
, (DWORD
)lParam
);
2470 return LISTBOX_SetCount( wnd
, descr
, (INT
)wParam
);
2472 case LB_SETTABSTOPS16
:
2473 return LISTBOX_SetTabStops( wnd
, descr
, (INT
)(INT16
)wParam
,
2474 (LPINT
)PTR_SEG_TO_LIN(lParam
), TRUE
);
2476 case LB_SETTABSTOPS
:
2477 return LISTBOX_SetTabStops( wnd
, descr
, wParam
, (LPINT
)lParam
, FALSE
);
2481 if (descr
->caret_on
)
2483 descr
->caret_on
= TRUE
;
2484 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2485 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2490 if (!descr
->caret_on
)
2492 descr
->caret_on
= FALSE
;
2493 if ((descr
->focus_item
!= -1) && (GetFocus() == wnd
->hwndSelf
))
2494 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2498 return LISTBOX_Destroy( wnd
, descr
);
2501 InvalidateRect( hwnd
, NULL
, TRUE
);
2505 LISTBOX_SetRedraw( wnd
, descr
, wParam
!= 0 );
2509 return 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
);
2521 LISTBOX_UpdateSize( wnd
, descr
);
2526 LISTBOX_SetFont( wnd
, descr
, (HFONT
)wParam
);
2527 if (lParam
) InvalidateRect( wnd
->hwndSelf
, 0, TRUE
);
2530 descr
->caret_on
= TRUE
;
2531 if (descr
->focus_item
!= -1)
2532 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2533 SEND_NOTIFICATION( wnd
, descr
, LBN_SETFOCUS
);
2536 if ((descr
->focus_item
!= -1) && descr
->caret_on
)
2537 LISTBOX_RepaintItem( wnd
, descr
, descr
->focus_item
, ODA_FOCUS
);
2538 SEND_NOTIFICATION( wnd
, descr
, LBN_KILLFOCUS
);
2541 return LISTBOX_HandleHScroll( wnd
, descr
, wParam
, lParam
);
2543 return LISTBOX_HandleVScroll( wnd
, descr
, wParam
, lParam
);
2544 case WM_LBUTTONDOWN
:
2545 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2546 (INT16
)LOWORD(lParam
),
2547 (INT16
)HIWORD(lParam
) );
2548 case WM_LBUTTONDBLCLK
:
2549 if (descr
->style
& LBS_NOTIFY
)
2550 SEND_NOTIFICATION( wnd
, descr
, LBN_DBLCLK
);
2553 if (GetCapture() == hwnd
)
2554 LISTBOX_HandleMouseMove( wnd
, descr
, (INT16
)LOWORD(lParam
),
2555 (INT16
)HIWORD(lParam
) );
2558 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2560 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2562 return LISTBOX_HandleChar( wnd
, descr
, wParam
);
2564 return LISTBOX_HandleSystemTimer( wnd
, descr
);
2566 if (IS_OWNERDRAW(descr
))
2569 HBRUSH hbrush
= SendMessageA( descr
->owner
, WM_CTLCOLORLISTBOX
,
2570 wParam
, (LPARAM
)wnd
->hwndSelf
);
2571 GetClientRect(hwnd
, &rect
);
2572 if (hbrush
) FillRect( (HDC
)wParam
, &rect
, hbrush
);
2577 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2581 case WM_QUERYDROPOBJECT
:
2586 LPDRAGINFO dragInfo
= (LPDRAGINFO
)PTR_SEG_TO_LIN( (SEGPTR
)lParam
);
2587 dragInfo
->l
= LISTBOX_GetItemFromPoint( wnd
, descr
, dragInfo
->pt
.x
,
2589 return SendMessageA( descr
->owner
, msg
, wParam
, lParam
);
2594 if ((msg
>= WM_USER
) && (msg
< 0xc000))
2595 WARN("[%04x]: unknown msg %04x wp %08x lp %08lx\n",
2596 hwnd
, msg
, wParam
, lParam
);
2597 return DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2602 /***********************************************************************
2605 * This is just a wrapper for the real wndproc, it only does window locking
2608 LRESULT WINAPI
ListBoxWndProc( HWND hwnd
, UINT msg
,
2609 WPARAM wParam
, LPARAM lParam
)
2611 WND
* wndPtr
= WIN_FindWndPtr( hwnd
);
2612 LRESULT res
= ListBoxWndProc_locked(wndPtr
,msg
,wParam
,lParam
);
2614 WIN_ReleaseWndPtr(wndPtr
);
2618 /***********************************************************************
2621 LRESULT
COMBO_Directory( LPHEADCOMBO lphc
, UINT attrib
, LPSTR dir
, BOOL bLong
)
2623 WND
*wnd
= WIN_FindWndPtr( lphc
->hWndLBox
);
2627 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2630 LRESULT lRet
= LISTBOX_Directory( wnd
, descr
, attrib
, dir
, bLong
);
2632 RedrawWindow( lphc
->self
->hwndSelf
, NULL
, 0,
2633 RDW_INVALIDATE
| RDW_ERASE
| RDW_UPDATENOW
);
2634 WIN_ReleaseWndPtr(wnd
);
2637 WIN_ReleaseWndPtr(wnd
);
2642 /***********************************************************************
2643 * ComboLBWndProc_locked
2645 * The real combo listbox wndproc, but called with locked WND struct.
2647 static inline LRESULT WINAPI
ComboLBWndProc_locked( WND
* wnd
, UINT msg
,
2648 WPARAM wParam
, LPARAM lParam
)
2651 HWND hwnd
= wnd
->hwndSelf
;
2655 LB_DESCR
*descr
= *(LB_DESCR
**)wnd
->wExtra
;
2657 TRACE_(combo
)("[%04x]: msg %s wp %08x lp %08lx\n",
2658 wnd
->hwndSelf
, SPY_GetMsgName(msg
), wParam
, lParam
);
2660 if( descr
|| msg
== WM_CREATE
)
2662 LPHEADCOMBO lphc
= (descr
) ? descr
->lphc
: NULL
;
2667 #define lpcs ((LPCREATESTRUCTA)lParam)
2668 TRACE_(combo
)("\tpassed parent handle = 0x%08x\n",
2669 (UINT
)lpcs
->lpCreateParams
);
2671 lphc
= (LPHEADCOMBO
)(lpcs
->lpCreateParams
);
2673 return LISTBOX_Create( wnd
, lphc
);
2675 if ( (TWEAK_WineLook
> WIN31_LOOK
) &&
2676 (CB_GETTYPE(lphc
) != CBS_SIMPLE
) )
2682 mousePos
.x
= (INT16
)LOWORD(lParam
);
2683 mousePos
.y
= (INT16
)HIWORD(lParam
);
2686 * If we are in a dropdown combobox, we simulate that
2687 * the mouse is captured to show the tracking of the item.
2689 captured
= descr
->captured
;
2690 descr
->captured
= TRUE
;
2692 LISTBOX_HandleMouseMove( wnd
,
2694 mousePos
.x
, mousePos
.y
);
2696 descr
->captured
= captured
;
2699 * However, when tracking, it is important that we do not
2700 * perform a selection if the cursor is outside the list.
2702 GetClientRect(hwnd
, &clientRect
);
2704 if (!PtInRect( &clientRect
, mousePos
))
2706 LISTBOX_MoveCaret( wnd
, descr
, -1, FALSE
);
2714 * If we are in Win3.1 look, go with the default behavior.
2716 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2719 if (TWEAK_WineLook
> WIN31_LOOK
)
2725 * If the mouse button "up" is not in the listbox,
2726 * we make sure there is no selection by re-selecting the
2727 * item that was selected when the listbox was made visible.
2729 mousePos
.x
= (INT16
)LOWORD(lParam
);
2730 mousePos
.y
= (INT16
)HIWORD(lParam
);
2732 GetClientRect(hwnd
, &clientRect
);
2735 * When the user clicks outside the combobox and the focus
2736 * is lost, the owning combobox will send a fake buttonup with
2737 * 0xFFFFFFF as the mouse location, we must also revert the
2738 * selection to the original selection.
2740 if ( (lParam
== 0xFFFFFFFF) ||
2741 (!PtInRect( &clientRect
, mousePos
)) )
2743 LISTBOX_MoveCaret( wnd
,
2749 return LISTBOX_HandleLButtonUp( wnd
, descr
);
2750 case WM_LBUTTONDOWN
:
2751 return LISTBOX_HandleLButtonDown( wnd
, descr
, wParam
,
2752 (INT16
)LOWORD(lParam
), (INT16
)HIWORD(lParam
));
2753 /* avoid activation at all costs */
2755 case WM_MOUSEACTIVATE
:
2756 return MA_NOACTIVATE
;
2760 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2762 /* for some reason(?) Windows makes it possible to
2763 * show/hide ComboLBox by sending it WM_KEYDOWNs */
2765 if( (!(lphc
->wState
& CBF_EUI
) && wParam
== VK_F4
) ||
2766 ( (lphc
->wState
& CBF_EUI
) && !(lphc
->wState
& CBF_DROPPED
)
2767 && (wParam
== VK_DOWN
|| wParam
== VK_UP
)) )
2769 COMBO_FlipListbox( lphc
, FALSE
);
2773 return LISTBOX_HandleKeyDown( wnd
, descr
, wParam
);
2775 case LB_SETCURSEL16
:
2777 lRet
= ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2778 lRet
=(lRet
== LB_ERR
) ? lRet
: descr
->selected_item
;
2781 if( CB_GETTYPE(lphc
) != CBS_SIMPLE
)
2786 return ListBoxWndProc( hwnd
, msg
, wParam
, lParam
);
2789 lRet
= DefWindowProcA( hwnd
, msg
, wParam
, lParam
);
2791 TRACE_(combo
)("\t default on msg [%04x]\n", (UINT16
)msg
);
2796 /***********************************************************************
2799 * NOTE: in Windows, winproc address of the ComboLBox is the same
2800 * as that of the Listbox.
2802 * This is just a wrapper for the real wndproc, it only does window locking
2805 LRESULT WINAPI
ComboLBWndProc( HWND hwnd
, UINT msg
,
2806 WPARAM wParam
, LPARAM lParam
)
2808 WND
*wnd
= WIN_FindWndPtr( hwnd
);
2809 LRESULT res
= ComboLBWndProc_locked(wnd
,msg
,wParam
,lParam
);
2811 WIN_ReleaseWndPtr(wnd
);