4 * Copyright 1999 Eric Kohl
5 * Copyright 2004 Robert Shearman
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * This code was audited for completeness against the documented features
24 * of Comctl32.dll version 6.0 on Mar. 10, 2004, by Robert Shearman.
26 * Unless otherwise noted, we believe this code to be complete, as per
27 * the specification mentioned above.
28 * If you discover missing features or bugs please note them below.
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(commctrl
);
45 /* for compiler compatibility we only accept literal ASCII strings */
47 #define TEXT(string) string
49 #define DRAGLIST_SUBCLASSID 0
50 #define DRAGLIST_SCROLLPERIOD 200
51 #define DRAGLIST_TIMERID 666
53 /* properties relating to IDI_DRAGICON */
54 #define DRAGICON_HOTSPOT_X 17
55 #define DRAGICON_HOTSPOT_Y 7
56 #define DRAGICON_HEIGHT 32
58 /* internal Wine specific data for the drag list control */
59 typedef struct _DRAGLISTDATA
61 /* are we currently in dragging mode? */
64 /* cursor to use as determined by DL_DRAGGING notification.
65 * NOTE: as we use LoadCursor we don't have to use DeleteCursor
66 * when we are finished with it */
69 /* optimisation so that we don't have to load the cursor
70 * all of the time whilst dragging */
71 LRESULT last_dragging_response
;
73 /* prevents flicker with drawing drag arrow */
74 RECT last_drag_icon_rect
;
77 static UINT uDragListMessage
= 0; /* registered window message code */
78 static DWORD dwLastScrollTime
= 0;
79 static HICON hDragArrow
= NULL
;
81 /***********************************************************************
82 * DragList_Notify (internal)
84 * Sends notification messages to the parent control. Note that it
85 * does not use WM_NOTIFY like the rest of the controls, but a registered
88 static LRESULT
DragList_Notify(HWND hwndLB
, UINT uNotification
)
92 dli
.uNotification
= uNotification
;
93 GetCursorPos(&dli
.ptCursor
);
94 return SendMessageW(GetParent(hwndLB
), uDragListMessage
, GetDlgCtrlID(hwndLB
), (LPARAM
)&dli
);
97 /* cleans up after dragging */
98 static inline void DragList_EndDrag(HWND hwnd
, DRAGLISTDATA
* data
)
100 KillTimer(hwnd
, DRAGLIST_TIMERID
);
102 data
->dragging
= FALSE
;
103 /* clear any drag insert icon present */
104 InvalidateRect(GetParent(hwnd
), &data
->last_drag_icon_rect
, TRUE
);
107 /***********************************************************************
108 * DragList_SubclassWindowProc (internal)
110 * Handles certain messages to enable dragging for the ListBox and forwards
111 * the rest to the ListBox.
113 static LRESULT CALLBACK
114 DragList_SubclassWindowProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, UINT_PTR uIdSubclass
, DWORD_PTR dwRefData
)
116 DRAGLISTDATA
* data
= (DRAGLISTDATA
*)dwRefData
;
122 SetRectEmpty(&data
->last_drag_icon_rect
);
123 data
->dragging
= DragList_Notify(hwnd
, DL_BEGINDRAG
);
127 SetTimer(hwnd
, DRAGLIST_TIMERID
, DRAGLIST_SCROLLPERIOD
, NULL
);
129 /* note that we don't absorb this message to let the list box
130 * do its thing (normally selecting an item) */
135 /* user cancelled drag by either right clicking or
136 * by pressing the escape key */
137 if ((data
->dragging
) &&
138 ((uMsg
== WM_RBUTTONDOWN
) || (wParam
== VK_ESCAPE
)))
140 /* clean up and absorb message */
141 DragList_EndDrag(hwnd
, data
);
142 DragList_Notify(hwnd
, DL_CANCELDRAG
);
151 LRESULT cursor
= DragList_Notify(hwnd
, DL_DRAGGING
);
152 /* optimisation so that we don't have to load the cursor
153 * all of the time whilst dragging */
154 if (data
->last_dragging_response
!= cursor
)
159 data
->cursor
= LoadCursorW(NULL
, (LPCWSTR
)IDC_NO
);
160 SetCursor(data
->cursor
);
163 data
->cursor
= LoadCursorW(COMCTL32_hModule
, (LPCWSTR
)IDC_COPY
);
164 SetCursor(data
->cursor
);
167 data
->cursor
= LoadCursorW(NULL
, (LPCWSTR
)IDC_ARROW
);
168 SetCursor(data
->cursor
);
171 data
->last_dragging_response
= cursor
;
173 /* don't pass this message on to List Box */
181 DragList_EndDrag(hwnd
, data
);
182 DragList_Notify(hwnd
, DL_DROPPED
);
187 /* if app has told us to set a cursor then do so */
188 if (data
->dragging
&& data
->cursor
)
190 SetCursor(data
->cursor
);
196 /* tell dialog boxes that we want to receive WM_KEYDOWN events
197 * for keys like VK_ESCAPE */
199 return DLGC_WANTALLKEYS
;
202 RemoveWindowSubclass(hwnd
, DragList_SubclassWindowProc
, DRAGLIST_SUBCLASSID
);
206 return DefSubclassProc(hwnd
, uMsg
, wParam
, lParam
);
209 /***********************************************************************
210 * MakeDragList (COMCTL32.13)
212 * Makes a normal ListBox into a DragList by subclassing it.
218 BOOL WINAPI
MakeDragList (HWND hwndLB
)
220 DRAGLISTDATA
* data
= Alloc(sizeof(DRAGLISTDATA
));
222 TRACE("(%p)\n", hwndLB
);
224 if (!uDragListMessage
)
225 uDragListMessage
= RegisterWindowMessageA(DRAGLISTMSGSTRING
);
227 return SetWindowSubclass(hwndLB
, DragList_SubclassWindowProc
, DRAGLIST_SUBCLASSID
, (DWORD_PTR
)data
);
230 /***********************************************************************
231 * DrawInsert (COMCTL32.15)
233 * Draws insert arrow by the side of the ListBox item in the parent window.
238 VOID WINAPI
DrawInsert (HWND hwndParent
, HWND hwndLB
, INT nItem
)
240 RECT rcItem
, rcListBox
, rcDragIcon
;
244 TRACE("(%p %p %d)\n", hwndParent
, hwndLB
, nItem
);
247 hDragArrow
= LoadIconW(COMCTL32_hModule
, (LPCWSTR
)IDI_DRAGARROW
);
249 if (LB_ERR
== SendMessageW(hwndLB
, LB_GETITEMRECT
, nItem
, (LPARAM
)&rcItem
))
252 if (!GetWindowRect(hwndLB
, &rcListBox
))
255 /* convert item rect to parent co-ordinates */
256 if (!MapWindowPoints(hwndLB
, hwndParent
, (LPPOINT
)&rcItem
, 2))
259 /* convert list box rect to parent co-ordinates */
260 if (!MapWindowPoints(HWND_DESKTOP
, hwndParent
, (LPPOINT
)&rcListBox
, 2))
263 rcDragIcon
.left
= rcListBox
.left
- DRAGICON_HOTSPOT_X
;
264 rcDragIcon
.top
= rcItem
.top
- DRAGICON_HOTSPOT_Y
;
265 rcDragIcon
.right
= rcListBox
.left
;
266 rcDragIcon
.bottom
= rcDragIcon
.top
+ DRAGICON_HEIGHT
;
268 if (!GetWindowSubclass(hwndLB
, DragList_SubclassWindowProc
, DRAGLIST_SUBCLASSID
, (DWORD_PTR
*)&data
))
272 SetRectEmpty(&rcDragIcon
);
274 /* prevent flicker by only redrawing when necessary */
275 if (!EqualRect(&rcDragIcon
, &data
->last_drag_icon_rect
))
277 /* get rid of any previous inserts drawn */
278 RedrawWindow(hwndParent
, &data
->last_drag_icon_rect
, NULL
,
279 RDW_INTERNALPAINT
| RDW_ERASE
| RDW_INVALIDATE
| RDW_UPDATENOW
);
281 CopyRect(&data
->last_drag_icon_rect
, &rcDragIcon
);
285 hdc
= GetDC(hwndParent
);
287 DrawIcon(hdc
, rcDragIcon
.left
, rcDragIcon
.top
, hDragArrow
);
289 ReleaseDC(hwndParent
, hdc
);
294 /***********************************************************************
295 * LBItemFromPt (COMCTL32.14)
297 * Gets the index of the ListBox item under the specified point,
298 * scrolling if bAutoScroll is TRUE and pt is outside of the ListBox.
301 * The ListBox item ID if pt is over a list item or -1 otherwise.
303 INT WINAPI
LBItemFromPt (HWND hwndLB
, POINT pt
, BOOL bAutoScroll
)
309 TRACE("(%p %ld x %ld %s)\n",
310 hwndLB
, pt
.x
, pt
.y
, bAutoScroll
? "TRUE" : "FALSE");
312 ScreenToClient (hwndLB
, &pt
);
313 GetClientRect (hwndLB
, &rcClient
);
314 nIndex
= (INT
)SendMessageA (hwndLB
, LB_GETTOPINDEX
, 0, 0);
316 if (PtInRect (&rcClient
, pt
))
318 /* point is inside -- get the item index */
321 if (SendMessageA (hwndLB
, LB_GETITEMRECT
, nIndex
, (LPARAM
)&rcClient
) == LB_ERR
)
324 if (PtInRect (&rcClient
, pt
))
332 /* point is outside */
336 if ((pt
.x
> rcClient
.right
) || (pt
.x
< rcClient
.left
))
344 dwScrollTime
= GetTickCount ();
346 if ((dwScrollTime
- dwLastScrollTime
) < DRAGLIST_SCROLLPERIOD
)
349 dwLastScrollTime
= dwScrollTime
;
351 SendMessageA (hwndLB
, LB_SETTOPINDEX
, (WPARAM
)nIndex
, 0);