[COMCTL32] Install comctl32 v6 and its manifest in first stage
[reactos.git] / dll / win32 / comctl32 / draglist.c
1 /*
2 * Drag List control
3 *
4 * Copyright 1999 Eric Kohl
5 * Copyright 2004 Robert Shearman
6 *
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.
11 *
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.
16 *
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 *
21 * NOTES
22 *
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.
25 *
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.
29 *
30 */
31
32 #include "comctl32.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
35
36 #define DRAGLIST_SUBCLASSID 0
37 #define DRAGLIST_SCROLLPERIOD 200
38 #define DRAGLIST_TIMERID 666
39
40 /* properties relating to IDI_DRAGICON */
41 #define DRAGICON_HOTSPOT_X 17
42 #define DRAGICON_HOTSPOT_Y 7
43 #define DRAGICON_HEIGHT 32
44
45 /* internal Wine specific data for the drag list control */
46 typedef struct _DRAGLISTDATA
47 {
48 /* are we currently in dragging mode? */
49 BOOL dragging;
50
51 /* cursor to use as determined by DL_DRAGGING notification.
52 * NOTE: as we use LoadCursor we don't have to use DeleteCursor
53 * when we are finished with it */
54 HCURSOR cursor;
55
56 /* optimisation so that we don't have to load the cursor
57 * all of the time whilst dragging */
58 LRESULT last_dragging_response;
59
60 /* prevents flicker with drawing drag arrow */
61 RECT last_drag_icon_rect;
62 } DRAGLISTDATA;
63
64 UINT uDragListMessage = 0; /* registered window message code */
65 static DWORD dwLastScrollTime = 0;
66 static HICON hDragArrow = NULL;
67
68 /***********************************************************************
69 * DragList_Notify (internal)
70 *
71 * Sends notification messages to the parent control. Note that it
72 * does not use WM_NOTIFY like the rest of the controls, but a registered
73 * window message.
74 */
75 static LRESULT DragList_Notify(HWND hwndLB, UINT uNotification)
76 {
77 DRAGLISTINFO dli;
78 dli.hWnd = hwndLB;
79 dli.uNotification = uNotification;
80 GetCursorPos(&dli.ptCursor);
81 return SendMessageW(GetParent(hwndLB), uDragListMessage, GetDlgCtrlID(hwndLB), (LPARAM)&dli);
82 }
83
84 /* cleans up after dragging */
85 static void DragList_EndDrag(HWND hwnd, DRAGLISTDATA * data)
86 {
87 KillTimer(hwnd, DRAGLIST_TIMERID);
88 ReleaseCapture();
89 /* clear any drag insert icon present */
90 InvalidateRect(GetParent(hwnd), &data->last_drag_icon_rect, TRUE);
91 /* clear data for next use */
92 memset(data, 0, sizeof(*data));
93 }
94
95 /***********************************************************************
96 * DragList_SubclassWindowProc (internal)
97 *
98 * Handles certain messages to enable dragging for the ListBox and forwards
99 * the rest to the ListBox.
100 */
101 static LRESULT CALLBACK
102 DragList_SubclassWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
103 {
104 DRAGLISTDATA * data = (DRAGLISTDATA*)dwRefData;
105 switch (uMsg)
106 {
107 case WM_LBUTTONDOWN:
108 SetFocus(hwnd);
109 data->dragging = DragList_Notify(hwnd, DL_BEGINDRAG);
110 if (data->dragging)
111 {
112 SetCapture(hwnd);
113 SetTimer(hwnd, DRAGLIST_TIMERID, DRAGLIST_SCROLLPERIOD, NULL);
114 }
115 /* note that we don't absorb this message to let the list box
116 * do its thing (normally selecting an item) */
117 break;
118
119 case WM_KEYDOWN:
120 case WM_RBUTTONDOWN:
121 /* user cancelled drag by either right clicking or
122 * by pressing the escape key */
123 if ((data->dragging) &&
124 ((uMsg == WM_RBUTTONDOWN) || (wParam == VK_ESCAPE)))
125 {
126 /* clean up and absorb message */
127 DragList_EndDrag(hwnd, data);
128 DragList_Notify(hwnd, DL_CANCELDRAG);
129 return 0;
130 }
131 break;
132
133 case WM_MOUSEMOVE:
134 case WM_TIMER:
135 if (data->dragging)
136 {
137 LRESULT cursor = DragList_Notify(hwnd, DL_DRAGGING);
138 /* optimisation so that we don't have to load the cursor
139 * all of the time whilst dragging */
140 if (data->last_dragging_response != cursor)
141 {
142 switch (cursor)
143 {
144 case DL_STOPCURSOR:
145 data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_NO);
146 SetCursor(data->cursor);
147 break;
148 case DL_COPYCURSOR:
149 data->cursor = LoadCursorW(COMCTL32_hModule, (LPCWSTR)IDC_COPY);
150 SetCursor(data->cursor);
151 break;
152 case DL_MOVECURSOR:
153 data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
154 SetCursor(data->cursor);
155 break;
156 }
157 data->last_dragging_response = cursor;
158 }
159 /* don't pass this message on to List Box */
160 return 0;
161 }
162 break;
163
164 case WM_LBUTTONUP:
165 if (data->dragging)
166 {
167 DragList_EndDrag(hwnd, data);
168 DragList_Notify(hwnd, DL_DROPPED);
169 }
170 break;
171
172 case WM_GETDLGCODE:
173 /* tell dialog boxes that we want to receive WM_KEYDOWN events
174 * for keys like VK_ESCAPE */
175 if (data->dragging)
176 return DLGC_WANTALLKEYS;
177 break;
178 case WM_NCDESTROY:
179 RemoveWindowSubclass(hwnd, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID);
180 Free(data);
181 break;
182 }
183 return DefSubclassProc(hwnd, uMsg, wParam, lParam);
184 }
185
186 /***********************************************************************
187 * MakeDragList (COMCTL32.13)
188 *
189 * Makes a normal ListBox into a DragList by subclassing it.
190 *
191 * RETURNS
192 * Success: Non-zero
193 * Failure: Zero
194 */
195 BOOL WINAPI MakeDragList (HWND hwndLB)
196 {
197 DRAGLISTDATA *data = Alloc(sizeof(DRAGLISTDATA));
198
199 TRACE("(%p)\n", hwndLB);
200
201 if (!uDragListMessage)
202 uDragListMessage = RegisterWindowMessageW(DRAGLISTMSGSTRINGW);
203
204 return SetWindowSubclass(hwndLB, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID, (DWORD_PTR)data);
205 }
206
207 /***********************************************************************
208 * DrawInsert (COMCTL32.15)
209 *
210 * Draws insert arrow by the side of the ListBox item in the parent window.
211 *
212 * RETURNS
213 * Nothing.
214 */
215 VOID WINAPI DrawInsert (HWND hwndParent, HWND hwndLB, INT nItem)
216 {
217 RECT rcItem, rcListBox, rcDragIcon;
218 HDC hdc;
219 DRAGLISTDATA * data;
220
221 TRACE("(%p %p %d)\n", hwndParent, hwndLB, nItem);
222
223 if (!hDragArrow)
224 hDragArrow = LoadIconW(COMCTL32_hModule, (LPCWSTR)IDI_DRAGARROW);
225
226 if (LB_ERR == SendMessageW(hwndLB, LB_GETITEMRECT, nItem, (LPARAM)&rcItem))
227 return;
228
229 if (!GetWindowRect(hwndLB, &rcListBox))
230 return;
231
232 /* convert item rect to parent co-ordinates */
233 if (!MapWindowPoints(hwndLB, hwndParent, (LPPOINT)&rcItem, 2))
234 return;
235
236 /* convert list box rect to parent co-ordinates */
237 if (!MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rcListBox, 2))
238 return;
239
240 rcDragIcon.left = rcListBox.left - DRAGICON_HOTSPOT_X;
241 rcDragIcon.top = rcItem.top - DRAGICON_HOTSPOT_Y;
242 rcDragIcon.right = rcListBox.left;
243 rcDragIcon.bottom = rcDragIcon.top + DRAGICON_HEIGHT;
244
245 if (!GetWindowSubclass(hwndLB, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID, (DWORD_PTR*)&data))
246 return;
247
248 if (nItem < 0)
249 SetRectEmpty(&rcDragIcon);
250
251 /* prevent flicker by only redrawing when necessary */
252 if (!EqualRect(&rcDragIcon, &data->last_drag_icon_rect))
253 {
254 /* get rid of any previous inserts drawn */
255 RedrawWindow(hwndParent, &data->last_drag_icon_rect, NULL,
256 RDW_INTERNALPAINT | RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW);
257
258 data->last_drag_icon_rect = rcDragIcon;
259
260 if (nItem >= 0)
261 {
262 hdc = GetDC(hwndParent);
263
264 DrawIcon(hdc, rcDragIcon.left, rcDragIcon.top, hDragArrow);
265
266 ReleaseDC(hwndParent, hdc);
267 }
268 }
269 }
270
271 /***********************************************************************
272 * LBItemFromPt (COMCTL32.14)
273 *
274 * Gets the index of the ListBox item under the specified point,
275 * scrolling if bAutoScroll is TRUE and pt is outside of the ListBox.
276 *
277 * RETURNS
278 * The ListBox item ID if pt is over a list item or -1 otherwise.
279 */
280 INT WINAPI LBItemFromPt (HWND hwndLB, POINT pt, BOOL bAutoScroll)
281 {
282 RECT rcClient;
283 INT nIndex;
284 DWORD dwScrollTime;
285
286 TRACE("(%p %d x %d %s)\n",
287 hwndLB, pt.x, pt.y, bAutoScroll ? "TRUE" : "FALSE");
288
289 ScreenToClient (hwndLB, &pt);
290 GetClientRect (hwndLB, &rcClient);
291 nIndex = (INT)SendMessageW (hwndLB, LB_GETTOPINDEX, 0, 0);
292
293 if (PtInRect (&rcClient, pt))
294 {
295 /* point is inside -- get the item index */
296 while (TRUE)
297 {
298 if (SendMessageW (hwndLB, LB_GETITEMRECT, nIndex, (LPARAM)&rcClient) == LB_ERR)
299 return -1;
300
301 if (PtInRect (&rcClient, pt))
302 return nIndex;
303
304 nIndex++;
305 }
306 }
307 else
308 {
309 /* point is outside */
310 if (!bAutoScroll)
311 return -1;
312
313 if ((pt.x > rcClient.right) || (pt.x < rcClient.left))
314 return -1;
315
316 if (pt.y < 0)
317 nIndex--;
318 else
319 nIndex++;
320
321 dwScrollTime = GetTickCount ();
322
323 if ((dwScrollTime - dwLastScrollTime) < DRAGLIST_SCROLLPERIOD)
324 return -1;
325
326 dwLastScrollTime = dwScrollTime;
327
328 SendMessageW (hwndLB, LB_SETTOPINDEX, nIndex, 0);
329 }
330
331 return -1;
332 }