offsets array is the size of the wine data format so there is no need
[wine.git] / dlls / comctl32 / draglist.c
blob26411e9ad98d75a135627c470fdfd9593f79378f
1 /*
2 * Drag List control
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
21 * NOTES
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.
32 #include <stdarg.h>
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"
43 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
45 /* for compiler compatibility we only accept literal ASCII strings */
46 #undef TEXT
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? */
62 BOOL dragging;
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 */
67 HCURSOR cursor;
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;
75 } DRAGLISTDATA;
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
86 * window message.
88 static LRESULT DragList_Notify(HWND hwndLB, UINT uNotification)
90 DRAGLISTINFO dli;
91 dli.hWnd = hwndLB;
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);
101 ReleaseCapture();
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;
117 switch (uMsg)
119 case WM_LBUTTONDOWN:
120 SetFocus(hwnd);
121 data->cursor = NULL;
122 SetRectEmpty(&data->last_drag_icon_rect);
123 data->dragging = DragList_Notify(hwnd, DL_BEGINDRAG);
124 if (data->dragging)
126 SetCapture(hwnd);
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) */
131 break;
133 case WM_KEYDOWN:
134 case WM_RBUTTONDOWN:
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);
143 return 0;
145 break;
147 case WM_MOUSEMOVE:
148 case WM_TIMER:
149 if (data->dragging)
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)
156 switch (cursor)
158 case DL_STOPCURSOR:
159 data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_NO);
160 SetCursor(data->cursor);
161 break;
162 case DL_COPYCURSOR:
163 data->cursor = LoadCursorW(COMCTL32_hModule, (LPCWSTR)IDC_COPY);
164 SetCursor(data->cursor);
165 break;
166 case DL_MOVECURSOR:
167 data->cursor = LoadCursorW(NULL, (LPCWSTR)IDC_ARROW);
168 SetCursor(data->cursor);
169 break;
171 data->last_dragging_response = cursor;
173 /* don't pass this message on to List Box */
174 return 0;
176 break;
178 case WM_LBUTTONUP:
179 if (data->dragging)
181 DragList_EndDrag(hwnd, data);
182 DragList_Notify(hwnd, DL_DROPPED);
184 break;
186 case WM_SETCURSOR:
187 /* if app has told us to set a cursor then do so */
188 if (data->dragging && data->cursor)
190 SetCursor(data->cursor);
191 return TRUE;
193 break;
195 case WM_GETDLGCODE:
196 /* tell dialog boxes that we want to receive WM_KEYDOWN events
197 * for keys like VK_ESCAPE */
198 if (data->dragging)
199 return DLGC_WANTALLKEYS;
200 break;
201 case WM_NCDESTROY:
202 RemoveWindowSubclass(hwnd, DragList_SubclassWindowProc, DRAGLIST_SUBCLASSID);
203 Free(data);
204 break;
206 return DefSubclassProc(hwnd, uMsg, wParam, lParam);
209 /***********************************************************************
210 * MakeDragList (COMCTL32.13)
212 * Makes a normal ListBox into a DragList by subclassing it.
214 * RETURNS
215 * Success: Non-zero
216 * Failure: Zero
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.
235 * RETURNS
236 * Nothing.
238 VOID WINAPI DrawInsert (HWND hwndParent, HWND hwndLB, INT nItem)
240 RECT rcItem, rcListBox, rcDragIcon;
241 HDC hdc;
242 DRAGLISTDATA * data;
244 TRACE("(%p %p %d)\n", hwndParent, hwndLB, nItem);
246 if (!hDragArrow)
247 hDragArrow = LoadIconW(COMCTL32_hModule, (LPCWSTR)IDI_DRAGARROW);
249 if (LB_ERR == SendMessageW(hwndLB, LB_GETITEMRECT, nItem, (LPARAM)&rcItem))
250 return;
252 if (!GetWindowRect(hwndLB, &rcListBox))
253 return;
255 /* convert item rect to parent co-ordinates */
256 if (!MapWindowPoints(hwndLB, hwndParent, (LPPOINT)&rcItem, 2))
257 return;
259 /* convert list box rect to parent co-ordinates */
260 if (!MapWindowPoints(HWND_DESKTOP, hwndParent, (LPPOINT)&rcListBox, 2))
261 return;
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))
269 return;
271 if (nItem < 0)
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);
283 if (nItem >= 0)
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.
300 * RETURNS
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)
305 RECT rcClient;
306 INT nIndex;
307 DWORD dwScrollTime;
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 */
319 while (TRUE)
321 if (SendMessageA (hwndLB, LB_GETITEMRECT, nIndex, (LPARAM)&rcClient) == LB_ERR)
322 return -1;
324 if (PtInRect (&rcClient, pt))
325 return nIndex;
327 nIndex++;
330 else
332 /* point is outside */
333 if (!bAutoScroll)
334 return -1;
336 if ((pt.x > rcClient.right) || (pt.x < rcClient.left))
337 return -1;
339 if (pt.y < 0)
340 nIndex--;
341 else
342 nIndex++;
344 dwScrollTime = GetTickCount ();
346 if ((dwScrollTime - dwLastScrollTime) < DRAGLIST_SCROLLPERIOD)
347 return -1;
349 dwLastScrollTime = dwScrollTime;
351 SendMessageA (hwndLB, LB_SETTOPINDEX, (WPARAM)nIndex, 0);
354 return -1;