That is much better.
There seems to have been a change that has crept in, in that the filetype is now JPG it was jpg
Also the default datestamp, has Minutes omitted
It may also need an error trap.
If you create a second event but don't change anything from the default it will throw an Invalid Option. This then cause a problem when you try to return to the config with a further error Invalid Gui name, and again on exiting.
I can use it with Firefox for what I need, but will look at IE11 again and the security issue when I get a chance
-magician62
Not sure about this one, I'm able to download .jpg and they stay .jpg, can you check the actual image on the website and verify it isn't named .JPG?
I added some error checking, and fixed the cause of the error popup.
I've changed the default dateFormat, the original post didn't specify minutes. => 'PREFIX YYYY-MM-DD_HH-SS SUFFIX'
Reminder: you'll have to update the timeFormat for all your created dropZone.
; -------------------------------------------------- Auto Execute --------------------------------------------------
#Persistent
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory.
#SingleInstance force
SetBatchLines, -1
SetKeyDelay, -1
title := "Web Images Drop Zone"
iniPath := A_ScriptDir . "\WIDZ.data"
Menu, Tray, NoStandard
Menu, Tray, Add, &Config, Show
Menu, Tray, Default, &Config
Menu, Tray, Add, &Toggle DropZones, toggleDropZones
Menu, Tray, Add, E&xit, Exit
Menu, Tray, Click, 1
Menu, Tray, Tip, %title%
LoadSettings(iniPath)
if(firstTimeOpenShowConfig)
{
firstTimeOpenShowConfig := false
gosub, show
WinWaitClose, %title% - Config
}
dropZoneGuis := CreateDropZoneGuis(iniPath)
OnExit("Exit")
return
; -------------------------------------------------- Labels --------------------------------------------------
createOrUpdateDropZone:
; Get dropZone info info
GuiControlGet, dropZone,, dropZone
GuiControlGet, timeFormat,, timeFormat
GuiControlGet, fileDrop,, fileDrop
GuiControlGet, icon,, icon
GuiControlGet, pos, %pos%
GuiControlGet, prefix,, prefix
GuiControlGet, suffix,, suffix
; Error handling
response := ValidateFields(dropZone, prefix, suffix, pos, fileDrop, icon, timeFormat)
if(response != "")
{
msgbox invalid field(s): %response%
return
}
; Check if the dropZone already exists
Loop % LV_GetCount()
{
Lv_GetText(dropZoneName, A_index)
if(dropZoneName = dropZone)
{
LV_Modify(A_Index,,dropZone, prefix, suffix, pos, fileDrop, icon, timeFormat)
return
}
}
LV_Add(,dropZone, prefix, suffix, pos, fileDrop, icon, timeFormat)
return
drawDropWindow:
;http://www.autohotkey.com/board/topic/111196-drawing-boxes-on-screen-need-help-finishing/?p=655416
GuID := "dropzone"
LetUserSelectRect(GuiID, x1, y1, x2, y2)
lastDropWindowPos := x1 . ":" . x2 . ":" . y1 . ":" . y2
GuiControl,, pos, %lastDropWindowPos%
Loop 4
Gui, % GuiID A_Index ": Destroy"
return
dropZoneList:
; populate fields on a double click
if(A_GuiEvent = "Normal")
CopyDropZoneToForm(A_EventInfo)
else if(A_GuiEvent = "RightClick")
DeleteDropZoneData(A_EventInfo)
return
Exit:
IfWinExist, %title% - Config
SaveData(iniPath)
ExitApp
return
fileDrop:
FileSelectFolder, fileDrop,,, Select file folder
if(fileDrop)
GuiControl, , fileDrop, %fileDrop%
else
msgbox your folder wasn't found please select a valid folder, i.e. no the virtual ones like Libraries/Documents/Music/etc
return
icon:
FileSelectFile, icon, 3, %A_MyDocuments%,Choose an Icon, Image files (*.jpg; *.jpeg; *.gif; *.png)
GuiControl, , icon, %icon%
return
show:
DeleteAllDropZones(dropZoneGuis)
Gui, WebImageDropZoneConfig: New, hwndWIDZ, Web Image Drop Zone
global WIDZ
Gui, Add, ListView, x12 y10 w450 r15 gdropZoneList AltSubmit, DropZone|Prefix|Suffix|Pos|Save Path|Icon|TimeFormat
Gui, Add, Text, x12 y300 w60 h20 , DropZone:
Gui, Add, Edit, x72 y300 w90 h20 vdropZone , dropzone
Gui, Add, Text, x172 y300 w60 h20 , TimeFormat:
Gui, Add, Edit, x232 y300 w230 h20 vtimeFormat , yyyy-MM-dd_hh-mm-ss
Gui, font, s11 bold, MS sans serif
Gui, Add, Link, x462 y300 w10 h20 , <a href="http://www.autohotkey.com/docs/commands/FormatTime.htm">?</a>
gui, font
Gui, Add, Text, x12 y330 w50 h20 , File Drop:
Gui, Add, Edit, x72 y330 w320 h20 vfileDrop , file drop
Gui, Add, Button, x392 y330 w70 h20 gfileDrop , Browse
Gui, Add, Text, x12 y360 w40 h20 , Icon:
Gui, Add, Edit, x72 y360 w320 h20 vicon, Edit
Gui, Add, Button, x392 y360 w70 h20 gicon , Browse
Gui, Add, Text, x12 y390 w40 h20 , Prefix:
Gui, Add, Edit, x72 y390 w110 h20 vprefix , prefix
Gui, Add, Text, x192 y390 w40 h20 , Suffix:
Gui, Add, Edit, x232 y390 w110 h20 vsuffix , suffix
Gui, Add, Button, x12 y420 w110 h20 gdrawDropWindow, DropZone Position
Gui, Add, Edit, x132 y420 w210 h20 ReadOnly vpos , 0:100:0:100
Gui, Add, Button, x352 y390 w110 h50 gcreateOrUpdateDropZone , Create/Update
; Generated using SmartGUI Creator 4.0
Gui, Show, x138 y90 h450 w479, %title% - Config
PopulateDropZoneList(iniPath)
OnMessage(0x200, "GuiToolTip")
Loop 6
LV_ModifyCol(A_Index, "AutoHdr")
return
toggleDropZones:
for index, array in dropZoneGuis
{
hwnd := array["hwnd"]
if WinExist("ahk_id " . hwnd)
gui, %hwnd%:Hide
else
gui, %hwnd%:Show
}
return
WebImageDropZoneConfigGuiClose:
WebImageDropZoneConfigGuiEscape:
IfWinExist, %title% - Config
SaveData(iniPath)
Gui, WebImageDropZoneConfig:Destroy
; Update the dropzones
dropZoneGuis := CreateDropZoneGuis(iniPath)
return
; -------------------------------------------------- Methods --------------------------------------------------
CopyDropZoneToForm(eventInfo)
{
global lastDropWindowPos
; get row text
LV_GetText(dropZone, eventInfo, 1)
LV_GetText(prefix, eventInfo, 2)
LV_GetText(suffix, eventInfo, 3)
LV_GetText(pos, eventInfo, 4)
LV_GetText(fileDrop, eventInfo, 5)
LV_GetText(icon, eventInfo, 6)
LV_GetText(TimeFormat, eventInfo, 7)
; update fields
GuiControl,, dropZone, %dropZone%
GuiControl,, prefix, %prefix%
GuiControl,, suffix, %suffix%
GuiControl,, pos, %pos%
lastDropWindowPos := pos
GuiControl,, fileDrop, %fileDrop%
GuiControl,, icon, %icon%
GuiControl,, TimeFormat, %TimeFormat%
}
CreateDropZoneGuis(path)
{
dropZoneGuis := Array()
dropZoneData := LoadData(path)
for index, array in dropZoneData
{
extractedPos := StrSplit(array["pos"], ":")
guiHwnd := DropZoneGui(array["dropZone"], array["icon"], extractedPos[1], extractedPos[2], extractedPos[3], extractedPos[4])
IDT_WIDZ := IDropTarget_Create(guiHwnd, "_WIDZ", [1, 13, 15]) ; CF_TEXT, CF_UNICODETEXT, CF_HDROP
dropZoneGuis.insert({dropZone: array["dropZone"], hwnd: guiHwnd, iDropTarget: IDT_WIDZ})
}
return dropZoneGuis
}
; Custom message box for the confirm dropzone delete.
Custom_MsgBox(msg_text)
{
global showCancelConfirm, dontShowAgain
Gui, CustomMsgBox: New, hwndCMB
Gui, Add, Text, x12 y10 w430 h70, %msg_text%
Gui, Add, CheckBox, x12 y90 w140 h30 vdontShowAgain , Don't show this again?
Gui, Add, Button, x232 y90 w100 h30 gCustomMsgBoxGuiSubmit , OK
Gui, Add, Button, x342 y90 w100 h30 gCustomMsgBoxGuiSubmit Default , Cancel
; Generated using SmartGUI Creator 4.0
Gui, Show
WinWaitClose, ahk_id %CMB%
return buttonClicked
CustomMsgBoxGuiSubmit:
GuiControlGet, dontShowAgainChecked,, dontShowAgain
if(dontShowAgainChecked)
showCancelConfirm := false
buttonClicked := A_GuiControl
gosub, CustomMsgBoxGuiClose
return
CustomMsgBoxGuiClose:
CustomMsgBoxGuiEscape:
Gui, CustomMsgBox:Destroy
return
}
DeleteDropZoneData(eventInfo)
{
global showCancelConfirm
Gui, WebImageDropZoneConfig:Default
LV_GetText(thisDropZone, eventInfo)
if(showCancelConfirm)
{
if(Custom_MsgBox("Are you sure you want to delete the '" . thisDropZone . "' drop zone?") = "OK")
{
Gui, WebImageDropZoneConfig:Default
LV_Delete(eventInfo)
}
}
else
LV_Delete(eventInfo)
}
DeleteAllDropZones(dropZoneGuis)
{
; dropZone, hwnd, iDropTarget
for index, array in dropZoneGuis
{
;~ Revoke the registration of the ListView as a potential target for OLE drag-and-drop operations.
array["iDropTarget"].RevokeDragDrop()
hwnd := array["hwnd"]
if(WinExist("ahk_id " . hwnd))
Gui, %hwnd%:Destroy
}
}
determineRealURL(url)
{
origUrl := url
if(InStr(url, "google.com"))
RegExMatch(url, "(?<=\?imgurl=).*?(?=&)", url)
;~ msgbox % "url: " url "`n`norigUrl: " origUrl
return url
}
downloadFile(origUrl, foundUrl, dropZoneInfo, targetHwnd)
{
SplitPath, foundUrl,,, imageFileExt
fileDropDir := dropZoneInfo["fileDrop"]
prefix := dropZoneInfo["prefix"]
suffix := dropZoneInfo["suffix"]
timeFormat := dropZoneInfo["timeFormat"]
FormatTime, nowTime,, % timeFormat
; Per request removed the fileName check
;~ Inputbox, fileName, File Name Entry, Please Enter a file name. (no extension),,,,,,,, %prefix% %nowTime% %suffix%
;~ if(fileName = "")
fileName := prefix . " " . nowTime . " " . suffix
fileName := Trim(fileName)
; Only try to download the file if its a jpg, otherwise create a link
if(RegExMatch(foundUrl, "\.\w{3,4}$"))
{
try
{
saveLocation := fileDropDir . "\" . fileName . "." . imageFileExt
URLDownloadToFile, %foundUrl%, %saveLocation%
if(ErrorLevel)
return "Unable to download '" . foundUrl . "' parsed from '" . origUrl . "'" . "`n`nfileDropDir: " . fileDropDir . "`nfileName: " . fileName . "`next: " . imageFileExt
NotificationPopup(saveLocation, targetHwnd)
return true
}
catch, e
{
msgbox % "ErrorLevel: " ErrorLevel "`nA_LastError: " A_LastError "`nmessage: " e.message "`nWhat: " e.what "`nExtra: " e.extra "`nLine: " e.line
return "Unable to download '" . foundUrl . "' parsed from '" . origUrl . "'" . "`n`nfileDropDir: " . fileDropDir . "`nfileName: " . fileName . "`next: " . imageFileExt
}
}
else
{
FileCreateShortcut, %foundUrl%, %fileDropDir%\%fileName%.lnk
TrayTip, Link Created, Couldn't find image so a link was created instead., 15
return true
}
}
DropFile(dropZoneGuis, targetHwnd, url)
{
dropZoneInfo := findDropZoneInfo(dropZoneGuis, targetHwnd)
foundUrl := determineRealURL(url)
return downloadFile(url, foundUrl, dropZoneInfo, targetHwnd)
}
; Called to create/display dropZone gui
DropZoneGui(label, icon, x1, x2, y1, y2)
{
static index = 0
index += 1
; If no pos data was provided
if(!x1)
{
x1 := 0
x2 := 100
y1 := 0
y2 := 100
}
width := x2 - x1
height := y2 - y1
labelWidth := StrLen(label) * 25
labelHeight := 32
; If the label is too long we set the labelsPerRow to 1 otherwise nothing would be displayed
labelsPerColumn := height / labelHeight < 1 ? 1 : height / labelHeight
labelsPerRow := width / labelWidth < 1 ? 1 : width / labelWidth
CustomColor = EEAA99 ; Can be any RGB color (it will be made transparent below).
Gui, New, hwndDZG%index% +LastFound +AlwaysOnTop -Caption +ToolWindow ; +ToolWindow avoids a taskbar button and an alt-tab menu item.
Gui, Color, %CustomColor%
; If an icon was provided make it the size of the gui else use the label text
if(icon && FileExist(icon))
Gui, Add, Picture, w%width% h%height%, %icon%
else
{
Gui, Font, s32 ; Set a large font size (32-point).
x := 0
Loop %labelsPerRow%
{
y := 0
Loop %labelsPerColumn%
{
Gui, Add, Text, % "x" x " y" y " cLime", %label%
y += 50
}
x += labelWidth
}
}
; Make all pixels of this color transparent and make the text itself translucent (150):
WinSet, TransColor, %CustomColor% 150
Gui, Show, x%x1% y%y1% w%width% h%height% NoActivate ; NoActivate avoids deactivating the currently active window.
return DZG%index%
}
Exit(ExitReason, ExitCode)
{
global dropZoneGuis, iniPath
IfWinExist, %title% - Config
SaveData(iniPath)
DeleteAllDropZones(dropZoneGuis)
ExitApp
}
findDropZoneInfo(dropZoneGuis, targetHwnd)
{
global iniPath
data := LoadData(iniPath)
for index, array in dropZoneGuis
if(array["hwnd"] = targetHwnd)
for dindex, darray in data
if(darray["dropZone"] = array["dropZone"])
return darray
}
GuiToolTip(wParam, lParam, Msg)
{
MouseGetPos,,,mhwnd, OutputVarControl
if(OutputVarControl = "Button3" && mhwnd = WIDZ)
Help := "After pressing 'Dropzone Position' move your mouse to the desired location; hold down the mouse button then move the mouse to select your area."
else
Help := ""
ToolTip % Help
}
LetUserSelectRect(ByRef ID, ByRef X1, ByRef Y1, ByRef X2, ByRef Y2)
{ ;http://www.autohotkey.com/board/topic/111196-drawing-boxes-on-screen-need-help-finishing/?p=655416
CoordMode, Mouse ; Required: change coord mode to screen vs relative.
static r := 3
; Create the "selection rectangle" GUIs (one for each edge).
Loop 4 {
Gui, % ID A_Index ": -Caption +ToolWindow +AlwaysOnTop"
Gui, % ID A_Index ": Color", Red
}
; Disable LButton.
Hotkey, *LButton, lusr_return, On
; Wait for user to press LButton.
KeyWait, LButton, D
; Get initial coordinates.
MouseGetPos, xorigin, yorigin
; Set timer for updating the selection rectangle.
SetTimer, lusr_update, 10
; Wait for user to release LButton.
KeyWait, LButton
; Re-enable LButton.
Hotkey, *LButton, Off
; Disable timer.
SetTimer, lusr_update, Off
return
lusr_update:
CoordMode, Mouse ; Required: change coord mode to screen vs relative.
MouseGetPos, x, y
if (x = xlast && y = ylast)
; Mouse hasn't moved so there's nothing to do.
return
if (x < xorigin)
x1 := x, x2 := xorigin
else x2 := x, x1 := xorigin
if (y < yorigin)
y1 := y, y2 := yorigin
else y2 := y, y1 := yorigin
; Update the "selection rectangle".
Gui, % ID "1:Show", % "NA X" x1 " Y" y1 " W" x2-x1 " H" r
Gui, % ID "2:Show", % "NA X" x1 " Y" y2-r " W" x2-x1 " H" r
Gui, % ID "3:Show", % "NA X" x1 " Y" y1 " W" r " H" y2-y1
Gui, % ID "4:Show", % "NA X" x2-r " Y" y1 " W" r " H" y2-y1
lusr_return:
return
}
;~ LV_Add(, array[1], array[2], array[3], array[4], array[5], array[6], array[7])
LoadData(path)
{
FileRead, data, %path%
dataArray := Object()
Loop, Parse, data, `n, `r`n
{
; If not the setttings row
if(A_Index != 1)
{
array := StrSplit(A_LoopField, "|")
if(array[1])
{
dataArray.Insert({dropZone: array[1], prefix: array[2], suffix: array[3], pos: array[4], fileDrop: array[5], icon: array[6], timeFormat: array[7]})
}
}
}
return dataArray
}
LoadSettings(path)
{
global showCancelConfirm, firstTimeOpenShowConfig
FileReadLine, data, %path%, 1
array := StrSplit(data, "|")
firstTimeOpenShowConfig := array[1] = 0 ? false : true
showCancelConfirm := array[2] = 0 ? false : true
}
NotificationPopup(saveLocation, targetHwnd)
{
static index = 0
static hwnd := Array()
index += 1
Gui, New, hwndNewConformation%index% +LastFound +AlwaysOnTop -Caption +ToolWindow
Gui, Add, Text,, Success! - %saveLocation%
WinGetPos, xpos, ypos, w, h, ahk_id %targetHwnd%
Gui, Show, NA x%xpos% y%ypos%
hwnd.Push(NewConformation%index%)
SetTimer, deleteNotification, 3000
return
deleteNotification:
deleteHwnd := hwnd.RemoveAt(1)
if(deleteHwnd)
Gui, %deleteHwnd%:Destroy
if(hwnd.Length() < 1)
SetTimer, deleteNotification, off
return
}
PopulateDropZoneList(path)
{
data := LoadData(path)
for index, array in data
LV_Add(, array["dropZone"], array["prefix"], array["suffix"], array["pos"], array["fileDrop"], array["icon"], array["timeFormat"])
}
SaveData(path)
{
global showCancelConfirm, firstTimeOpenShowConfig
FileCopy, %path%, %path%.bak
FileDelete, %path%
data := ""
; Save the setttings
data := firstTimeOpenShowConfig . "|" . showCancelConfirm . "`n"
FileAppend, %data%, %path%
; Save the list data
Loop, % LV_GetCount()
{
rowData := RowText(A_Index)
data := rowData[1] . "|" . rowData[2] . "|" . rowData[3] . "|" . rowData[4] . "|" . rowData[5] . "|" . rowData[6] . "|" . rowData[7] . "`n"
FileAppend, %data%, %path%
}
FileDelete, %path%.bak
}
RowText(rowNumber)
{
LV_GetText(dropZone, rowNumber, 1)
LV_GetText(prefix, rowNumber, 2)
LV_GetText(suffix, rowNumber, 3)
LV_GetText(pos, rowNumber, 4)
LV_GetText(location, rowNumber, 5)
LV_GetText(icon, rowNumber, 6)
LV_GetText(TimeFormat, rowNumber, 7)
return [dropZone, prefix, suffix, pos, location, icon, TimeFormat]
}
ValidateFields(dropZone, prefix, suffix, lastDropWindowPos, fileDrop, icon, timeFormat)
{
reason := ""
; Make sure file locations exist
if(!FileExist(fileDrop))
reason .= "fileDrop, "
if(!FileExist(icon))
reason .= "icon, "
; Make sure dropzone is not blank
if(dropZone = "")
reason .= "dropZone, "
if(lastDropWindowPos = "")
reason .= "DropZone_Position, "
if(timeFormat = "")
reason .= "timeFormat"
return reason
}
IDropTargetOnDrop_WIDZ(TargetObject, pDataObj, KeyState, X, Y, DropEffect)
{
Static CF_NATIVE := A_IsUnicode ? 13 : 1 ; CF_UNICODETEXT : CF_TEXT
global dropZoneGuis
; if valid
If (pEnumObj := IDataObject_EnumFormatEtc(pDataObj))
{
; Loop through structure
While IEnumFORMATETC_Next(pEnumObj, FORMATETC)
{
; Populate variables with current item data
IDataObject_ReadFormatEtc(FORMATETC, Format, Device, Aspect, Index, Type)
; We only want the text since it will have the shortcut url so continue looping until it shows up
if(Format != CF_NATIVE)
continue
else
{
IDataObject_GetData(pDataObj, FORMATETC, Size, Data)
url := StrGet(&Data)
response := DropFile(dropZoneGuis, TargetObject.hwnd, url)
if(response != true)
msgbox % response
break
}
}
}
}
; ************************************************ All the code below is from https://github.com/AHK-just-me and wasn't written by me
; from https://github.com/AHK-just-me/DoDragDrop/blob/master/sources/IDropTarget.ahk
; ==================================================================================================================================
; IDropTarget interface -> msdn.microsoft.com/en-us/library/ms679679(v=vs.85).aspx
; Requires: IDataObject.ahk
; ==================================================================================================================================
IDropTarget_Create(HWND, UserFuncSuffix, RequiredFormats := "", Register := True, UseHelper := True) {
Return New IDropTarget(HWND, UserFuncSuffix, RequiredFormats, Register, UseHelper)
}
; ==================================================================================================================================
Class IDropTarget {
__New(HWND, UserFuncSuffix, RequiredFormats := "", Register := True, UseHelper := True) {
Static Methods := ["QueryInterface", "AddRef", "Release", "DragEnter", "DragOver", "DragLeave", "Drop"]
Static Params := (A_PtrSize = 8 ? [3, 1, 1, 5, 4, 1, 5] : [3, 1, 1, 6, 5, 1, 6])
Static DefaultFormat := 15 ; CF_HDROP
Static DropFunc := "IDropTargetOnDrop"
Static EnterFunc := "IDropTargetOnEnter"
Static OverFunc := "IDropTargetOnOver"
Static LeaveFunc := "IDropTargetOnLeave"
Static CLSID_IDTH := "{4657278A-411B-11D2-839A-00C04FD918D0}" ; CLSID_DragDropHelper
Static IID_IDTH := "{4657278B-411B-11D2-839A-00C04FD918D0}" ; IID_IDropTargetHelper
If This.Base.HasKey("Ptr")
Return False
UserFunc := DropFunc . UserFuncSuffix
If !IsFunc(UserFunc) || (Func(UserFunc).MinParams < 6)
Return False
This.DropUserFunc := Func(UserFunc)
UserFunc := EnterFunc . UserFuncSuffix
If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 5))
This.EnterUserFunc := Func(UserFunc)
UserFunc := OverFunc . UserFuncSuffix
If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 4))
This.OverUserFunc := Func(UserFunc)
UserFunc := LeaveFunc . UserFuncSuffix
If (IsFunc(UserFunc) && (Func(UserFunc).MinParams > 0))
This.LeaveUserFunc := Func(UserFunc)
This.HWND := HWND
This.Registered := False
If IsObject(RequiredFormats)
This.Required := RequiredFormats
Else
This.Required := [DefaultFormat]
This.PreferredDropEffect := 0
SizeOfVTBL := (Methods.Length() + 2) * A_PtrSize
This.SetCapacity("VTBL", SizeOfVTBL)
This.Ptr := This.GetAddress("VTBL")
DllCall("RtlZeroMemory", "Ptr", This.Ptr, "UInt", SizeOfVTBL)
NumPut(This.Ptr + A_PtrSize, This.Ptr + 0, "UPtr")
For Index, Method In Methods {
CB := RegisterCallback("IDropTarget." . Method, "", Params[Index], &This)
NumPut(CB, This.Ptr + 0, A_Index * A_PtrSize, "UPtr")
}
This.Helper := ComObjCreate(CLSID_IDTH, IID_IDTH)
If (Register)
If !This.RegisterDragDrop()
Return False
}
; -------------------------------------------------------------------------------------------------------------------------------
; Registers window/control as a drop target.
; -------------------------------------------------------------------------------------------------------------------------------
RegisterDragDrop() {
If !(This.Registered)
If DllCall("Ole32.dll\RegisterDragDrop", "Ptr", This.HWND, "Ptr", This.Ptr, "Int")
Return False
Return (This.Registered := True)
}
; -------------------------------------------------------------------------------------------------------------------------------
; Revokes registering of the window/control as a drop target.
; This method should be called before the window/control will be destroyed.
; -------------------------------------------------------------------------------------------------------------------------------
RevokeDragDrop() {
If (This.Registered)
DllCall("Ole32.dll\RevokeDragDrop", "Ptr", This.HWND)
Return !(This.Registered := False)
}
; -------------------------------------------------------------------------------------------------------------------------------
; Notifies the drag-image manager, if used, to show or hide the drag image.
; Parameter:
; Show - If true, the drag image will be shown; otherwise it will be hidden.
; -------------------------------------------------------------------------------------------------------------------------------
HelperShow(Show := True) {
Static HelperShow := A_PtrSize * 7
If (This.Helper) {
pVTBL := NumGet(This.Helper + 0, "UPtr")
, DllCall(NumGet(pVTBL + HelperShow, "UPtr"), "Ptr", This.Helper, "UInt", !!Show)
Return True
}
Return False
}
; ===============================================================================================================================
; The following methods must not be called directly, they are reserved for internal and system use.
; ===============================================================================================================================
__Delete() {
This.RevokeDragDrop()
While (CB := NumGet(This.Ptr + (A_PtrSize * A_Index), "Ptr"))
DllCall("GlobalFree", "Ptr", CB)
If (This.Helper)
ObjRelease(This.Helper)
}
; -------------------------------------------------------------------------------------------------------------------------------
QueryInterface(RIID, PPV) {
; IUnknown -> msdn.microsoft.com/en-us/library/ms682521(v=vs.85).aspx
Static IID := "{00000122-0000-0000-C000-000000000046}"
VarSetCapacity(QID, 80, 0)
QIDLen := DllCall("Ole32.dll\StringFromGUID2", "Ptr", RIID, "Ptr", &QID, "Int", 40, "Int")
If (StrGet(&QID, QIDLen, "UTF-16") = IID) {
NumPut(This, PPV + 0, "Ptr")
Return 0 ; S_OK
}
Else {
NumPut(0, PPV + 0, "Ptr")
Return 0x80004002 ; E_NOINTERFACE
}
}
; -------------------------------------------------------------------------------------------------------------------------------
AddRef() {
; IUnknown -> msdn.microsoft.com/en-us/library/ms691379(v=vs.85).aspx
; Reference counting is not needed in this case.
Return 1
}
; -------------------------------------------------------------------------------------------------------------------------------
Release() {
; IUnknown -> msdn.microsoft.com/en-us/library/ms682317(v=vs.85).aspx
; Reference counting is not needed in this case.
Return 0
}
; -------------------------------------------------------------------------------------------------------------------------------
DragEnter(pDataObj, grfKeyState, P3 := "", P4 := "", P5 := "") {
; DragEnter -> msdn.microsoft.com/en-us/library/ms680106(v=vs.85).aspx
; Params 32: IDataObject *pDataObj, DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
; Params 64: IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
Static HelperEnter := A_PtrSize * 3
Instance := Object(A_EventInfo)
If (A_PtrSize = 8)
X := P2 & 0xFFFFFFFF, Y := P2 >> 32
Else
X := P2, Y := P3
Effect := 0
If !(grfKeyState & 0x02) { ; right-drag isn't supported by default
For Each, Format In Instance.Required {
IDataObject_CreateFormatEtc(FORMATETC, Format)
If (Effect := IDataObject_QueryGetData(pDataObj, FORMATETC))
Break
}
}
If (Effect) && (Instance.EnterUserFunc)
Effect := Instance.EnterUserFunc.Call(Instance, pDataObj, grfKeyState, X, Y, Effect)
Instance.PreferredDropEffect := Effect
; If Ctrl and/or Shift is pressed swap the effect
Effect ^= grfKeyState & 0x0C ? 3 : 0
; Call IDropTargetHelper, if created
If (Instance.Helper) {
VarSetCapacity(PT, 8, 0)
, NumPut(X, PT, 0, "Int")
, NumPut(Y, PT, 0, "Int")
, pVTBL := NumGet(Instance.Helper + 0, "UPtr")
, DllCall(NumGet(pVTBL + HelperEnter, "UPtr")
, "Ptr", Instance.Helper, "Ptr", Instance.HWND, "Ptr", pDataObj, "Ptr", &PT, "UInt", Effect, "Int")
}
NumPut(Effect, (A_PtrSize = 8 ? P4 : P5) + 0, "UInt")
Return 0 ; S_OK
}
; -------------------------------------------------------------------------------------------------------------------------------
DragOver(grfKeyState, P2 := "", P3 := "", P4 := "") {
; DragOver -> msdn.microsoft.com/en-us/library/ms680129(v=vs.85).aspx
; Params 32: DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
; Params 64: DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
Static HelperOver := A_PtrSize * 5
Instance := Object(A_EventInfo)
If (A_PtrSize = 8)
X := P2 & 0xFFFFFFFF, Y := P2 >> 32
Else
X := P2, Y := P3
; If Ctrl and/or Shift is pressed swap the effect
Effect := Instance.PreferredDropEffect ^ (grfKeyState & 0x0C ? 3 : 0)
If (Effect) && (Instance.OverUserFunc)
Effect := Instance.OverUserFunc.Call(Instance, grfKeyState, X, Y, Effect)
If (Instance.Helper) {
VarSetCapacity(PT, 8, 0)
, NumPut(X, PT, 0, "Int")
, NumPut(Y, PT, 0, "Int")
, pVTBL := NumGet(Instance.Helper + 0, "UPtr")
, DllCall(NumGet(pVTBL + HelperOver, "UPtr"), "Ptr", Instance.Helper, "Ptr", &PT, "UInt", Effect, "Int")
}
NumPut(Effect, (A_PtrSize = 8 ? P3 : P4) + 0, "UInt")
Return 0 ; S_OK
}
; -------------------------------------------------------------------------------------------------------------------------------
DragLeave() {
; DragLeave -> msdn.microsoft.com/en-us/library/ms680110(v=vs.85).aspx
Static HelperLeave := A_PtrSize * 4
Instance := Object(A_EventInfo)
Instance.PreferredDropEffect := 0
If (Instance.LeaveUserFunc)
Instance.LeaveUserFunc.Call(Instance)
If (Instance.Helper) {
pVTBL := NumGet(Instance.Helper + 0, "UPtr"), DllCall(NumGet(pVTBL + HelperLeave, "UPtr"), "Ptr", Instance.Helper)
}
Return 0 ; S_OK
}
; -------------------------------------------------------------------------------------------------------------------------------
Drop(pDataObj, grfKeyState, P3 := "", P4 := "", P5 := "") {
; Drop -> msdn.microsoft.com/en-us/library/ms687242(v=vs.85).aspx
; Params 32: IDataObject *pDataObj, DWORD grfKeyState, LONG x, LONG y, DWORD *pdwEffect
; Params 64: IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect
Static HelperDrop := A_PtrSize * 6
Instance := Object(A_EventInfo)
If (A_PtrSize = 8)
X := P3 & 0xFFFFFFFF, Y := P3 >> 32
Else
X := P3, Y := P4
Effect := Instance.PreferredDropEffect ^ (grfKeyState & 0x0C ? 3 : 0)
Effect := Instance.DropUserFunc.Call(Instance, pDataObj, grfKeyState, X, Y, Effect)
NumPut(Effect, (A_PtrSize = 8 ? P4 : P5) + 0, "UInt")
If (Instance.Helper) {
VarSetCapacity(PT, 8, 0)
, NumPut(X, PT, 0, "Int")
, NumPut(Y, PT, 0, "Int")
, pVTBL := NumGet(Instance.Helper + 0, "UPtr")
, DllCall(NumGet(pVTBL + HelperDrop, "UPtr"), "Ptr", Instance.Helper, "Ptr", pDataObj, "Ptr", &PT, "UInt", Effect, "Int")
}
ObjRelease(pDataObj)
Return 0 ; S_OK
}
}
; ==================================================================================================================================
; from https://github.com/AHK-just-me/DoDragDrop/blob/master/sources/IDataObject.ahk
; ==================================================================================================================================
; IDataObject interface -> msdn.microsoft.com/en-us/library/ms688421(v=vs.85).aspx
; Partial implementation.
; Requires: IEnumFORMATETC.ahk
; ==================================================================================================================================
IDataObject_EnumFormatEtc(pDataObj) {
; EnumFormatEtc -> msdn.microsoft.com/en-us/library/ms683979(v=vs.85).aspx
; DATADIR_GET = 1
Static EnumFormatEtc := A_PtrSize * 8
pVTBL := NumGet(pDataObj + 0, "UPtr")
If !DllCall(NumGet(pVTBL + EnumFormatEtc, "UPtr"), "Ptr", pDataObj, "UInt", 1, "PtrP", ppenumFormatEtc, "Int")
Return ppenumFormatEtc
Return False
}
; ==================================================================================================================================
IDataObject_GetData(pDataObj, ByRef FORMATETC, ByRef Size, ByRef Data) {
; GetData -> msdn.microsoft.com/en-us/library/ms678431(v=vs.85).aspx
Static GetData := A_PtrSize * 3
Data := ""
, Size := -1
, VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
, pVTBL := NumGet(pDataObj + 0, "UPtr")
If !DllCall(NumGet(pVTBL + GetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Ptr", &STGMEDIUM, "Int") {
If (NumGet(STGMEDIUM, "UInt") = 1) { ; TYMED_HGLOBAL
hGlobal := NumGet(STGMEDIUM, A_PtrSize, "UPtr")
, pGlobal := DllCall("GlobalLock", "Ptr", hGlobal, "Uptr")
, Size := DllCall("GlobalSize", "Ptr", hGlobal, "UPtr")
, VarSetCapacity(Data, Size, 0)
, DllCall("RtlMoveMemory", "Ptr", &Data, "Ptr", pGlobal, "Ptr", Size)
, DllCall("GlobalUnlock", "Ptr", hGlobal)
, DllCall("Ole32.dll\ReleaseStgMedium", "Ptr", &STGMEDIUM)
Return True
}
DllCall("Ole32.dll\ReleaseStgMedium", "Ptr", &STGMEDIUM)
}
Return False
}
; ==================================================================================================================================
IDataObject_QueryGetData(pDataObj, ByRef FORMATETC) {
; QueryGetData -> msdn.microsoft.com/en-us/library/ms680637(v=vs.85).aspx
Static QueryGetData := A_PtrSize * 5
pVTBL := NumGet(pDataObj + 0, "UPtr")
Return !DllCall(NumGet(pVTBL + QueryGetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Int")
}
; ==================================================================================================================================
IDataObject_SetData(pDataObj, ByRef FORMATETC, ByRef STGMEDIUM) {
; SetData -> msdn.microsoft.com/en-us/library/ms686626(v=vs.85).aspx
Static SetData := A_PtrSize * 7
pVTBL := NumGet(pDataObj + 0, "UPtr")
Return !DllCall(NumGet(pVTBL + SetData, "UPtr"), "Ptr", pDataObj, "Ptr", &FORMATETC, "Ptr", &STGMEDIUM, "Int", True, "Int")
}
; ==================================================================================================================================
; Auxiliary functions to get/set data of the data object.
; ==================================================================================================================================
; FORMATETC structure -> msdn.microsoft.com/en-us/library/ms682242(v=vs.85).aspx
; ==================================================================================================================================
IDataObject_CreateFormatEtc(ByRef FORMATETC, Format, Aspect := 1, Index := -1, Tymed := 1) {
; DVASPECT_CONTENT = 1, Index all data = -1, TYMED_HGLOBAL = 1
VarSetCapacity(FORMATETC, 32, 0) ; 64-bit
, NumPut(Format, FORMATETC, 0, "Ushort")
, NumPut(Aspect, FORMATETC, A_PtrSize = 8 ? 16 : 8 , "UInt")
, NumPut(Index, FORMATETC, A_PtrSIze = 8 ? 20 : 12, "Int")
, NumPut(Tymed, FORMATETC, A_PtrSize = 8 ? 24 : 16, "UInt")
Return &FORMATETC
}
; ==================================================================================================================================
IDataObject_ReadFormatEtc(ByRef FORMATETC, ByRef Format, ByRef Device, ByRef Aspect, ByRef Index, ByRef Tymed) {
Format := NumGet(FORMATETC, OffSet := 0, "UShort")
, Device := NumGet(FORMATETC, Offset += A_PtrSize, "UPtr")
, Aspect := NumGet(FORMATETC, Offset += A_PtrSize, "UInt")
, Index := NumGet(FORMATETC, Offset += 4, "Int")
, Tymed := NumGet(FORMATETC, Offset += 4, "UInt")
}
; ==================================================================================================================================
; Get/Set format data.
; ==================================================================================================================================
IDataObject_GetDroppedFiles(pDataObj, ByRef DroppedFiles) {
; msdn.microsoft.com/en-us/library/bb773269(v=vs.85).aspx
IDataObject_CreateFormatEtc(FORMATETC, 15) ; CF_HDROP
DroppedFiles := []
If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
Offset := NumGet(Data, 0, "UInt")
CP := NumGet(Data, 16, "UInt") ? "UTF-16" : "CP0"
Shift := (CP = "UTF-16")
While (File := StrGet(&Data + Offset, CP)) {
DroppedFiles.Push(File)
Offset += (StrLen(File) + 1) << Shift
}
}
Return DroppedFiles.Length()
}
; ==================================================================================================================================
IDataObject_GetLogicalDropEffect(pDataObj, ByRef DropEffect) {
Static LogicalDropEffect := DllCall("RegisterClipboardFormat", "Str", "Logical Performed DropEffect")
IDataObject_CreateFormatEtc(FORMATETC, LogicalDropEffect)
DropEffect := ""
If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
DropEffect := NumGet(Data, "UChar")
Return True
}
Return False
}
; ==================================================================================================================================
IDataObject_GetPerformedDropEffect(pDataObj, ByRef DropEffect) {
Static PerformedDropEffect := DllCall("RegisterClipboardFormat", "Str", "Performed DropEffect")
IDataObject_CreateFormatEtc(FORMATETC, PerformedDropEffect)
DropEffect := ""
If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
DropEffect := NumGet(Data, "UChar")
Return True
}
Return False
}
; ==================================================================================================================================
IDataObject_GetText(pDataObj, ByRef Txt) {
Static CF_NATIVE := A_IsUnicode ? 13 : 1 ; CF_UNICODETEXT : CF_TEXT
IDataObject_CreateFormatEtc(FORMATETC, CF_NATIVE)
Txt := ""
If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
Txt := StrGet(Data, Size >> !!A_IsUnicode)
Return True
}
Return False
}
; ==================================================================================================================================
IDataObject_SetLogicalDropEffect(pDataObj, DropEffect) {
Static LogicalDropEffect := DllCall("RegisterClipboardFormat", "Str", "Logical Performed DropEffect")
IDataObject_CreateFormatEtc(FORMATETC, LogicalDropEffect)
, VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
, NumPut(1, STGMEDIUM, "UInt") ; TYMED_HGLOBAL
; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
, hMem := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 4, "UPtr")
, pMem := DllCall("GlobalLock", "Ptr", hMem)
, NumPut(DropEffect, pMem + 0, "UChar")
, DllCall("GlobalUnlock", "Ptr", hMem)
, NumPut(hMem, STGMEDIUM, A_PtrSize, "UPtr")
Return IDataObject_SetData(pDataObj, FORMATETC, STGMEDIUM)
}
; ==================================================================================================================================
IDataObject_SetPerformedDropEffect(pDataObj, DropEffect) {
Static PerformedDropEffect := DllCall("RegisterClipboardFormat", "Str", "Performed DropEffect")
IDataObject_CreateFormatEtc(FORMATETC, PerformedDropEffect)
, VarSetCapacity(STGMEDIUM, 24, 0) ; 64-bit
, NumPut(1, STGMEDIUM, "UInt") ; TYMED_HGLOBAL
; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
, hMem := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 4, "UPtr")
, pMem := DllCall("GlobalLock", "Ptr", hMem)
, NumPut(DropEffect, pMem + 0, "UChar")
, DllCall("GlobalUnlock", "Ptr", hMem)
, NumPut(hMem, STGMEDIUM, A_PtrSize, "UPtr")
Return IDataObject_SetData(pDataObj, FORMATETC, STGMEDIUM)
}
; ==================================================================================================================================
IDataObject_SHFileOperation(pDataObj, TargetPath, Operation, HWND := 0) {
; SHFileOperation -> msdn.microsoft.com/en-us/library/bb762164(v=vs.85).aspx
If Operation Not In 1,2
Return False
IDataObject_CreateFormatEtc(FORMATETC, 15) ; CF_HDROP
If IDataObject_GetData(pDataObj, FORMATETC, Size, Data) {
Offset := NumGet(Data, 0, "UInt") ; offset of the file list
IsUnicode := NumGet(Data, 16, "UInt") ; 1: Unicode, 0: ANSI
TargetLen := StrPut(TargetPath, IsUnicode ? "UTF-16" : "CP0") + 2
VarSetCapacity(Target, TargetLen << !!IsUnicode, 0)
StrPut(TargetPath, &Target, IsUnicode ? "UTF-16" : "CP0")
SHFOSLen := A_PtrSize * (A_PtrSize = 8 ? 7 : 8)
VarSetCapacity(SHFOS, SHFOSLen, 0) ; SHFILEOPSTRUCT
NumPut(HWND, SHFOS, 0, "UPtr")
NumPut(Operation, SHFOS, A_PtrSize, "UInt") ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect
NumPut(&Data + Offset, SHFOS, A_PtrSize * 2, "UPtr")
NumPut(&Target, SHFOS, A_PtrSize * 3, "UPtr")
NumPut(0x0200, SHFOS, A_PtrSize * 4, "UInt") ; FOF_NOCONFIRMMKDIR
If (IsUnicode)
Return DllCall("Shell32.dll\SHFileOperationW", "Ptr", &SHFOS, "Int")
Else
Return DllCall("Shell32.dll\SHFileOperationA", "Ptr", &SHFOS, "Int")
}
}
; ==================================================================================================================================
; from https://github.com/AHK-just-me/DoDragDrop/blob/master/sources/IEnumFORMATETC.ahk
; ==================================================================================================================================
; IEnumFORMATETC interface -> msdn.microsoft.com/en-us/library/ms682337(v=vs.85).aspx
; Partial implementation, 'Clone' method is missing.
; ==================================================================================================================================
IEnumFORMATETC_Next(pEnumObj, ByRef FORMATETC) {
; Next -> msdn.microsoft.com/en-us/library/dd542673(v=vs.85).aspx
Static Next := A_PtrSize * 3
VarSetCapacity(FORMATETC, A_PtrSize = 8 ? 32 : 20, 0)
, pVTBL := NumGet(pEnumObj + 0, "UPtr")
Return !DllCall(NumGet(pVTBL + Next, "UPtr"), "Ptr", pEnumObj, "UInt", 1, "Ptr", &FORMATETC, "Ptr", 0, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumFORMATETC_Reset(pEnumObj) {
; Reset -> msdn.microsoft.com/en-us/library/dd542674(v=vs.85).aspx
Static Reset := A_PtrSize * 5
pVTBL := NumGet(pEnumObj + 0, "UPtr")
Return !DllCall(NumGet(pVTBL + Reset, "UPtr"), "Ptr", pEnumObj, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------------------
IEnumFORMATETC_Skip(pEnumObj, ItemCount) {
; Skip -> msdn.microsoft.com/en-us/library/dd542674(v=vs.85).aspx
Static Skip := A_PtrSize * 4
pVTBL := NumGet(pEnumObj + 0, "UPtr")
Return !DllCall(NumGet(pVTBL + Skip, "UPtr"), "Ptr", pEnumObj, "UInt", ItemCount, "Int")
}
; ==================================================================================================================================