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