-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathclass_DragDrop.ahk
267 lines (227 loc) · 8.09 KB
/
class_DragDrop.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
*************************************************************************************
***************************** Drag n Drop Like a Boss *****************************
*************************************************************************************
This class supports Drag n Drop from EXPLORER windows to AutoHotKey GUIs -- that's it.
I recommend having your GUI still retain the label for GUIDropFiles for 2 reasons:
1. Win7 and earlier natively support DnD. It's better to NOT use this class when you don't need to.
2. The mouse becomes a No/X icon when you try to drag & drop onto the AutoHotKey GUI
In this case, drag & drop *does* still work, but that's unintuitive to the user.
Highlights:
This uses a global mouse hook. This isn't common, but if you have one set, there may be performance issues.
DragDrop.ShouldUseDD() -- call this to determine whether or not you need to use this class.
This class std lib compliant.
How to use:
_DragDrop() ; Init stb lib.
if (DragDrop.ShouldUseDD())
g_vDD := new DragDrop("SimulateDragNDrop", g_hQL)
...when you're done with the object...
g_vDD := ; Delete this object to unregister it from the super class and release it from the mouse hook.
Credits:
Joshua A. Kinnison for great Explorer functions.
License:
Attribution 3.0 -- just give Verdlin credit
*/
_DragDrop()
{ } ; for stb lib
class DragDrop
{
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: __New(0
Purpose: Initialize DragDrop object
Parameters
sCallback: String of callback function. Callback function takes a COM object of selected items from the dragged explorer window.
hDropWnd: Target hWnd. IMPORTANT: Must be a window handle! Also used to attach class to hWnd.
*/
__New(sCallback, hDropWnd)
{
this.m_hCallback := Func(sCallback)
if (!IsFunc(this.m_hCallback))
{
Msgbox 8256,, Error: %sCallback% is not a valid function.
return false
}
global g_hMouseHook
if (!g_hMouseHook) ; Only set hook once.
g_hMouseHook := DllCall("SetWindowsHookEx", "int", WH_MOUSE_LL:=14 , "uint", RegisterCallback("DD_MouseProc", "Fast"), "uint", 0, "uint", 0)
sPrev := A_DetectHiddenWindows ; Preserve existing setting.
DetectHiddenWindows, on ; So we can get this script's hWnd.
this.m_hScriptWnd := WinExist("Ahk_PID " DllCall("GetCurrentProcessId"))
DetectHiddenWindows, %sPrev% ; Restore existing setting.
; Override obscure window message.
OnMessage(WM_CAP_SET_CALLBACK_ERRORW:=1126, "DragDropProc")
; Register this instance in super class.
this.m_hDropWnd := hDropWnd
DragDrop.ForHwnd[hDropWnd] := &this ; for DD_MouseProc
return this
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: __Delete()
Purpose: Properly delete object
Parameters
*/
__Delete()
{
global g_hMouseHook
; Unregister instance from super class.
DragDrop.ForHwnd.Remove(this.m_hDropWnd)
; Apparently MaxIndex() doesn't work :\
for k, v in DragDrop.ForHwnd
iClassCnt++
; If that was the last DD, unhook
if (!iClassCnt)
DllCall("UnhookWindowsHookEx", "Uint", g_hMouseHook)
return
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: ShouldUseDD
Purpose: Help to conditionally use DragDrop
You can call using the super class instantiator like this: DragDrop.ShouldUseDD()
Parameters
*/
ShouldUseDD()
{
return A_OSVersion != "WIN_7" && A_OSVersion != "WIN_VISTA" && A_OSVersion != "WIN_XP"
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: GetExplorerDDContents
Purpose: To get the contents (if any) of explorer's DD.
Returns COM object of explorer contents or else false if empty.
Parameters
hExplorerWnd: Explorer wnd to get contents from
*/
GetExplorerDDContents(hExplorerWnd)
{
; This prevents the code from firing if you simply click on the QL.
vSelItems := this.Explorer_GetWindow(hExplorerWnd).Document.SelectedItems
if (vSelItems.Count < 1)
return false
return vSelItems
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Joshua A. Kinnison 2011-04-27, 16:12
Function: Explorer_GetWindow
Purpose: Get explorer window COM object
Parameters
hWnd=""
*/
Explorer_GetWindow(hwnd="")
{
hwnd := hwnd ? hwnd : WinExist("A")
WinGetClass class, ahk_id %hwnd%
if (class="CabinetWClass" or class="ExploreWClass" or class="Progman")
for window in ComObjCreate("Shell.Application").Windows
{
if (window.hwnd==hwnd)
return window
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: DragDropProc
Purpose: To simulate do Drag n Drop!
Parameters
hExplorerWnd: Explorer window to grab selection from
pDragDrop: Pointer to DragDrop class object.
*/
DragDropProc(hExplorerWnd, pDragDrop)
{
; Get the DD contents out of explorer now.
vDragDrop := Object(pDragDrop)
vDDContents := vDragDrop.GetExplorerDDContents(hExplorerWnd)
; Is there something to drag and drop?
if (vDDContents.Count)
vDragDrop.m_hCallback.(vDDContents) ; Do it!
return
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
Author: Verdlin
Function: DD_MouseProc
Purpose: Global mouse hook to detect click n drag and trigger Drag n Drop event.
Parameters
nCode:
wParam: Window message WM_
lParam:
*/
DD_MouseProc(nCode, wParam, lParam)
{
; Variables used to filter out clicks.
static s_hWndOnClick := 0, s_bTrackingClickNDrag := false, s_bIsDragging := false, s_iPrevMouseX, s_iPrevMouseY
static WM_MOUSEMOVE := 0x200
static WM_LBUTTONDOWN := 0x201
static WM_LBUTTONUP := 0x202
CoordMode, Mouse, Screen
MouseGetPos, iMouseX, iMouseY, hMouseWnd
if (wParam == WM_LBUTTONDOWN)
s_hWndOnClick := hMouseWnd
vDragDrop := Object(DragDrop.ForHwnd[hMouseWnd]) ; DragDrop is a super class or super global or something...it just works, OK?
bMouseOverDropWnd := IsObject(vDragDrop)
; If we started clicking ON the drop wnd, there's nothing to click n drag.
bDraggingDropWnd := (s_hWndOnClick == vDragDrop.m_hDropWnd)
if (bDraggingDropWnd)
return DD_CallNextHookEx(nCode, wParam, lParam)
bMouseMove := (wParam == WM_MOUSEMOVE)
bLButtonUp := (wParam == WM_LBUTTONUP)
bLButtonDown := (wParam == WM_LBUTTONDOWN)
bLButtonDown_P := GetKeyState("LButton", "P")
bLButtonDown_Valid := (bLButtonDown_P && !bMouseOverDropWnd)
; If the LButton is down or the mouse is moving AND click n drag did NOT start on a drop hwnd...
if (bLButtonDown_Valid && (bLButtonDown || bMouseMove))
{
; If we are clicking and dragging...
if (bLButtonDown_P && bMouseMove)
{
; X delta
if (s_iPrevMouseX == "")
iXDelta := 0
else iXDelta := abs(iMouseX-s_iPrevMouseX)
; Y delta
if (s_iPrevMouseY == "")
iYDelta := 0
else iYDelta := abs(iMouseY-s_iPrevMouseY)
; Detect if we are dragging uses delta.
s_bIsDragging := (s_bIsDragging || iXDelta || iYDelta)
}
s_bTrackingClickNDrag := s_bTrackingClickNDrag || s_bIsDragging
s_iPrevMouseX := iMouseX
s_iPrevMouseY := iMouseY
}
else if (bMouseOverDropWnd && s_bTrackingClickNDrag && s_bIsDragging && bLButtonUp)
{
s_bTrackingClickNDrag := false
s_bIsDragging := false
; PostMessage because we can't call GetExplorerDDContents while dispatching an input-synchronous call.
PostMessage, WM_CAP_SET_CALLBACK_ERRORW:=1126, s_hWndOnClick, &vDragDrop,, % "ahk_id" vDragDrop.m_hScriptWnd
}
else if (bLButtonUp)
{
; Reset click n drag vars.
s_bTrackingClickNDrag := s_bIsDragging := false
s_hWndOnClick := s_iPrevMouseX := s_iPrevMouseY := ""
}
return DD_CallNextHookEx(nCode, wParam, lParam)
}
DD_CallNextHookEx(nCode, wParam, lParam)
{
global g_hMouseHook
return DllCall("CallNextHookEx", "uint", g_hMouseHook, "int", nCode, "uint", wParam, "uint", lParam)
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;