ATTENTION: You are viewing a page formatted for mobile devices; to view the full web page, click HERE.

DonationCoder.com Software > Post New Requests Here

IDEA: Drop Zone for Web Images

<< < (4/4)

lifeweaver:
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 (October 12, 2015, 11:34 AM)
--- End quote ---

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")
}
; ==================================================================================================================================

magician62:
Your right, I must have missed typing minutes in the original spec :)

The JPG jpg issue is as you say. I must have hit on some images where everything was JPG which made it appear that a change has happened.

Can you repost the code, it didn't add correctly and now has emoticons :)

lifeweaver:
Just modified it, I think there is a limit to how much text can be posted in one post as the preview looked correct but it kept cutting off some.

magician62:
Works well. I like the create /update change. It will save 4 clicks for every image, especially when you are almost certain there will be a name conflict 

You should find a small donation of my appreciation. :)

lifeweaver:
Thanks!

Navigation

[0] Message Index

[*] Previous page

Go to full version