[COM] Help with the IDropSource and IDropTarget interfaces
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Oh, that is unfortunate! Just me's implementation is so close to being the perfect one. One can only hope someone, by sheer luck or by vast experience (really unfortunate that 'Sean' seems no longer active), manages to get the OLE data source modification needed for the source helper right.
- lifeweaver
- Posts: 144
- Joined: 10 May 2014, 05:57
- Location: OH
- Contact:
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Here is an interesting post: http://www.autohotkey.com/board/topic/8 ... /?p=525764
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Here is a "real life" example of Just Me`s D&D interface. Download the needed scripts from Github https://github.com/AHK-just-me/DoDragDr ... er/sources or copy from earlier posts in this thread and place:jballi wrote:...A few "real life" examples wouldn't hurt. Thanks for sharing.
DoDragDrop.ahk
IDataObject.ahk
IDropSource.ahk
IDropTarget.ahk
IEnumFORMATETC.ahk
in the same folder as this GUI script below. Set your folder you want to display in that script.
Copy the non-working IDragSourceHelper.ahk from earlier in thread and place it with the others.
Now D&D from the windows explorer to the AHK GUI, notice how source drag icon shows up when start dragging a file. If using Shift or Ctrl + Lefltclick drag we get to know if its a Copy or Move operation just below the source drag icon. If not holding down Shift or Ctrl while drag, when drag icon/cursor leaves the shell window then user gets to know if it is a move or copy D&D. That is what the IDragSourceHelper does for the shell. Dragging from the AHK GUI it differentiate between copy and move operations, but the drag symbol is hardly visible and no source icon. Getting the IDragSourceHelper to work when doing D&D operations from an AHK GUI would be amazing Could this be solved?
Regards
zcooler
Code: Select all
#NoEnv
#SingleInstance, force
SetBatchLines, -1 ; important for performance
SetWinDelay, -1 ; important for Dock()
; ==================================================================================================================================
; Media folders
RSFolder := "F:\TV\Seen Recordings" ; Set the folder path here...easily better test if the folder has sub-folders.
RSCaption := "DRAGnDROP Interface Test - Set some folder which this button should display!"
; WatchFolder settings
WatchFolders=%RSFolder%* ; *=recurse subfolders
; ==================================================================================================================================
; Global variables for Dock() replacement
Global Gui1X, Gui1Y, Gui2X, Gui2Y, HGUI1, HGUI2
Global Gui2Visible := False
; Global variables for the TreeView
Global Logging := False ; set to True to enable logging within the ReportFunction
Global TVHWND ; handle of the TreeView control, filled later
Global TVFolder ; name of the current root folder, filled later
Global TVRootID ; ID of the current root node, filled later
Global TVDragID := 0 ; ID of the currently dragged item +++++
Global TVDropID := 0 ; ID of the item on which a drop ocurred +++++
Global TVLastID ; ID of the last visible item +++++
Global TVClientW ; the width of the TreeView control's client area, calculated later
Global TVClientH ; the height of the TreeView control's client area, calculated later
Global TVDeltaW ; delta to add to the width of the TreeView control, calculated later
Global TVDeltaH ; delta to add to the height of the TreeView control, calculated later
Global TVItemHeight ; height of all tree view items, calculated later
Global TVMaxHeight ; maximum height of the TreeView control, calculated later
Global TVMaxRows := 20 ; maximum rows shown in the TreeView control
Global TVMinWidth := 300 ; minimum width of the TreeView control - not implemented yet
Global TVMaxWidth := 600 ; maximum width of the TreeView control - not implemented yet
;========================================================================================
; GUI 1
Gui, Gui1:New, +HwndHGUI1 +LabelGui1
Gui, Margin, 10, 10
Gui, Add, Button, ym gGetFiles vRS hwndHRSBTN, %RSCaption% ;+++ added hwnd option
IDT_RS := IDropTarget_Create(HRSBTN, "_BTN") ;+++ Create a drop target for the buttons
Gui, Show, x400 y35, Some GUI
; ==================================================================================================================================
; GUI 2
Gui, Gui2:New, +HwndHGUI2 +OwnerGui1 -Caption +0x02000000 ; WS_CLIPCHILDREN = 0x02000000 is needed in this case ;
Gui, Margin, 0, 0
Gui, Color, 000000
Gui, Font, s9 w800, Ebrima ; Segoe UI Symbol Arial Tahoma
; Create the TreeView
Gui, Add, TreeView, r%TVMaxRows% w650 hwndTVHWND +0x0800 +0x200 Backgroundf2f2f2 -HScroll AltSubmit gTVLabel vTVVar -E0x0200 +E0x020000
; Calculate the deltas between the TreeView's non-client and client area
GuiControlGet, TV, Pos, TVVar
TVMaxHeight := TVH
TVDeltaH := Mod(TVMaxHeight, TVMaxRows)
TVDeltaW := TVDeltaH
; Set Vista+ theme and styles
TV_SetExplorerTheme(TVHWND, "R")
TV_DoubleBuffer(TVHWND)
ImageListID := IL_Create(12)
Loop 12 {
IL_Add(ImageListID, "shell32.dll", A_Index)
}
TV_SetImageList(ImageListID)
; Add a faked StatusBar
Gui, Add, Text, xp y+0 wp h20 +0x200 cWhite hwndTVHSB vSBVar +E0x020000
IDT_TV := IDropTarget_Create(TVHWND, "_TV") ;+++ Create a drop target for the TreeView
; ==================================================================================================================================
; Message handlers and monitoring
; Watched folders
Loop, Parse, WatchFolders,|
{
SubTree := (SubStr(A_LoopField, StrLen(A_LoopField)) = "*")
WatchFolder(RTrim(A_LoopField, "*"), "ReportFunction", SubTree, Watch := 19)
}
; Dock message handler
OnMessage(0x0003, "Dock") ; WM_MOVE = 0x0003
; Keyboard Nav handler
OnMessage(0x100, "OnKeyDown")
Return
; ======================================================================================================================
; Gui1 labels
; ======================================================================================================================
Gui1Close:
IDT_TV.RevokeDragDrop()
ExitApp
; ======================================================================================================================
; Gui2 labels
; ======================================================================================================================
GetFiles:
IsKEvent := true
WinGetPos, Gui1X, Gui1Y, , , ahk_id %HGUI1%
GuiControlGet, HCTL, Hwnd, %A_GuiControl%
WinGetPos, CtlX, CtlY, CtlW, CtlH, ahk_id %HCTL%
Gui, Gui2:Default
Gui, Hide
Gui2Visible := False
GuiControl, -g, TVVar
TV_Delete()
TVFolder := %A_GuiControl%Folder
ItemCount := AddFoldersAndFilesToTree(TVFolder) ; Populate treeview call
TVRootID := TV_GetNext()
TV_Modify(TVRootID, "Select VisFirst Expand")
TVW := TVH := 0
GuiControlGet, TV, Pos, TVVar
GuiControl, Move, SBVar, % "y" . (TVY + TVH) . " w" . TVW
GuiControl, , SBVar, % " " . ItemCount - 1 . " objects."
X := CtlX
Y := CtlY + CtlH
Gui, Show, x%X% y%Y% AutoSize
WinGetPos, Gui2X, Gui2Y, , , ahk_id %HGUI2%
GuiControl, +gTVLabel, TVVar
Gui2Visible := True
Return
; ----------------------------------------------------------------------------------------------------------------------
;+++ The GuiDropFiles label for Gui2 has to be removed. All code has to be moved to the IDropTargetOnDrop user function.
; ======================================================================================================================
; Common subroutines to Gui2
; ======================================================================================================================
TVLabel:
Critical ; try to forgo the use of Critical if possible
If (A_GuiEvent = "+") || (A_GuiEvent = "-") {
GuiControl, -Redraw, TVVar
GuiControlGet, TV, Pos, TVVar
Gui, Show, AutoSize
GuiControl, +Redraw, TVVar
}
Else If (A_GuiEvent = "Normal") && (A_EventInfo = TV_HitTest(TVHWND)) { ; in this case the item will be selected definitely
ItemID := A_EventInfo
Gosub, TVLabelSelect
}
Else If (A_GuiEvent == "D") { ;+++ Only left dragging is supported, changed
TVDragID := A_EventInfo
GoSub, TVDragDrop
}
Else
IsKEvent := A_GuiEvent == "K" ? True : False
Return
;------------------------------------------------------------------------------
TVLabelSelect:
TV_GetText(item, ItemID)
FileName := item
ItemID2 := ItemID
while ParentID := TV_GetParent(ItemID2)
{
TV_GetText(item, ParentID)
FileName := item "\" FileName
ItemID2 := ParentID
}
if (IsMovie := InStr(FileExist(FileName), "D") ? false : true)
RunWait, Open %FileName%
else {
GuiControl, -g, TVVar
TV_Modify(ItemID, TV_Get(ItemID, "Expand") ? "-Expand" : "Expand")
GuiControl, -Redraw, TVVar
GuiControlGet, TV, Pos, TVVar
Gui, Show, AutoSize
GuiControl, +Redraw, TVVar
GuiControl, +gTVLabel, TVVar
}
return
;------------------------------------------------------------------------------
TVDragDrop:
TVDragPath := TV_GetPath(TVDragID)
TVDropID := 0
TVLastID := TV_GetLastVisible(TVHWND)
TV_GetClientSize(TVHWND, TVClientW, TVClientH)
TVItemHeight := TV_GetItemHeight(TVHWND) ;;;;from old TVDragDrop label
If (IsCritical := A_IsCritical)
Critical, Off
GuiControl, -g, TVVar ;;;;from old TVDragDrop label
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0009, "Ptr", 0) ; TVM_SELECTITEM, TVGN_CARET (remove the selection)
;+++ TVDragDropRunning := True v ; deprecated
SavedClipboard := ClipBoardAll
ClipboardSetFiles(TVDragPath, "")
;SB_SetText(" Dragging ...")
DropEffect := DoDragDrop()
;SB_SetText(" DropEffect: " . {0: "NONE", 1: "COPY", 2: "MOVE"}[DropEffect])
SetTimer, TVDragDropTimer, Off
TVDragID := 0 ;+++
TVDropID := 0 ;+++
;+++ Sleep, 10 ; deprecated
;+++ If (DropEffect = 0) ; deprecated
;+++ TVDragDropRunning := False ; deprecated
ClipBoard := SavedClipboard
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
GuiControl, +gTVLabel, TVVar ;;;;from old TVDragDrop label
If (IsCritical)
Critical, %IsCritical%
Return
;------------------------------------------------------------------------------
TVDragDropTimer:
MouseItemID := TV_HitTest(TVHWND, 0x0026)
If (MouseItemID = 0) {
TVDropID := 0
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
}
Else If (MouseItemID <> TVDropID) {
TVDropID := MouseItemID
HoverTickCount := ScrollTickCount := A_TickCount
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", TVDropID) ; TVM_SELECTITEM, TVIS_DROPHILITED
}
Else {
If ((A_TickCount - HoverTickCount) >= 1000) {
VarSetCapacity(TVIX, 40 + (A_PtrSize * 5), 0)
NumPut(0x0048, TVIX, 0, "UInt") ; TVIF_CHILDREN | TVIF_STATE
NumPut(TVDropID, TVIX, A_PtrSize, "UPtr")
If DllCall("SendMessage", "Ptr", TVHWND, "UInt", A_IsUnicode ? 0x113E : 0x110C, "Ptr", 0, "Ptr", &TVIX, "Int")
&& (NumGet(TVIX, 20 + (A_PtrSize * 3), "Int") = 1) && !(NumGet(TVIX, A_PtrSize * 2, "Uint") & 0x0020) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x1102, "Ptr", 0x0002, "Ptr", TVDropID, "Int") ; TVM_EXPAND
HoverTickCount := A_TickCount
If (TVDropID = TVLastID)
TVLastID := TV_GetLastVisible(TVHWND)
}
}
If ((A_TickCount - ScrollTickCount) >= 100) { ; 100 ms seems to be sufficient here for scrolling
If (TVDropID = TV_GetFirstVisible(TVHWND)) && (TVDropID <> TVRootID) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 0, "Ptr", 0) ; WM_VSCROLL
TVDropID := 0
HoverTickCount := A_TickCount
}
Else {
TV_GetItemRect(TVHWND, TVDropID, X, Y, W, H)
If (TVDropID = TVLastID) {
If ((Y + H) > TVClientH)
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 1, "Ptr", 0) ; WM_VSCROLL
}
Else If ((Y + H + TVItemHeight) > TVClientH) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 1, "Ptr", 0) ; WM_VSCROLL
TVDropID := 0
HoverTickCount := A_TickCount
}
}
ScrollTickCount := A_TickCount
}
}
Return
;----------------------------------------------------------------------------------------------------------
;-FUNCTIONS------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
AddFoldersAndFilesToTree(Folder, ParentID := 0, First := 0) {
;msgbox % First
SplitPath, Folder, FolderName
If (ParentID = 0)
ID := TV_Add(Folder, ParentID, "Icon7 Expand") ; ID gets a value
Else If (First = 0)
ID := TV_Add(FolderName, ParentID, "Icon7") ; ID gets a value
Else If (First = 1)
ID := TV_Add(FolderName, ParentID, "First Icon7") ; ID gets a value
; -------------------------------------------------------------------------------------------------------------------------------
FolderList =
Loop, %Folder%\*.*, 2
FolderList .= (A_Index = 1 ? "" : "`n") A_LoopFileTimeModified "|" A_LoopFileName "|" A_LoopFileLongPath
; Sort folder options If recurse
If (Settings.SortRev = "1") ; Sort folders reverse enabled
Sort, FolderList, F ReverseDirection D`n, ; Reverses the list so that it contains 4,3,2,1
If (Settings.SortTimeRev = "1") ; Sort folders timereverse enabled
Sort, FolderList, F SortFunc ; Sort folders TimeModified, newest on top to oldest in bottom
Loop, Parse, FolderList, `n
AddFoldersAndFilesToTree(StrSplit(A_LoopField, "|").3, ID)
; -------------------------------------------------------------------------------------------------------------------------------
FileList := ""
Loop, % Folder . "\*.*", 0
FileList .= (FileList <> "" ? "`n" : "") . A_LoopFileTimeCreated . "|" . A_LoopFileName . "|" . A_LoopFileTimeModified
; -------------------------------------------------------------------------------------------------------------------------------
Sort, FileList, F ReverseDirection D`n, ; Reverses the list so that it contains 4,3,2,1
; -------------------------------------------------------------------------------------------------------------------------------
Loop, Parse, FileList, `n
{
Split := StrSplit(A_LoopField, "|") ;+++ to avoid repeated calls of StrSplit()
If (Split.1 = Split.3) ; recording isn't finished yet
TV_Add(Split.2, ID, "Icon4")
Else ; recording is finished
TV_Add(Split.2, ID, "Icon2")
}
Return TV_GetCount()
}
; ==================================================================================================================================
; Auxiliary functions ==============================================================================================================
; ==================================================================================================================================
SortFunc(_line1, _line2)
{
line1 := StrSplit(_line1, "|")
line2 := StrSplit(_line2, "|")
if (line1[1] < line2[1]) ; sort in descending order
;msgbox % line1[1] " " line2[1] "`ndescending"
return 1
if (line1[1] = line2[1])
;msgbox % line1[1] "`nascending"
if (line1[2] > line2[2]) ; sort in ascending order
return 1
return -1
}
ReverseDirection(a1, a2, offset)
{
return offset ; Offset is positive if a2 came after a1 in the original list; negative otherwise.
}
;--------------------------------------------------------------------------------
; Docking Guis
Dock(W, L, M, H) { ; <<<<< added Dock replacement
If (H = HGUI1) && Gui2Visible {
WinGetPos, X, Y, , , ahk_id %HGUI1%
Gui2X += X - Gui1X
Gui2Y += Y - Gui1Y
Gui1X := X
Gui1Y := Y
WinMove, ahk_id %HGUI2%, , %Gui2X%, %Gui2Y%
}
}
; ==================================================================================================================================
; Explorer function for Drag&Drop and Pasting. Enables the explorer paste context menu option.
; ==================================================================================================================================
ClipboardSetFiles(FilesToSet, DropEffect := "Copy") {
Static TCS := A_IsUnicode ? 2 : 1 ; size of a TCHAR
Static PreferredDropEffect := DllCall("RegisterClipboardFormat", "Str", "Preferred DropEffect")
Static DropEffects := {1: 1, 2: 2, Copy: 1, Move: 2}
; -------------------------------------------------------------------------------------------------------------------
; Count files and total string length
TotalLength := 0
FileArray := []
Loop, Parse, FilesToSet, `n, `r
{
If (Length := StrLen(A_LoopField))
FileArray.Push({Path: A_LoopField, Len: Length + 1})
TotalLength += Length
}
FileCount := FileArray.Length()
If !(FileCount && TotalLength)
Return
; -------------------------------------------------------------------------------------------------------------------
; Add files to the clipboard
If DllCall("OpenClipboard", "Ptr", A_ScriptHwnd) && DllCall("EmptyClipboard") {
; HDROP format ---------------------------------------------------------------------------------------------------
; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
hPath := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 20 + (TotalLength + FileCount + 1) * TCS, "UPtr")
pPath := DllCall("GlobalLock", "Ptr" , hPath)
Offset := 20
NumPut(Offset, pPath + 0, "UInt") ; DROPFILES.pFiles = offset of file list
NumPut(!!A_IsUnicode, pPath + 16, "UInt") ; DROPFILES.fWide = 0 --> ANSI, fWide = 1 --> Unicode
For Each, File In FileArray
Offset += StrPut(File.Path, pPath + Offset, File.Len) * TCS
DllCall("GlobalUnlock", "Ptr", hPath)
DllCall("SetClipboardData","UInt", 0x0F, "UPtr", hPath) ; 0x0F = CF_HDROP
; Preferred DropEffect format ------------------------------------------------------------------------------------
If (DropEffect := DropEffects[DropEffect]) {
; Write Preferred DropEffect structure to clipboard to switch between copy/cut operations
; 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)
DllCall("SetClipboardData", "UInt", PreferredDropEffect, "Ptr", hMem)
}
DllCall("CloseClipboard")
}
Return
}
;--------------------------------------------------------------------------------------
; Clipboard function. Retrieves if files in clipboard comes from an explorer cut or copy operation.
; explorer copy = 5, explorer cut = 2
ClipboardGetDropEffect() {
Static PreferredDropEffect := DllCall("RegisterClipboardFormat", "Str" , "Preferred DropEffect")
DropEffect := 0
If DllCall("IsClipboardFormatAvailable", "UInt", PreferredDropEffect) {
If DllCall("OpenClipboard", "Ptr", 0) {
hDropEffect := DllCall("GetClipboardData", "UInt", PreferredDropEffect, "UPtr")
pDropEffect := DllCall("GlobalLock", "Ptr", hDropEffect, "UPtr")
DropEffect := NumGet(pDropEffect + 0, 0, "UChar")
DllCall("GlobalUnlock", "Ptr", hDropEffect)
DllCall("CloseClipboard")
}
}
Return DropEffect
}
;------------reverse logging function------------------------------------------------------------------------------
AddToBOF(FileName, String) { ; AddToBeginOfFile
If (File := FileOpen(FileName, "rw", "UTF-8-RAW")) {
Content := File.Read()
File.Pos := 0
File.Write(String . Content)
File.Close()
Return True
}
Return False
}
; ==================================================================================================================================
; Function: Notifies about changes within folders.
; This is a rewrite of HotKeyIt's WatchDirectory() released at
; http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with: AHK 1.1.23.01 (A32/U32/U64)
; Tested on: Win 10 Pro x64
; Usage: WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
; Folder - The full qualified path of the folder to be watched.
; Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
; Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
; If not, it will be done internally on exit.
; UserFunc - The name of a user-defined function to call on changes. The function must accept at least two parameters:
; 1: The path of the affected folder. The final backslash is not included even if it is a drive's root
; directory (e.g. C:).
; 2: An array of change notifications containing the following keys:
; Action: One of the integer values specified as FILE_ACTION_... (see below).
; In case of renaming Action is set to FILE_ACTION_RENAMED (4).
; Name: The full path of the changed file or folder.
; OldName: The previous path in case of renaming, otherwise not used.
; IsDir: True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
; Pass the string "**DEL" to remove the directory from the list of watched folders.
; SubTree - Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
; Default: False - sub-folders aren't watched.
; Watch - The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
; values specified below.
; Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
; Returns True on success; otherwise False.
; Change history:
; 1.0.01.00/2016-03-14/just me - bug-fix for multiple folders
; 1.0.00.00/2015-06-21/just me - initial release
; License:
; The Unlicense -> http://unlicense.org/
; Remarks:
; Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
; folders simultaneously.
; MSDN:
; ReadDirectoryChangesW msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
; FILE_NOTIFY_CHANGE_FILE_NAME = 1 (0x00000001) : Notify about renaming, creating, or deleting a file.
; FILE_NOTIFY_CHANGE_DIR_NAME = 2 (0x00000002) : Notify about creating or deleting a directory.
; FILE_NOTIFY_CHANGE_ATTRIBUTES = 4 (0x00000004) : Notify about attribute changes.
; FILE_NOTIFY_CHANGE_SIZE = 8 (0x00000008) : Notify about any file-size change.
; FILE_NOTIFY_CHANGE_LAST_WRITE = 16 (0x00000010) : Notify about any change to the last write-time of files.
; FILE_NOTIFY_CHANGE_LAST_ACCESS = 32 (0x00000020) : Notify about any change to the last access time of files.
; FILE_NOTIFY_CHANGE_CREATION = 64 (0x00000040) : Notify about any change to the creation time of files.
; FILE_NOTIFY_CHANGE_SECURITY = 256 (0x00000100) : Notify about any security-descriptor change.
; FILE_NOTIFY_INFORMATION msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
; FILE_ACTION_ADDED = 1 (0x00000001) : The file was added to the directory.
; FILE_ACTION_REMOVED = 2 (0x00000002) : The file was removed from the directory.
; FILE_ACTION_MODIFIED = 3 (0x00000003) : The file was modified.
; FILE_ACTION_RENAMED = 4 (0x00000004) : The file was renamed (not defined by Microsoft).
; FILE_ACTION_RENAMED_OLD_NAME = 4 (0x00000004) : The file was renamed and this is the old name.
; FILE_ACTION_RENAMED_NEW_NAME = 5 (0x00000005) : The file was renamed and this is the new name.
; GetOverlappedResult msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
; CreateFile msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
; FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
; FILE_FLAG_OVERLAPPED = 0x40000000
; ==================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
Static TimerID := "**" . A_TickCount
Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
Static MAXIMUM_WAIT_OBJECTS := 64
Static MAX_DIR_PATH := 260 - 12 + 1
Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
Static SizeOfOVL := 32 ; size of the OVERLAPPED structure (64-bit)
Static WatchedFolders := {}
Static EventArray := []
Static HandleArray := []
Static WaitObjects := 0
Static BytesRead := 0
Static Paused := False
; ===============================================================================================================================
If (Folder = "")
Return False
SetTimer, % TimerFunc, Off
RebuildWaitObjects := False
; ===============================================================================================================================
If (Folder = TimerID) { ; called by timer
If (ObjCount := EventArray.Length()) && !Paused {
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
FolderName := WatchedFolders[ObjIndex + 1]
D := WatchedFolders[FolderName]
If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) {
Changes := []
FNIAddr := D.FNIAddr
FNIMax := FNIAddr + BytesRead
OffSet := 0
PrevIndex := 0
PrevAction := 0
PrevName := ""
Loop {
FNIAddr += Offset
OffSet := NumGet(FNIAddr + 0, "UInt")
Action := NumGet(FNIAddr + 4, "UInt")
Length := NumGet(FNIAddr + 8, "UInt") // 2
Name := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
IsDir := InStr(FileExist(Name), "D") ? 1 : 0
If (Name = PrevName) {
If (Action = PrevAction)
Continue
If (Action = 1) && (PrevAction = 2) {
PrevAction := Action
Changes.RemoveAt(PrevIndex--)
Continue
}
}
If (Action = 4)
PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
Else If (Action = 5) && (PrevAction = 4) {
Changes[PrevIndex, "Name"] := Name
Changes[PrevIndex, "IsDir"] := IsDir
}
Else
PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
PrevAction := Action
PrevName := Name
} Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
If (Changes.Length() > 0)
D.Func.Call(FolderName, Changes)
DllCall("ResetEvent", "Ptr", EventArray[D.Index])
DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree
, "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0)
}
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
Sleep, 0
}
}
}
; ===============================================================================================================================
Else If (Folder = "**PAUSE") { ; called to pause/resume watching
Paused := !!UserFunc
RebuildObjects := Paused
}
; ===============================================================================================================================
Else If (Folder = "**END") { ; called to stop watching
For D In WatchedFolders
If D Is Not Integer
DllCall("CloseHandle", "Ptr", D.Handle)
For Each, Event In EventArray
DllCall("CloseHandle", "Ptr", Event)
WatchedFolders := {}
EventArray := []
Paused := False
Return True
}
; ===============================================================================================================================
Else { ; called to add, update, or remove folders
Folder := RTrim(Folder, "\")
VarSetCapacity(LongPath, SizeOfLongPath, 0)
If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath)
Return False
VarSetCapacity(LongPath, -1)
Folder := LongPath
If (WatchedFolders[Folder]) { ; update or remove
Handle := WatchedFolders[Folder, "Handle"]
Index := WatchedFolders[Folder, "Index"]
DllCall("CloseHandle", "Ptr", Handle)
DllCall("CloseHandle", "Ptr", EventArray[Index])
EventArray.RemoveAt(Index)
WatchedFolders.RemoveAt(Index)
WatchedFolders.Delete(Folder)
RebuildWaitObjects := True
}
If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) {
If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
, "UInt", 0x42000000, "Ptr", 0, "UPtr")
If (Handle > 0) {
Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
Index := EventArray.Push(Event)
WatchedFolders[Index] := Folder
WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch}
WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI)
FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff")
DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
WatchedFolders[Folder, "FNIAddr"] := FNIAddr
WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL)
OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff")
DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
WatchedFolders[Folder, "OVLAddr"] := OVLAddr
DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
, "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
RebuildWaitObjects := True
}
}
}
If (RebuildWaitObjects) {
VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
OffSet := &WaitObjects
For Index, Event In EventArray
Offset := NumPut(Event, Offset + 0, 0, "Ptr")
}
}
; ===============================================================================================================================
If (EventArray.Length() > 0)
SetTimer, % TimerFunc, -100
Return (RebuildWaitObjects) ; returns True on success, otherwise False
}
; ==================================================================================================================================
; WatchFolder - Reportfunctions
ReportFunction(Directory, Changes) {
Global Dummy_DB_BackupEntry, TooltipInfoUpdater, SRFolder, tooltip_infofiles, IsMovie, Logging, RecycleBin_C, RecycleBin_D, RecycleBin_E, RecycleBin_F, RecycleBin_G
Static LogFile := ""
; -------------------------------------------------------------------------------------------------------------------------------
; Logging
If (LogFile = "")
LogFile := TooltipInfoUpdater . "\wd_changes.log"
If (Logging)
FormatTime, Prefix, , yyyy-MM-dd HH:mm:ss
; -------------------------------------------------------------------------------------------------------------------------------
; Check if the current TreeView might be affected.
TVChanged := False
If (Gui2Visible) {
Gui, Gui2:Default
Gui, TreeView, TVVar
GuiControl, -g, TVVar
GuiControl, -Redraw, TVVar
; ----------------------------------------------------------------------------------------------------------------------------
; Loop through the changes.
For Each, Change In Changes {
Action := Change.Action
Name := Change.Name
OldName := Change.Oldname
IsDir := Change.Isdir
If (Logging)
AddToBOF(LogFile, Prefix . " ReportFunction: Action: " . Action . " - Name: " . Name . " - OldName: " . OldName
. " - IsDir: " . IsDir . "`r`n")
; -------------------------------------------------------------------------------------------------------------------------
; Process action 4 (renamed) at first, because Action might have to be changed and processed later on.
If (Action = 4) {
OldID := FindPath(OldName, OldFoundPath)
If (OldName <> OldFoundPath) ; OldName was not found, so we change Action to 1 (added)
Action := 1 ; will be processed later on
Else { ; OldName has been found, so we have to check if the new name is valid
SplitPath, Name, File, Dir, Ext
If (IsDir) { ; the new name is valid, modify the tree
TVChanged := TV_Modify(OldID, , File)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Renamed " . OldName . " to " . Name . "`r`n")
}
Else { ; modifying is not possible, so we have to delete the entry
TVChanged := TV_Delete(OldID)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Removed " . Name . "`r`n")
}
Continue ; nothing else to do, next change
}
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 3 (modified)
If (Action = 3) {
ItemID := FindPath(Name, FoundPath)
If (!IsDir && (Name = FoundPath)) { ; a file shown in the TreeView has been modified
SplitPath, Name, , , Ext
If Ext In ts,avi,mp4,mkv,wmv,mpg ; it's a movie file, so the icon might need to be updated
{
TVChanged := TV_Modify(ItemID, "Icon2")
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Modified " . Name . "`r`n")
}
}
Continue ; nothing else to do, next change
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 2 (removed)
If (Action = 2) {
ItemID := FindPath(Name, FoundPath)
If (Name = FoundPath) { ; Name was found and must be deleted
TVChanged := TV_Delete(ItemID)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Removed " . Name . "`r`n")
}
Continue ; nothing else to do, next change
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 1 (added)
If (Action = 1) {
ItemID := FindPath(Name, FoundPath)
If (!ItemID || (Name = FoundPath)) ; Name doesn't belong to the TreeView or exists already, nothing to do with it
Continue
SplitPath, Name, File, Dir, Ext
If (IsDir) { ; it's a folder
TVChanged := AddFoldersAndFilesToTree(Name, ItemID, First := 1)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Added " . Name . "`r`n")
}
Else { ; it's a file
If (FoundPath = Dir) {
If Ext In ts,avi,mp4,mkv,wmv,mpg
{
FileGetTime, Created, %Name%, C
FileGetTime, Modified, %Name%, M
If (Created = Modified) ; File still in recording
TVChanged := TV_Add(File, ItemID, "First Icon4")
Else ; Recording is finished
TVChanged := TV_Add(File, ItemID, "First Icon2")
}
Else ; If FileExt In txt,srt,sub,log
TV_Changed := TV_Add(File, ItemID, "First Icon5")
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Added " . Name . "`r`n")
}
}
}
}
If (TVChanged) {
GuiControlGet, TV, Pos, TVVar
GuiControl, , SBVar, % " " . TV_GetCount() . " objects."
Gui, Show, AutoSize ;NoActivate
GuiControl, +Redraw, TVVar
}
Else
GuiControl, +Redraw, TVVar
GuiControl, +gTVLabel, TVVar
}
}
;-----------------------------------------------------------------------------------------------------------------------------------
; FindPath - Find (parts of) the path within the TreeView
FindPath(PathToFind, ByRef FoundPath) {
FoundPath := ""
If !(InStr(PathToFind, TVFolder) = 1) ; wrong parameters - PathToFind does not belong to the root folder
Return 0
FoundPath := TVFolder
PathID := TVRootID
Split := StrSplit(SubStr(PathToFind, StrLen(TVFolder) + 2), "\") ; split the part following the root folder
ItemID := TV_GetChild(PathID)
Loop, % Split.Length() {
Name := Split[A_Index]
While (ItemID) {
TV_GetText(ItemText, ItemID)
If (Name = ItemText) {
PathID := ItemID
FoundPath .= "\" . Name
ItemID := TV_GetChild(ItemID)
Break
}
ItemID := TV_GetNext(ItemID)
}
} Until (ItemID = 0)
Return PathID
}
; ==================================================================================================================================
; Additional TV functions
; ==================================================================================================================================
TV_GetClientSize(HTV, ByRef W, ByRef H) { ; retrieves the client size of the TreeView
W := H := 0
, VarSetCapacity(RC, 16, 0)
If DllCall("GetClientRect", "Ptr", HTV, "Ptr", &RC) {
W := NumGet(RC, 8, "Int")
, H := NumGet(RC, 12, "Int")
Return True
}
Return False
}
TV_GetFirstVisible(HTV) { ; retrieves the first currently visible item of the TreeView
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x110A, "Ptr", 0x0005, "Ptr", 0, "UPtr") ; TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE
}
TV_GetLastVisible(HTV) { ; retrieves the last visible item of the TreeView
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x110A, "Ptr", 0x000A, "Ptr", 0, "UPtr") ; TVM_GETNEXTITEM, TVGN_LASTVISIBLE
}
TV_GetPath(ItemID, Delimiter := "\") {
TV_GetText(ItemPath, ItemID)
While (ItemID := TV_GetParent(ItemID)) {
TV_GetText(ItemText, ItemID)
ItemPath := ItemText . Delimiter . ItemPath
}
Return ItemPath
}
TV_GetItemHeight(HTV) { ; retrieves the height af all TreeView items
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x111C, "Ptr", 0, "Ptr", 0) ; TVM_GETITEMHEIGHT
}
TV_GetItemRect(HTV, ItemID, ByRef X, ByRef Y, ByRef W, ByRef H, TextOnly := True) { ; retrieves the item's position and size
X := 0, Y := 0, W := 0, H := 0
, VarSetCapacity(RC, 16, 0)
, NumPut(ItemID, RC, 0, "UPtr")
If DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1104, "Ptr", !!TextOnly, "Ptr", &RC) { ; TVM_GETITEMRECT
X := NumGet(RC, 0, "Int")
, Y := NumGet(RC, 4, "Int")
, W := NumGet(RC, 8, "Int") - X
, H := NumGet(RC, 12, "Int") - Y
Return True
}
Return False
}
TV_HitTest(HTV, Test := 0x0006, X := "", Y := "") { ; TVM_HITTEST
; TVM_HITTEST = 0x1111 -> msdn.microsoft.com/en-us/library/bb773732(v=vs.85).aspx
; TVHT_NOWHERE 0x0001 - In the client area, but below the last item.
; TVHT_ONITEMICON 0x0002 - On the bitmap associated with an item.
; TVHT_ONITEMLABEL 0x0004 - On the label (string) associated with an item.
; TVHT_ONITEM 0x0006 - On the bitmap or label associated with an item (defined differently in CommCtrl.h).
; TVHT_ONITEMINDENT 0x0008 - In the indentation associated with an item.
; TVHT_ONITEMBUTTON 0x0010 - On the button associated with an item.
; TVHT_ONITEMRIGHT 0x0020 - In the area to the right of an item.
; TVHT_ONITEMSTATEICON 0x0040 - On the state icon for a tree-view item that is in a user-defined state.
; TVHT_ONITEMROW 0x007E - On or right of an item (not defined in CommCtrl.h).
; TVHT_ABOVE 0x0100 - Above the client area.
; TVHT_BELOW 0x0200 - Below the client area.
; TVHT_TORIGHT 0x0400 - To the right of the client area.
; TVHT_TOLEFT 0x0800 - To the left of the client area.
Test &= 0x0FFF
VarSetCapacity(TVHTI, 8 + (A_PtrSize * 2), 0) ; TVHITTESTINFO
If (X = "") || (Y = "") {
DllCall("User32.dll\GetCursorPos", "Ptr", &TVHTI)
, DllCall("User32.dll\ScreenToClient", "Ptr", HTV, "Ptr", &TVHTI)
}
Else {
NumPut(X, TVHTI, 0, "Int")
, NumPut(Y, TVHTI, 4, "Int")
}
If !(ItemID := DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1111, "Ptr", 0, "Ptr", &TVHTI, "UPtr"))
Return 0
Return (NumGet(TVHTI, 8, "UInt") & Test ? ItemID : 0)
}
; Retrieves the rectangle of the item's text area in screen coordinates
TV_GetItemScreenRect(HTV, ItemID, Byref L := 0, ByRef T := 0, ByRef R := 0, ByRef B := 0) {
; TVM_GETITEMRECT = 0x1104 -> msdn.microsoft.com/en-us/library/bb773610(v=vs.85).aspx
L := T := R := B := 0
VarSetCapacity(RC, 16, 0)
NumPut(ItemID, RC, 0, "Int")
If DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1104, "Ptr", True, "Ptr", &RC) { ; get the rectangle
DllCall("MapWindowPoints", "Ptr", HTV, "Ptr", 0, "Ptr", &RC, "UInt", 2) ; convert client to screen coordinates
L := NumGet(RC, 0, "Int")
T := NumGet(RC, 4, "Int")
R := NumGet(RC, 8, "Int")
B := NumGet(RC, 12, "Int")
Return True
}
Return False
}
TV_SetExplorerTheme(HTV, Option := "") {
; Options:
; R - Remove the TVS_HASLINES style using lines to show the hierarchy of items.
; F - Set the TVS_FULLROWSELECT style. The entire row of the selected item is highlighted, and clicking anywhere on an
; item's row causes it to be selected. The TVS_HASLINES style will be removed, also.
If (TV_OSVersion() > 5) {
If (Option = "R") || (Option = "F") {
Control, Style, -0x0002, , ahk_id %HTV%
If (Option = "F")
Control, Style, +0x1000, , ahk_id %HTV%
}
Return !DllCall("UxTheme.dll\SetWindowTheme", "Ptr", HTV, "WStr", "Explorer", "Ptr", 0)
}
Return False
}
TV_AutoHScroll(HTV, Set := True) {
; TVS_EX_AUTOHSCROLL = 0x0020
Return TV_SetExStyle(HTV, 0x0020, Set ? 0x0020 : 0)
}
TV_DoubleBuffer(HTV, Set := True) {
; TVS_EX_DOUBLEBUFFER = 0x0004
Return TV_SetExStyle(HTV, 0x0004, Set ? 0x0004 : 0)
}
TV_FadeInOutExpandos(HTV, Set := True) {
; TVS_EX_FADEINOUTEXPANDOS = 0x0040
Return TV_SetExStyle(HTV, 0x0040, Set ? 0x0040 : 0)
}
TV_RichToolTips(HTV, Set := True) {
; TVS_EX_RICHTOOLTIP = 0x0010
Return TV_SetExStyle(HTV, 0x0010, Set ? 0x0010 : 0)
}
TV_SetExStyle(HTV, StyleMask, Style) {
; TVM_SETEXTENDEDSTYLE = 0x0112
If (TV_OSVersion() > 5) {
;SendMessage, 0x112C, %StyleMask%, %Style%, , ahk_id %HTV% ; Old code
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x112C, "Ptr", StyleMask, "Ptr", Style) ; New DLL call
Return (ErrorLevel = "FAIL" ? 0 : !ErrorLevel)
}
Return False
}
TV_OSVersion() {
Static OSVersion := DllCall("GetVersion", "UChar")
Return OSVersion
}
; ==================================================================================================================================
; Drag N Drop Functions
; ==================================================================================================================================
; IDropTarget user functions
; ==================================================================================================================================
; Gui2 tree view
; ----------------------------------------------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_TV(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Static FO := [2, 1]
SetTimer, TVDragDropTimer, Off
; DropEffect := CheckDropEffect(KeyState, DropEffect)
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
; ToolTip, DropEffect = %DropEffect%
; Support drag & drop.
Gui, Gui2:Default
FolderPath := ""
If (ItemID := TV_HitTest(TVHWND)) { ; dropped on an item
FolderPath := TV_GetPath(ItemID)
If !InStr(FileExist(FolderPath), "d") ; dropped onto a file, so we either have use the parent folder or ignore the event
SplitPath, FolderPath, , FolderPath
}
Else
FolderPath := TVFolder ; super-global TVFolder has been set to the root folder's name when the TreeView was created
; MsgBox, 0, %A_ThisFunc%, %FolderPath% ; for testing
; IDataObject_SHFileOperation will use CF_HDROP directly
; ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect (Copy = 1, Move = 2)
IDataObject_SHFileOperation(pDataObj, FolderPath, DropEffect ^ 3, TargetObject.HWND)
GuiControl, +gTVLabel, %TVHWND%
Return DropEffect
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnEnter_TV(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
If (DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
DragFile := DroppedFiles[1]
SplitPath, DragFile, , , , , DragDrive
SplitPath, TVFolder, , , , , TVDrive
If (DragDrive = TVDrive)
DropEffect := 2
}
TVDropID := 0
TVLastID := TV_GetLastVisible(TVHWND)
TV_GetClientSize(TVHWND, TVClientW, TVClientH)
TVItemHeight := TV_GetItemHeight(TVHWND) ;;;;from old TVDragDrop label
GuiControl, -g, %TVHWND%
SetTimer, TVDragDropTimer, 10
}
Return DropEffect
}
IDropTargetOnEnter_RB(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Return 2
}
; ----------------------------------------------------------------------------------------------------------------------------------
; IDropTargetOnOver_TV(TargetObject, KeyState, X, Y, DropEffect) {
; Return DropEffect
; }
IDropTargetOnOver_RB(TargetObject, KeyState, X, Y, DropEffect) {
Return 2
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnLeave_TV(TargetObject) {
SetTimer, TVDragDropTimer, Off
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
GuiControl, +gTVLabel, %TVHWND%
Return 0 ; S_OK
}
; ----------------------------------------------------------------------------------------------------------------------------------
; Gui1 buttons
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_BTN(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Global ERFolder, NMFolder, RSFolder, SRFolder, BUFolder
; Support drag & drop.
GuiControlGet, BtnName, Name, % TargetObject.HWND
FolderPath := %BtnName%Folder
; MsgBox, 0, %A_ThisFunc%, %FolderPath% ; for testing
; IDataObject_SHFileOperation will use CF_HDROP directly
; ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect (Copy = 1, Move = 2)
IDataObject_SHFileOperation(pDataObj, FolderPath, DropEffect ^ 3, TargetObject.HWND)
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnEnter_BTN(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Global ERFolder, NMFolder, RSFolder, SRFolder, BUFolder
If (DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
DragFile := DroppedFiles[1]
GuiControlGet, BtnName, Name, % TargetObject.HWND
BtnFolder := %BtnName%Folder
SplitPath, DragFile, , , , , DragDrive
SplitPath, BtnFolder, , , , , BtnDrive
If (DragDrive = BtnDrive)
DropEffect := 2
Else
DropEffect := 2
}
}
Return DropEffect
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_RB(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
For Each, FilePath In DroppedFiles
FileRecycle, %FilePath%
; Msg .= FilePath . "`r`n" ; for testing
; ToolTip, %A_ThisFunc%:`r`n%Msg% ; for testing
; Return 2
}
Return 0
}
; ==================================================================================================================================
; Drag-and-drop includes
; ==================================================================================================================================
#Include *i DoDragDrop.ahk
#Include *i IDropTarget.ahk
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi just me,just me wrote:Nobody can contribute?
zcooler pointed me to this thread elsewhere. There's a lot I've not done, but this should be enough to get IDragSourceHelper::FromBitmap working using the file represented by the clipboard, as per your example, just me. S_FAIL was being returned because the IDropSource's vtable wasn't being properly created.
Here's the files I've modified (minor modifications to everything but IDropSource.ahk), based on the files from just me's GitHub and with replacement/addition of the files in JM's post above the one I quoted.
Code: Select all
; ==================================================================================================================================
; DoDragDrop -> msdn.microsoft.com/en-us/library/ms678486(v=vs.85).aspx
; Requires: IDataObject.ahk, IDropSource.ahk
; ==================================================================================================================================
; Carries out an OLE drag and drop operation using the current contents of the clipboard.
; Return values:
; If the data have been dropped successfully, the functions returns the performed drop operation (i.e. 1 for
; DROPEFFECT_COPY or 2 for DROPEFFECT_MOVE). In all other cases the function returns 0.
; If DROPEFFECT_MOVE is returned, the drag source should remove the data.
; ==================================================================================================================================
DoDragDrop() {
; DRAGDROP_S_DROP = 0x40100
Static DropEffects := 0x03 ; DROPEFFECT_COPY | DROPEFFECT_MOVE
IDropSource_Create(IDS)
If !DllCall("Ole32.dll\OleGetClipboard", "PtrP", pDataObj) {
HBITMAP := IDragSourceHelper_LoadImage(A_ScriptDir . "\Test.bmp", 64, 64)
IDSH := IDragSourceHelper_CreateFromBitmap(pDataObj, HBITMAP, 64, 64)
; IDSH := IDragSourceHelper_CreateFromWindow(pDataObj, TVHWND)
RC := DllCall("Ole32.dll\DoDragDrop","Ptr", pDataObj, "Ptr", &IDS, "UInt", DropEffects, "PtrP", Effect, "Int")
If IDataObject_GetPerformedDropEffect(pDataObj, PerformedDropEffect)
Effect := PerformedDropEffect
ObjRelease(pDataObj)
ObjRelease(IDSH)
DllCall("Gdi32.dll\DeleteObject", "Ptr", HBITMAP)
}
IDropSource_Free(IDS)
Return (RC = 0x40100 ? Effect : 0)
}
; ==================================================================================================================================
#Include *i %A_ScriptDir%\IDataObject.ahk
#Include *i %A_ScriptDir%\IDropSource.ahk
; ==================================================================================================================================
Code: Select all
; ==================================================================================================================================
; IDragSourceHelper interface -> msdn.microsoft.com/en-us/library/bb762034(v=vs.85).aspx
; CLSID_DragDropHelper "{4657278A-411B-11D2-839A-00C04FD918D0}"
; IID_IDropSourcetHelper "{DE5BF786-477A-11D2-839D-00C04FD918D0}"
; ==================================================================================================================================
; Initializes the drag-image manager for a windowless control.
; ==================================================================================================================================
IDragSourceHelper_CreateFromBitmap(pDataObj, HBITMAP, Width, Height, ColorKey := 0x00FFFFFF, OffCX := 0, OffCY := 0) {
Static InitializeFromBitmap := A_PtrSize * 3
VarSetCapacity(SHDI, 32, 0) ; SHDRAGIMAGE structure, 64-bit size
NumPut(Width, SHDI, 0, "Int")
NumPut(Height, SHDI, 4, "Int")
NumPut(OffCX, SHDI, 8, "Int")
NumPut(OffCY, SHDI, 12, "Int")
NumPut(20, SHDI, 8, "Int")
NumPut(20, SHDI, 12, "Int")
NumPut(HBITMAP, SHDI, 16, "UPtr")
NumPut(ColorKey, SHDI, 16 + A_PtrSize, "UInt")
If (pIDSH := _IDragSourceHelper_GetAppropriateObject()) {
pVTBL := NumGet(pIDSH + 0, "UPtr")
DllCall(NumGet(pVTBL + 0, InitializeFromBitmap, "UPtr"), "Ptr", pIDSH, "Ptr", &SHDI, "Ptr", pDataObj, "Int")
Return pIDSH
}
Return False
}
; ==================================================================================================================================
; Initializes the drag-image manager for a control with a window.
; ==================================================================================================================================
IDragSourceHelper_CreateFromWindow(pDataObj, HWND, OffCX := 0, OffCY := 0) {
Static InitializeFromWindow := A_PtrSize * 4
If (pIDSH := _IDragSourceHelper_GetAppropriateObject()) {
pVTBL := NumGet(pIDSH + 0, "UPtr")
DllCall(NumGet(pVTBL + 0, InitializeFromWindow, "UPtr"), "Ptr", pIDSH, "Ptr", HWND, "Ptr", 0, "Ptr", pDataObj, "Int")
Return pIDSH
}
Return False
}
; ==================================================================================================================================
; Auxiliary functions:
; ==================================================================================================================================
IDragSourceHelper_LoadImage(ImagePath, W := 0, H := 0) {
HBITMAP := 0
GDIPModule := DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
VarSetCapacity(SI, 24, 0)
NumPut(1, SI, 0, "UInt")
DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", GDIPToken, "Ptr", &SI, "Ptr", 0)
DllCall("Gdiplus.dll\GdipCreateBitmapFromFile", "WStr", ImagePath, "PtrP", GDIPBitmap)
DllCall("Gdiplus.dll\GdipGetImageWidth", "Ptr", GDIPBitmap, "UIntP", PW)
DllCall("Gdiplus.dll\GdipGetImageHeight", "Ptr", GDIPBitmap, "UIntP", PH)
DllCall("Gdiplus.dll\GdipCreateHBITMAPFromBitmap", "Ptr", GDIPBitmap, "PtrP", HBITMAP, "UInt", 0xFFFFFFFF)
DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", GDIPBitmap)
DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GDIPToken)
DllCall("FreeLibrary", "Ptr", GDIPModule)
If (W = 0)
W := PW
If (H = 0)
H := PH
If (W <> PW) || (H <> PH)
HBITMAP := DllCall("CopyImage", "Ptr", HBITMAP, "UInt", 0, "Int", W, "Int", H, "UInt", 0x800A, "UPtr") ; 0x200A
Return HBITMAP
}
; ==================================================================================================================================
; Private functions:
; ==================================================================================================================================
_IDragSourceHelper_GetAppropriateObject()
{
; This just returns IDSH1; on Vista and later, ComObjQuery for IDragSourceHelper2 appears to work
return ComObjCreate("{4657278A-411B-11D2-839A-00C04FD918D0}", "{DE5BF786-477A-11D2-839D-00C04FD918D0}")
}
Code: Select all
; ==================================================================================================================================
; IDropSource interface -> msdn.microsoft.com/en-us/library/ms690071(v=vs.85).aspx
; Note: Right-drag is not supported as yet!
; ==================================================================================================================================
IDropSource_Create(ByRef IDropSource) {
Static Methods := ["QueryInterface", "AddRef", "Release", "QueryContinueDrag", "GiveFeedback"]
Static VTBL
if (!VarSetCapacity(VTBL)) {
VarSetCapacity(VTBL, Methods.Length() * A_PtrSize)
For Index, Method In Methods {
CB := RegisterCallback("IDropSource_" . Method, "")
NumPut(CB, VTBL, (Index-1) * A_PtrSize, "UPtr")
}
}
VarSetCapacity(IDropSource, A_PtrSize)
NumPut(&VTBL, IDropSource, "UPtr")
}
; ----------------------------------------------------------------------------------------------------------------------------------
; Assuming VTBL is kept static (which I'm doing because of possible scope issues, and the freeing ones that arise from that), this wouldn't work well if multiple
; IDropSources are present
IDropSource_Free(IDropSource) {
RETURN
While (CB := NumGet(IDropSource + (A_PtrSize * A_Index), "Ptr"))
DllCall("GlobalFree", "Ptr", CB)
NumPut(0, IDropSource + 0, "UPtr")
}
; ==================================================================================================================================
; The following functions must not be called directly, they are reserved for internal and system use.
; ==================================================================================================================================
IDropSource_QueryInterface(IDropSource, RIID, PPV) {
; IUnknown -> msdn.microsoft.com/en-us/library/ms682521(v=vs.85).aspx
; IDropSource -> msdn.microsoft.com/en-us/library/windows/desktop/ms690071(v=vs.85).aspx
static IID_IUnknown, IID_IDropSource
if (!VarSetCapacity(IID_IUnknown))
IDropSource_GuidFromStr("{00000000-0000-0000-C000-000000000046}", IID_IUnknown)
,IDropSource_GuidFromStr("{00000121-0000-0000-C000-000000000046}", IID_IDropSource)
If (IDropSource_IsEqualIID(RIID, &IID_IDropSource) || IDropSource_IsEqualIID(RIID, &IID_IUnknown)) {
NumPut(IDropSource, PPV + 0, "Ptr")
Return 0 ; S_OK
}
Else {
NumPut(0, PPV + 0, "Ptr")
Return 0x80004002 ; E_NOINTERFACE
}
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_AddRef(IDropSource) {
; IUnknown -> msdn.microsoft.com/en-us/library/ms691379(v=vs.85).aspx
; Reference counting is not needed in this case.
Return 1
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_Release(IDropSource) {
; IUnknown -> msdn.microsoft.com/en-us/library/ms682317(v=vs.85).aspx
; Reference counting is not needed in this case.
Return 0
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_QueryContinueDrag(IDropSource, fEscapePressed, grfKeyState) {
; QueryContinueDrag -> msdn.microsoft.com/en-us/library/ms690076(v=vs.85).aspx
; DRAGDROP_S_CANCEL : S_OK : DRAGDROP_S_DROP
Return (fEscapePressed ? 0x40101 : (grfKeyState & 0x01) ? 0 : 0x40100)
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_GiveFeedback(IDropSource, dwEffect) {
; GiveFeedback -> msdn.microsoft.com/en-us/library/ms693723(v=vs.85).aspx
Return 0x40102 ; DRAGDROP_S_USEDEFAULTCURSORS
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_IsEqualIID(rguid1, rguid2) {
Static IsEqualGUID := DllCall("GetProcAddress", "Ptr", DllCall("GetModuleHandle", "Str", "ole32", "Ptr"), "AStr", "IsEqualGUID", "Ptr")
Return DllCall(IsEqualGUID, "Ptr", rguid1, "Ptr", rguid2)
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropSource_GuidFromStr(sGuid, ByRef pGuid) {
If (VarSetCapacity(pGuid) < 16)
VarSetCapacity(pGuid, 16)
Return DllCall("ole32\CLSIDFromString", "WStr", sGuid, "Ptr", &pGuid) == 0 ? &pGuid : 0
}
; ==================================================================================================================================
#Include *i %A_ScriptDir%\IDragSourceHelper.ahk
; ==================================================================================================================================
Code: Select all
#NoEnv
#SingleInstance, force
SetBatchLines, -1 ; important for performance
SetWinDelay, -1 ; important for Dock()
; ==================================================================================================================================
; Media folders
RSFolder := "C:\1\Kaaris-Or_Noir_Part_II-FR-Reissue-2CD-FLAC-2014-Mrflac\CD1" ; Set the folder path here...easily better test if the folder has sub-folders.
RSCaption := "DRAGnDROP Interface Test - Set some folder which this button should display!"
; WatchFolder settings
WatchFolders=%RSFolder%* ; *=recurse subfolders
; ==================================================================================================================================
; Global variables for Dock() replacement
Global Gui1X, Gui1Y, Gui2X, Gui2Y, HGUI1, HGUI2
Global Gui2Visible := False
; Global variables for the TreeView
Global Logging := False ; set to True to enable logging within the ReportFunction
Global TVHWND ; handle of the TreeView control, filled later
Global TVFolder ; name of the current root folder, filled later
Global TVRootID ; ID of the current root node, filled later
Global TVDragID := 0 ; ID of the currently dragged item +++++
Global TVDropID := 0 ; ID of the item on which a drop ocurred +++++
Global TVLastID ; ID of the last visible item +++++
Global TVClientW ; the width of the TreeView control's client area, calculated later
Global TVClientH ; the height of the TreeView control's client area, calculated later
Global TVDeltaW ; delta to add to the width of the TreeView control, calculated later
Global TVDeltaH ; delta to add to the height of the TreeView control, calculated later
Global TVItemHeight ; height of all tree view items, calculated later
Global TVMaxHeight ; maximum height of the TreeView control, calculated later
Global TVMaxRows := 20 ; maximum rows shown in the TreeView control
Global TVMinWidth := 300 ; minimum width of the TreeView control - not implemented yet
Global TVMaxWidth := 600 ; maximum width of the TreeView control - not implemented yet
;========================================================================================
; GUI 1
Gui, Gui1:New, +HwndHGUI1 +LabelGui1
Gui, Margin, 10, 10
Gui, Add, Button, ym gGetFiles vRS hwndHRSBTN, %RSCaption% ;+++ added hwnd option
IDT_RS := IDropTarget_Create(HRSBTN, "_BTN") ;+++ Create a drop target for the buttons
Gui, Show, x400 y35, Some GUI
; ==================================================================================================================================
; GUI 2
Gui, Gui2:New, +HwndHGUI2 +OwnerGui1 -Caption +0x02000000 ; WS_CLIPCHILDREN = 0x02000000 is needed in this case ;
Gui, Margin, 0, 0
Gui, Color, 000000
Gui, Font, s9 w800, Ebrima ; Segoe UI Symbol Arial Tahoma
; Create the TreeView
Gui, Add, TreeView, r%TVMaxRows% w650 hwndTVHWND +0x0800 +0x200 Backgroundf2f2f2 -HScroll AltSubmit gTVLabel vTVVar -E0x0200 +E0x020000
; Calculate the deltas between the TreeView's non-client and client area
GuiControlGet, TV, Pos, TVVar
TVMaxHeight := TVH
TVDeltaH := Mod(TVMaxHeight, TVMaxRows)
TVDeltaW := TVDeltaH
; Set Vista+ theme and styles
TV_SetExplorerTheme(TVHWND, "R")
TV_DoubleBuffer(TVHWND)
ImageListID := IL_Create(12)
Loop 12 {
IL_Add(ImageListID, "shell32.dll", A_Index)
}
TV_SetImageList(ImageListID)
; Add a faked StatusBar
Gui, Add, Text, xp y+0 wp h20 +0x200 cWhite hwndTVHSB vSBVar +E0x020000
IDT_TV := IDropTarget_Create(TVHWND, "_TV") ;+++ Create a drop target for the TreeView
; ==================================================================================================================================
; Message handlers and monitoring
; Watched folders
Loop, Parse, WatchFolders,|
{
SubTree := (SubStr(A_LoopField, StrLen(A_LoopField)) = "*")
WatchFolder(RTrim(A_LoopField, "*"), "ReportFunction", SubTree, Watch := 19)
}
; Dock message handler
OnMessage(0x0003, "Dock") ; WM_MOVE = 0x0003
; Keyboard Nav handler
OnMessage(0x100, "OnKeyDown")
Return
; ======================================================================================================================
; Gui1 labels
; ======================================================================================================================
Gui1Close:
IDT_TV.RevokeDragDrop()
ExitApp
; ======================================================================================================================
; Gui2 labels
; ======================================================================================================================
GetFiles:
IsKEvent := true
WinGetPos, Gui1X, Gui1Y, , , ahk_id %HGUI1%
GuiControlGet, HCTL, Hwnd, %A_GuiControl%
WinGetPos, CtlX, CtlY, CtlW, CtlH, ahk_id %HCTL%
Gui, Gui2:Default
Gui, Hide
Gui2Visible := False
GuiControl, -g, TVVar
TV_Delete()
TVFolder := %A_GuiControl%Folder
ItemCount := AddFoldersAndFilesToTree(TVFolder) ; Populate treeview call
TVRootID := TV_GetNext()
TV_Modify(TVRootID, "Select VisFirst Expand")
TVW := TVH := 0
GuiControlGet, TV, Pos, TVVar
GuiControl, Move, SBVar, % "y" . (TVY + TVH) . " w" . TVW
GuiControl, , SBVar, % " " . ItemCount - 1 . " objects."
X := CtlX
Y := CtlY + CtlH
Gui, Show, x%X% y%Y% AutoSize
WinGetPos, Gui2X, Gui2Y, , , ahk_id %HGUI2%
GuiControl, +gTVLabel, TVVar
Gui2Visible := True
Return
; ----------------------------------------------------------------------------------------------------------------------
;+++ The GuiDropFiles label for Gui2 has to be removed. All code has to be moved to the IDropTargetOnDrop user function.
; ======================================================================================================================
; Common subroutines to Gui2
; ======================================================================================================================
TVLabel:
Critical ; try to forgo the use of Critical if possible
If (A_GuiEvent = "+") || (A_GuiEvent = "-") {
GuiControl, -Redraw, TVVar
GuiControlGet, TV, Pos, TVVar
Gui, Show, AutoSize
GuiControl, +Redraw, TVVar
}
Else If (A_GuiEvent = "Normal") && (A_EventInfo = TV_HitTest(TVHWND)) { ; in this case the item will be selected definitely
ItemID := A_EventInfo
Gosub, TVLabelSelect
}
Else If (A_GuiEvent == "D") { ;+++ Only left dragging is supported, changed
TVDragID := A_EventInfo
GoSub, TVDragDrop
}
Else
IsKEvent := A_GuiEvent == "K" ? True : False
Return
;------------------------------------------------------------------------------
TVLabelSelect:
TV_GetText(item, ItemID)
FileName := item
ItemID2 := ItemID
while ParentID := TV_GetParent(ItemID2)
{
TV_GetText(item, ParentID)
FileName := item "\" FileName
ItemID2 := ParentID
}
if (IsMovie := InStr(FileExist(FileName), "D") ? false : true)
RunWait, Open %FileName%
else {
GuiControl, -g, TVVar
TV_Modify(ItemID, TV_Get(ItemID, "Expand") ? "-Expand" : "Expand")
GuiControl, -Redraw, TVVar
GuiControlGet, TV, Pos, TVVar
Gui, Show, AutoSize
GuiControl, +Redraw, TVVar
GuiControl, +gTVLabel, TVVar
}
return
;------------------------------------------------------------------------------
TVDragDrop:
TVDragPath := TV_GetPath(TVDragID)
TVDropID := 0
TVLastID := TV_GetLastVisible(TVHWND)
TV_GetClientSize(TVHWND, TVClientW, TVClientH)
TVItemHeight := TV_GetItemHeight(TVHWND) ;;;;from old TVDragDrop label
If (IsCritical := A_IsCritical)
Critical, Off
GuiControl, -g, TVVar ;;;;from old TVDragDrop label
;DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0009, "Ptr", 0) ; TVM_SELECTITEM, TVGN_CARET (remove the selection)
;+++ TVDragDropRunning := True v ; deprecated
;SavedClipboard := ClipBoardAll
;ClipboardSetFiles(TVDragPath, "")
;SB_SetText(" Dragging ...")
DropEffect := DoDragDrop()
;SB_SetText(" DropEffect: " . {0: "NONE", 1: "COPY", 2: "MOVE"}[DropEffect])
SetTimer, TVDragDropTimer, Off
TVDragID := 0 ;+++
TVDropID := 0 ;+++
;+++ Sleep, 10 ; deprecated
;+++ If (DropEffect = 0) ; deprecated
;+++ TVDragDropRunning := False ; deprecated
;ClipBoard := SavedClipboard
;DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
GuiControl, +gTVLabel, TVVar ;;;;from old TVDragDrop label
If (IsCritical)
Critical, %IsCritical%
Return
;------------------------------------------------------------------------------
TVDragDropTimer:
MouseItemID := TV_HitTest(TVHWND, 0x0026)
If (MouseItemID = 0) {
TVDropID := 0
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
}
Else If (MouseItemID <> TVDropID) {
TVDropID := MouseItemID
HoverTickCount := ScrollTickCount := A_TickCount
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", TVDropID) ; TVM_SELECTITEM, TVIS_DROPHILITED
}
Else {
If ((A_TickCount - HoverTickCount) >= 1000) {
VarSetCapacity(TVIX, 40 + (A_PtrSize * 5), 0)
NumPut(0x0048, TVIX, 0, "UInt") ; TVIF_CHILDREN | TVIF_STATE
NumPut(TVDropID, TVIX, A_PtrSize, "UPtr")
If DllCall("SendMessage", "Ptr", TVHWND, "UInt", A_IsUnicode ? 0x113E : 0x110C, "Ptr", 0, "Ptr", &TVIX, "Int")
&& (NumGet(TVIX, 20 + (A_PtrSize * 3), "Int") = 1) && !(NumGet(TVIX, A_PtrSize * 2, "Uint") & 0x0020) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x1102, "Ptr", 0x0002, "Ptr", TVDropID, "Int") ; TVM_EXPAND
HoverTickCount := A_TickCount
If (TVDropID = TVLastID)
TVLastID := TV_GetLastVisible(TVHWND)
}
}
If ((A_TickCount - ScrollTickCount) >= 100) { ; 100 ms seems to be sufficient here for scrolling
If (TVDropID = TV_GetFirstVisible(TVHWND)) && (TVDropID <> TVRootID) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 0, "Ptr", 0) ; WM_VSCROLL
TVDropID := 0
HoverTickCount := A_TickCount
}
Else {
TV_GetItemRect(TVHWND, TVDropID, X, Y, W, H)
If (TVDropID = TVLastID) {
If ((Y + H) > TVClientH)
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 1, "Ptr", 0) ; WM_VSCROLL
}
Else If ((Y + H + TVItemHeight) > TVClientH) {
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x0115, "Ptr", 1, "Ptr", 0) ; WM_VSCROLL
TVDropID := 0
HoverTickCount := A_TickCount
}
}
ScrollTickCount := A_TickCount
}
}
Return
;----------------------------------------------------------------------------------------------------------
;-FUNCTIONS------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------------
AddFoldersAndFilesToTree(Folder, ParentID := 0, First := 0) {
;msgbox % First
SplitPath, Folder, FolderName
If (ParentID = 0)
ID := TV_Add(Folder, ParentID, "Icon7 Expand") ; ID gets a value
Else If (First = 0)
ID := TV_Add(FolderName, ParentID, "Icon7") ; ID gets a value
Else If (First = 1)
ID := TV_Add(FolderName, ParentID, "First Icon7") ; ID gets a value
; -------------------------------------------------------------------------------------------------------------------------------
FolderList =
Loop, %Folder%\*.*, 2
FolderList .= (A_Index = 1 ? "" : "`n") A_LoopFileTimeModified "|" A_LoopFileName "|" A_LoopFileLongPath
; Sort folder options If recurse
If (Settings.SortRev = "1") ; Sort folders reverse enabled
Sort, FolderList, F ReverseDirection D`n, ; Reverses the list so that it contains 4,3,2,1
If (Settings.SortTimeRev = "1") ; Sort folders timereverse enabled
Sort, FolderList, F SortFunc ; Sort folders TimeModified, newest on top to oldest in bottom
Loop, Parse, FolderList, `n
AddFoldersAndFilesToTree(StrSplit(A_LoopField, "|").3, ID)
; -------------------------------------------------------------------------------------------------------------------------------
FileList := ""
Loop, % Folder . "\*.*", 0
FileList .= (FileList <> "" ? "`n" : "") . A_LoopFileTimeCreated . "|" . A_LoopFileName . "|" . A_LoopFileTimeModified
; -------------------------------------------------------------------------------------------------------------------------------
Sort, FileList, F ReverseDirection D`n, ; Reverses the list so that it contains 4,3,2,1
; -------------------------------------------------------------------------------------------------------------------------------
Loop, Parse, FileList, `n
{
Split := StrSplit(A_LoopField, "|") ;+++ to avoid repeated calls of StrSplit()
If (Split.1 = Split.3) ; recording isn't finished yet
TV_Add(Split.2, ID, "Icon4")
Else ; recording is finished
TV_Add(Split.2, ID, "Icon2")
}
Return TV_GetCount()
}
; ==================================================================================================================================
; Auxiliary functions ==============================================================================================================
; ==================================================================================================================================
SortFunc(_line1, _line2)
{
line1 := StrSplit(_line1, "|")
line2 := StrSplit(_line2, "|")
if (line1[1] < line2[1]) ; sort in descending order
;msgbox % line1[1] " " line2[1] "`ndescending"
return 1
if (line1[1] = line2[1])
;msgbox % line1[1] "`nascending"
if (line1[2] > line2[2]) ; sort in ascending order
return 1
return -1
}
ReverseDirection(a1, a2, offset)
{
return offset ; Offset is positive if a2 came after a1 in the original list; negative otherwise.
}
;--------------------------------------------------------------------------------
; Docking Guis
Dock(W, L, M, H) { ; <<<<< added Dock replacement
If (H = HGUI1) && Gui2Visible {
WinGetPos, X, Y, , , ahk_id %HGUI1%
Gui2X += X - Gui1X
Gui2Y += Y - Gui1Y
Gui1X := X
Gui1Y := Y
WinMove, ahk_id %HGUI2%, , %Gui2X%, %Gui2Y%
}
}
; ==================================================================================================================================
; Explorer function for Drag&Drop and Pasting. Enables the explorer paste context menu option.
; ==================================================================================================================================
ClipboardSetFiles(FilesToSet, DropEffect := "Copy") {
Static TCS := A_IsUnicode ? 2 : 1 ; size of a TCHAR
Static PreferredDropEffect := DllCall("RegisterClipboardFormat", "Str", "Preferred DropEffect")
Static DropEffects := {1: 1, 2: 2, Copy: 1, Move: 2}
; -------------------------------------------------------------------------------------------------------------------
; Count files and total string length
TotalLength := 0
FileArray := []
Loop, Parse, FilesToSet, `n, `r
{
If (Length := StrLen(A_LoopField))
FileArray.Push({Path: A_LoopField, Len: Length + 1})
TotalLength += Length
}
FileCount := FileArray.Length()
If !(FileCount && TotalLength)
Return
; -------------------------------------------------------------------------------------------------------------------
; Add files to the clipboard
If DllCall("OpenClipboard", "Ptr", A_ScriptHwnd) && DllCall("EmptyClipboard") {
; HDROP format ---------------------------------------------------------------------------------------------------
; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
hPath := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 20 + (TotalLength + FileCount + 1) * TCS, "UPtr")
pPath := DllCall("GlobalLock", "Ptr" , hPath)
Offset := 20
NumPut(Offset, pPath + 0, "UInt") ; DROPFILES.pFiles = offset of file list
NumPut(!!A_IsUnicode, pPath + 16, "UInt") ; DROPFILES.fWide = 0 --> ANSI, fWide = 1 --> Unicode
For Each, File In FileArray
Offset += StrPut(File.Path, pPath + Offset, File.Len) * TCS
DllCall("GlobalUnlock", "Ptr", hPath)
DllCall("SetClipboardData","UInt", 0x0F, "UPtr", hPath) ; 0x0F = CF_HDROP
; Preferred DropEffect format ------------------------------------------------------------------------------------
If (DropEffect := DropEffects[DropEffect]) {
; Write Preferred DropEffect structure to clipboard to switch between copy/cut operations
; 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)
DllCall("SetClipboardData", "UInt", PreferredDropEffect, "Ptr", hMem)
}
DllCall("CloseClipboard")
}
Return
}
;--------------------------------------------------------------------------------------
; Clipboard function. Retrieves if files in clipboard comes from an explorer cut or copy operation.
; explorer copy = 5, explorer cut = 2
ClipboardGetDropEffect() {
Static PreferredDropEffect := DllCall("RegisterClipboardFormat", "Str" , "Preferred DropEffect")
DropEffect := 0
If DllCall("IsClipboardFormatAvailable", "UInt", PreferredDropEffect) {
If DllCall("OpenClipboard", "Ptr", 0) {
hDropEffect := DllCall("GetClipboardData", "UInt", PreferredDropEffect, "UPtr")
pDropEffect := DllCall("GlobalLock", "Ptr", hDropEffect, "UPtr")
DropEffect := NumGet(pDropEffect + 0, 0, "UChar")
DllCall("GlobalUnlock", "Ptr", hDropEffect)
DllCall("CloseClipboard")
}
}
Return DropEffect
}
;------------reverse logging function------------------------------------------------------------------------------
AddToBOF(FileName, String) { ; AddToBeginOfFile
If (File := FileOpen(FileName, "rw", "UTF-8-RAW")) {
Content := File.Read()
File.Pos := 0
File.Write(String . Content)
File.Close()
Return True
}
Return False
}
; ==================================================================================================================================
; Function: Notifies about changes within folders.
; This is a rewrite of HotKeyIt's WatchDirectory() released at
; http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with: AHK 1.1.23.01 (A32/U32/U64)
; Tested on: Win 10 Pro x64
; Usage: WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
; Folder - The full qualified path of the folder to be watched.
; Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
; Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
; If not, it will be done internally on exit.
; UserFunc - The name of a user-defined function to call on changes. The function must accept at least two parameters:
; 1: The path of the affected folder. The final backslash is not included even if it is a drive's root
; directory (e.g. C:).
; 2: An array of change notifications containing the following keys:
; Action: One of the integer values specified as FILE_ACTION_... (see below).
; In case of renaming Action is set to FILE_ACTION_RENAMED (4).
; Name: The full path of the changed file or folder.
; OldName: The previous path in case of renaming, otherwise not used.
; IsDir: True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
; Pass the string "**DEL" to remove the directory from the list of watched folders.
; SubTree - Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
; Default: False - sub-folders aren't watched.
; Watch - The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
; values specified below.
; Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
; Returns True on success; otherwise False.
; Change history:
; 1.0.01.00/2016-03-14/just me - bug-fix for multiple folders
; 1.0.00.00/2015-06-21/just me - initial release
; License:
; The Unlicense -> http://unlicense.org/
; Remarks:
; Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
; folders simultaneously.
; MSDN:
; ReadDirectoryChangesW msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
; FILE_NOTIFY_CHANGE_FILE_NAME = 1 (0x00000001) : Notify about renaming, creating, or deleting a file.
; FILE_NOTIFY_CHANGE_DIR_NAME = 2 (0x00000002) : Notify about creating or deleting a directory.
; FILE_NOTIFY_CHANGE_ATTRIBUTES = 4 (0x00000004) : Notify about attribute changes.
; FILE_NOTIFY_CHANGE_SIZE = 8 (0x00000008) : Notify about any file-size change.
; FILE_NOTIFY_CHANGE_LAST_WRITE = 16 (0x00000010) : Notify about any change to the last write-time of files.
; FILE_NOTIFY_CHANGE_LAST_ACCESS = 32 (0x00000020) : Notify about any change to the last access time of files.
; FILE_NOTIFY_CHANGE_CREATION = 64 (0x00000040) : Notify about any change to the creation time of files.
; FILE_NOTIFY_CHANGE_SECURITY = 256 (0x00000100) : Notify about any security-descriptor change.
; FILE_NOTIFY_INFORMATION msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
; FILE_ACTION_ADDED = 1 (0x00000001) : The file was added to the directory.
; FILE_ACTION_REMOVED = 2 (0x00000002) : The file was removed from the directory.
; FILE_ACTION_MODIFIED = 3 (0x00000003) : The file was modified.
; FILE_ACTION_RENAMED = 4 (0x00000004) : The file was renamed (not defined by Microsoft).
; FILE_ACTION_RENAMED_OLD_NAME = 4 (0x00000004) : The file was renamed and this is the old name.
; FILE_ACTION_RENAMED_NEW_NAME = 5 (0x00000005) : The file was renamed and this is the new name.
; GetOverlappedResult msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
; CreateFile msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
; FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
; FILE_FLAG_OVERLAPPED = 0x40000000
; ==================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
Static TimerID := "**" . A_TickCount
Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
Static MAXIMUM_WAIT_OBJECTS := 64
Static MAX_DIR_PATH := 260 - 12 + 1
Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
Static SizeOfOVL := 32 ; size of the OVERLAPPED structure (64-bit)
Static WatchedFolders := {}
Static EventArray := []
Static HandleArray := []
Static WaitObjects := 0
Static BytesRead := 0
Static Paused := False
; ===============================================================================================================================
If (Folder = "")
Return False
SetTimer, % TimerFunc, Off
RebuildWaitObjects := False
; ===============================================================================================================================
If (Folder = TimerID) { ; called by timer
If (ObjCount := EventArray.Length()) && !Paused {
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
FolderName := WatchedFolders[ObjIndex + 1]
D := WatchedFolders[FolderName]
If DllCall("GetOverlappedResult", "Ptr", D.Handle, "Ptr", D.OVLAddr, "UIntP", BytesRead, "Int", True) {
Changes := []
FNIAddr := D.FNIAddr
FNIMax := FNIAddr + BytesRead
OffSet := 0
PrevIndex := 0
PrevAction := 0
PrevName := ""
Loop {
FNIAddr += Offset
OffSet := NumGet(FNIAddr + 0, "UInt")
Action := NumGet(FNIAddr + 4, "UInt")
Length := NumGet(FNIAddr + 8, "UInt") // 2
Name := FolderName . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
IsDir := InStr(FileExist(Name), "D") ? 1 : 0
If (Name = PrevName) {
If (Action = PrevAction)
Continue
If (Action = 1) && (PrevAction = 2) {
PrevAction := Action
Changes.RemoveAt(PrevIndex--)
Continue
}
}
If (Action = 4)
PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
Else If (Action = 5) && (PrevAction = 4) {
Changes[PrevIndex, "Name"] := Name
Changes[PrevIndex, "IsDir"] := IsDir
}
Else
PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
PrevAction := Action
PrevName := Name
} Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
If (Changes.Length() > 0)
D.Func.Call(FolderName, Changes)
DllCall("ResetEvent", "Ptr", EventArray[D.Index])
DllCall("ReadDirectoryChangesW", "Ptr", D.Handle, "Ptr", D.FNIAddr, "UInt", SizeOfFNI, "Int", D.SubTree
, "UInt", D.Watch, "UInt", 0, "Ptr", D.OVLAddr, "Ptr", 0)
}
ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
Sleep, 0
}
}
}
; ===============================================================================================================================
Else If (Folder = "**PAUSE") { ; called to pause/resume watching
Paused := !!UserFunc
RebuildObjects := Paused
}
; ===============================================================================================================================
Else If (Folder = "**END") { ; called to stop watching
For D In WatchedFolders
If D Is Not Integer
DllCall("CloseHandle", "Ptr", D.Handle)
For Each, Event In EventArray
DllCall("CloseHandle", "Ptr", Event)
WatchedFolders := {}
EventArray := []
Paused := False
Return True
}
; ===============================================================================================================================
Else { ; called to add, update, or remove folders
Folder := RTrim(Folder, "\")
VarSetCapacity(LongPath, SizeOfLongPath, 0)
If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", SizeOfLongPath)
Return False
VarSetCapacity(LongPath, -1)
Folder := LongPath
If (WatchedFolders[Folder]) { ; update or remove
Handle := WatchedFolders[Folder, "Handle"]
Index := WatchedFolders[Folder, "Index"]
DllCall("CloseHandle", "Ptr", Handle)
DllCall("CloseHandle", "Ptr", EventArray[Index])
EventArray.RemoveAt(Index)
WatchedFolders.RemoveAt(Index)
WatchedFolders.Delete(Folder)
RebuildWaitObjects := True
}
If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Length() < MAXIMUM_WAIT_OBJECTS) {
If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
, "UInt", 0x42000000, "Ptr", 0, "UPtr")
If (Handle > 0) {
Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
Index := EventArray.Push(Event)
WatchedFolders[Index] := Folder
WatchedFolders[Folder] := {Func: UserFunc, Handle: Handle, Index: Index, SubTree: !!SubTree, Watch: Watch}
WatchedFolders[Folder].SetCapacity("FNIBuff", SizeOfFNI)
FNIAddr := WatchedFolders[Folder].GetAddress("FNIBuff")
DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
WatchedFolders[Folder, "FNIAddr"] := FNIAddr
WatchedFolders[Folder].SetCapacity("OVLBuff", SizeOfOVL)
OVLAddr := WatchedFolders[Folder].GetAddress("OVLBuff")
DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
WatchedFolders[Folder, "OVLAddr"] := OVLAddr
DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
, "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
RebuildWaitObjects := True
}
}
}
If (RebuildWaitObjects) {
VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
OffSet := &WaitObjects
For Index, Event In EventArray
Offset := NumPut(Event, Offset + 0, 0, "Ptr")
}
}
; ===============================================================================================================================
If (EventArray.Length() > 0)
SetTimer, % TimerFunc, -100
Return (RebuildWaitObjects) ; returns True on success, otherwise False
}
; ==================================================================================================================================
; WatchFolder - Reportfunctions
ReportFunction(Directory, Changes) {
Global Dummy_DB_BackupEntry, TooltipInfoUpdater, SRFolder, tooltip_infofiles, IsMovie, Logging, RecycleBin_C, RecycleBin_D, RecycleBin_E, RecycleBin_F, RecycleBin_G
Static LogFile := ""
; -------------------------------------------------------------------------------------------------------------------------------
; Logging
If (LogFile = "")
LogFile := TooltipInfoUpdater . "\wd_changes.log"
If (Logging)
FormatTime, Prefix, , yyyy-MM-dd HH:mm:ss
; -------------------------------------------------------------------------------------------------------------------------------
; Check if the current TreeView might be affected.
TVChanged := False
If (Gui2Visible) {
Gui, Gui2:Default
Gui, TreeView, TVVar
GuiControl, -g, TVVar
GuiControl, -Redraw, TVVar
; ----------------------------------------------------------------------------------------------------------------------------
; Loop through the changes.
For Each, Change In Changes {
Action := Change.Action
Name := Change.Name
OldName := Change.Oldname
IsDir := Change.Isdir
If (Logging)
AddToBOF(LogFile, Prefix . " ReportFunction: Action: " . Action . " - Name: " . Name . " - OldName: " . OldName
. " - IsDir: " . IsDir . "`r`n")
; -------------------------------------------------------------------------------------------------------------------------
; Process action 4 (renamed) at first, because Action might have to be changed and processed later on.
If (Action = 4) {
OldID := FindPath(OldName, OldFoundPath)
If (OldName <> OldFoundPath) ; OldName was not found, so we change Action to 1 (added)
Action := 1 ; will be processed later on
Else { ; OldName has been found, so we have to check if the new name is valid
SplitPath, Name, File, Dir, Ext
If (IsDir) { ; the new name is valid, modify the tree
TVChanged := TV_Modify(OldID, , File)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Renamed " . OldName . " to " . Name . "`r`n")
}
Else { ; modifying is not possible, so we have to delete the entry
TVChanged := TV_Delete(OldID)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Removed " . Name . "`r`n")
}
Continue ; nothing else to do, next change
}
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 3 (modified)
If (Action = 3) {
ItemID := FindPath(Name, FoundPath)
If (!IsDir && (Name = FoundPath)) { ; a file shown in the TreeView has been modified
SplitPath, Name, , , Ext
If Ext In ts,avi,mp4,mkv,wmv,mpg ; it's a movie file, so the icon might need to be updated
{
TVChanged := TV_Modify(ItemID, "Icon2")
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Modified " . Name . "`r`n")
}
}
Continue ; nothing else to do, next change
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 2 (removed)
If (Action = 2) {
ItemID := FindPath(Name, FoundPath)
If (Name = FoundPath) { ; Name was found and must be deleted
TVChanged := TV_Delete(ItemID)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Removed " . Name . "`r`n")
}
Continue ; nothing else to do, next change
}
; -------------------------------------------------------------------------------------------------------------------------
; Action 1 (added)
If (Action = 1) {
ItemID := FindPath(Name, FoundPath)
If (!ItemID || (Name = FoundPath)) ; Name doesn't belong to the TreeView or exists already, nothing to do with it
Continue
SplitPath, Name, File, Dir, Ext
If (IsDir) { ; it's a folder
TVChanged := AddFoldersAndFilesToTree(Name, ItemID, First := 1)
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Added " . Name . "`r`n")
}
Else { ; it's a file
If (FoundPath = Dir) {
If Ext In ts,avi,mp4,mkv,wmv,mpg
{
FileGetTime, Created, %Name%, C
FileGetTime, Modified, %Name%, M
If (Created = Modified) ; File still in recording
TVChanged := TV_Add(File, ItemID, "First Icon4")
Else ; Recording is finished
TVChanged := TV_Add(File, ItemID, "First Icon2")
}
Else ; If FileExt In txt,srt,sub,log
TV_Changed := TV_Add(File, ItemID, "First Icon5")
If (Logging)
AddToBOF(LogFile, Prefix . " TreeView: Added " . Name . "`r`n")
}
}
}
}
If (TVChanged) {
GuiControlGet, TV, Pos, TVVar
GuiControl, , SBVar, % " " . TV_GetCount() . " objects."
Gui, Show, AutoSize ;NoActivate
GuiControl, +Redraw, TVVar
}
Else
GuiControl, +Redraw, TVVar
GuiControl, +gTVLabel, TVVar
}
}
;-----------------------------------------------------------------------------------------------------------------------------------
; FindPath - Find (parts of) the path within the TreeView
FindPath(PathToFind, ByRef FoundPath) {
FoundPath := ""
If !(InStr(PathToFind, TVFolder) = 1) ; wrong parameters - PathToFind does not belong to the root folder
Return 0
FoundPath := TVFolder
PathID := TVRootID
Split := StrSplit(SubStr(PathToFind, StrLen(TVFolder) + 2), "\") ; split the part following the root folder
ItemID := TV_GetChild(PathID)
Loop, % Split.Length() {
Name := Split[A_Index]
While (ItemID) {
TV_GetText(ItemText, ItemID)
If (Name = ItemText) {
PathID := ItemID
FoundPath .= "\" . Name
ItemID := TV_GetChild(ItemID)
Break
}
ItemID := TV_GetNext(ItemID)
}
} Until (ItemID = 0)
Return PathID
}
; ==================================================================================================================================
; Additional TV functions
; ==================================================================================================================================
TV_GetClientSize(HTV, ByRef W, ByRef H) { ; retrieves the client size of the TreeView
W := H := 0
, VarSetCapacity(RC, 16, 0)
If DllCall("GetClientRect", "Ptr", HTV, "Ptr", &RC) {
W := NumGet(RC, 8, "Int")
, H := NumGet(RC, 12, "Int")
Return True
}
Return False
}
TV_GetFirstVisible(HTV) { ; retrieves the first currently visible item of the TreeView
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x110A, "Ptr", 0x0005, "Ptr", 0, "UPtr") ; TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE
}
TV_GetLastVisible(HTV) { ; retrieves the last visible item of the TreeView
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x110A, "Ptr", 0x000A, "Ptr", 0, "UPtr") ; TVM_GETNEXTITEM, TVGN_LASTVISIBLE
}
TV_GetPath(ItemID, Delimiter := "\") {
TV_GetText(ItemPath, ItemID)
While (ItemID := TV_GetParent(ItemID)) {
TV_GetText(ItemText, ItemID)
ItemPath := ItemText . Delimiter . ItemPath
}
Return ItemPath
}
TV_GetItemHeight(HTV) { ; retrieves the height af all TreeView items
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x111C, "Ptr", 0, "Ptr", 0) ; TVM_GETITEMHEIGHT
}
TV_GetItemRect(HTV, ItemID, ByRef X, ByRef Y, ByRef W, ByRef H, TextOnly := True) { ; retrieves the item's position and size
X := 0, Y := 0, W := 0, H := 0
, VarSetCapacity(RC, 16, 0)
, NumPut(ItemID, RC, 0, "UPtr")
If DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1104, "Ptr", !!TextOnly, "Ptr", &RC) { ; TVM_GETITEMRECT
X := NumGet(RC, 0, "Int")
, Y := NumGet(RC, 4, "Int")
, W := NumGet(RC, 8, "Int") - X
, H := NumGet(RC, 12, "Int") - Y
Return True
}
Return False
}
TV_HitTest(HTV, Test := 0x0006, X := "", Y := "") { ; TVM_HITTEST
; TVM_HITTEST = 0x1111 -> msdn.microsoft.com/en-us/library/bb773732(v=vs.85).aspx
; TVHT_NOWHERE 0x0001 - In the client area, but below the last item.
; TVHT_ONITEMICON 0x0002 - On the bitmap associated with an item.
; TVHT_ONITEMLABEL 0x0004 - On the label (string) associated with an item.
; TVHT_ONITEM 0x0006 - On the bitmap or label associated with an item (defined differently in CommCtrl.h).
; TVHT_ONITEMINDENT 0x0008 - In the indentation associated with an item.
; TVHT_ONITEMBUTTON 0x0010 - On the button associated with an item.
; TVHT_ONITEMRIGHT 0x0020 - In the area to the right of an item.
; TVHT_ONITEMSTATEICON 0x0040 - On the state icon for a tree-view item that is in a user-defined state.
; TVHT_ONITEMROW 0x007E - On or right of an item (not defined in CommCtrl.h).
; TVHT_ABOVE 0x0100 - Above the client area.
; TVHT_BELOW 0x0200 - Below the client area.
; TVHT_TORIGHT 0x0400 - To the right of the client area.
; TVHT_TOLEFT 0x0800 - To the left of the client area.
Test &= 0x0FFF
VarSetCapacity(TVHTI, 8 + (A_PtrSize * 2), 0) ; TVHITTESTINFO
If (X = "") || (Y = "") {
DllCall("User32.dll\GetCursorPos", "Ptr", &TVHTI)
, DllCall("User32.dll\ScreenToClient", "Ptr", HTV, "Ptr", &TVHTI)
}
Else {
NumPut(X, TVHTI, 0, "Int")
, NumPut(Y, TVHTI, 4, "Int")
}
If !(ItemID := DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1111, "Ptr", 0, "Ptr", &TVHTI, "UPtr"))
Return 0
Return (NumGet(TVHTI, 8, "UInt") & Test ? ItemID : 0)
}
; Retrieves the rectangle of the item's text area in screen coordinates
TV_GetItemScreenRect(HTV, ItemID, Byref L := 0, ByRef T := 0, ByRef R := 0, ByRef B := 0) {
; TVM_GETITEMRECT = 0x1104 -> msdn.microsoft.com/en-us/library/bb773610(v=vs.85).aspx
L := T := R := B := 0
VarSetCapacity(RC, 16, 0)
NumPut(ItemID, RC, 0, "Int")
If DllCall("SendMessage", "Ptr", HTV, "UInt", 0x1104, "Ptr", True, "Ptr", &RC) { ; get the rectangle
DllCall("MapWindowPoints", "Ptr", HTV, "Ptr", 0, "Ptr", &RC, "UInt", 2) ; convert client to screen coordinates
L := NumGet(RC, 0, "Int")
T := NumGet(RC, 4, "Int")
R := NumGet(RC, 8, "Int")
B := NumGet(RC, 12, "Int")
Return True
}
Return False
}
TV_SetExplorerTheme(HTV, Option := "") {
; Options:
; R - Remove the TVS_HASLINES style using lines to show the hierarchy of items.
; F - Set the TVS_FULLROWSELECT style. The entire row of the selected item is highlighted, and clicking anywhere on an
; item's row causes it to be selected. The TVS_HASLINES style will be removed, also.
If (TV_OSVersion() > 5) {
If (Option = "R") || (Option = "F") {
Control, Style, -0x0002, , ahk_id %HTV%
If (Option = "F")
Control, Style, +0x1000, , ahk_id %HTV%
}
Return !DllCall("UxTheme.dll\SetWindowTheme", "Ptr", HTV, "WStr", "Explorer", "Ptr", 0)
}
Return False
}
TV_AutoHScroll(HTV, Set := True) {
; TVS_EX_AUTOHSCROLL = 0x0020
Return TV_SetExStyle(HTV, 0x0020, Set ? 0x0020 : 0)
}
TV_DoubleBuffer(HTV, Set := True) {
; TVS_EX_DOUBLEBUFFER = 0x0004
Return TV_SetExStyle(HTV, 0x0004, Set ? 0x0004 : 0)
}
TV_FadeInOutExpandos(HTV, Set := True) {
; TVS_EX_FADEINOUTEXPANDOS = 0x0040
Return TV_SetExStyle(HTV, 0x0040, Set ? 0x0040 : 0)
}
TV_RichToolTips(HTV, Set := True) {
; TVS_EX_RICHTOOLTIP = 0x0010
Return TV_SetExStyle(HTV, 0x0010, Set ? 0x0010 : 0)
}
TV_SetExStyle(HTV, StyleMask, Style) {
; TVM_SETEXTENDEDSTYLE = 0x0112
If (TV_OSVersion() > 5) {
;SendMessage, 0x112C, %StyleMask%, %Style%, , ahk_id %HTV% ; Old code
Return DllCall("SendMessage", "Ptr", HTV, "UInt", 0x112C, "Ptr", StyleMask, "Ptr", Style) ; New DLL call
Return (ErrorLevel = "FAIL" ? 0 : !ErrorLevel)
}
Return False
}
TV_OSVersion() {
Static OSVersion := DllCall("GetVersion", "UChar")
Return OSVersion
}
; ==================================================================================================================================
; Drag N Drop Functions
; ==================================================================================================================================
; IDropTarget user functions
; ==================================================================================================================================
; Gui2 tree view
; ----------------------------------------------------------------------------------------------------------------------------------
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_TV(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Static FO := [2, 1]
SetTimer, TVDragDropTimer, Off
; DropEffect := CheckDropEffect(KeyState, DropEffect)
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
; ToolTip, DropEffect = %DropEffect%
; Support drag & drop.
Gui, Gui2:Default
FolderPath := ""
If (ItemID := TV_HitTest(TVHWND)) { ; dropped on an item
FolderPath := TV_GetPath(ItemID)
If !InStr(FileExist(FolderPath), "d") ; dropped onto a file, so we either have use the parent folder or ignore the event
SplitPath, FolderPath, , FolderPath
}
Else
FolderPath := TVFolder ; super-global TVFolder has been set to the root folder's name when the TreeView was created
; MsgBox, 0, %A_ThisFunc%, %FolderPath% ; for testing
; IDataObject_SHFileOperation will use CF_HDROP directly
; ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect (Copy = 1, Move = 2)
IDataObject_SHFileOperation(pDataObj, FolderPath, DropEffect ^ 3, TargetObject.HWND)
GuiControl, +gTVLabel, %TVHWND%
Return DropEffect
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnEnter_TV(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
If (DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
DragFile := DroppedFiles[1]
SplitPath, DragFile, , , , , DragDrive
SplitPath, TVFolder, , , , , TVDrive
If (DragDrive = TVDrive)
DropEffect := 2
}
TVDropID := 0
TVLastID := TV_GetLastVisible(TVHWND)
TV_GetClientSize(TVHWND, TVClientW, TVClientH)
TVItemHeight := TV_GetItemHeight(TVHWND) ;;;;from old TVDragDrop label
GuiControl, -g, %TVHWND%
SetTimer, TVDragDropTimer, 10
}
Return DropEffect
}
IDropTargetOnEnter_RB(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Return 2
}
; ----------------------------------------------------------------------------------------------------------------------------------
; IDropTargetOnOver_TV(TargetObject, KeyState, X, Y, DropEffect) {
; Return DropEffect
; }
IDropTargetOnOver_RB(TargetObject, KeyState, X, Y, DropEffect) {
Return 2
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnLeave_TV(TargetObject) {
SetTimer, TVDragDropTimer, Off
DllCall("SendMessage", "Ptr", TVHWND, "UInt", 0x110B, "Ptr", 0x0008, "Ptr", 0) ; TVM_SELECTITEM, TVIS_DROPHILITED
GuiControl, +gTVLabel, %TVHWND%
Return 0 ; S_OK
}
; ----------------------------------------------------------------------------------------------------------------------------------
; Gui1 buttons
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_BTN(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Global ERFolder, NMFolder, RSFolder, SRFolder, BUFolder
; Support drag & drop.
GuiControlGet, BtnName, Name, % TargetObject.HWND
FolderPath := %BtnName%Folder
; MsgBox, 0, %A_ThisFunc%, %FolderPath% ; for testing
; IDataObject_SHFileOperation will use CF_HDROP directly
; ; FO_MOVE = 1, FO_COPY = 2, so we have to swap the DropEffect (Copy = 1, Move = 2)
IDataObject_SHFileOperation(pDataObj, FolderPath, DropEffect ^ 3, TargetObject.HWND)
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnEnter_BTN(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
Global ERFolder, NMFolder, RSFolder, SRFolder, BUFolder
If (DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
DragFile := DroppedFiles[1]
GuiControlGet, BtnName, Name, % TargetObject.HWND
BtnFolder := %BtnName%Folder
SplitPath, DragFile, , , , , DragDrive
SplitPath, BtnFolder, , , , , BtnDrive
If (DragDrive = BtnDrive)
DropEffect := 2
Else
DropEffect := 2
}
}
Return DropEffect
}
; ----------------------------------------------------------------------------------------------------------------------------------
IDropTargetOnDrop_RB(TargetObject, pDataObj, KeyState, X, Y, DropEffect) {
If IDataObject_GetDroppedFiles(pDataObj, DroppedFiles) {
For Each, FilePath In DroppedFiles
FileRecycle, %FilePath%
; Msg .= FilePath . "`r`n" ; for testing
; ToolTip, %A_ThisFunc%:`r`n%Msg% ; for testing
; Return 2
}
Return 0
}
; ==================================================================================================================================
; Drag-and-drop includes
; ==================================================================================================================================
#Include *i DoDragDrop.ahk
#Include *i IDropTarget.ahk
To those reading: this isn't complete. IDataSource needs to be implemented to move arbitrary things. Currently this is limited to what's on the clipboard (I test by Ctrl-Cing a file before running Untitled.ahk). Also, currently, with IDragSourceHelper, you get this:
With Drag & Drop Images and Drop Descriptions for MFC Applications, you get this:
The CodeProject project has code for setting up a DROPDESCRIPTION and for also using the new cursors. This is far as I'll go because:
- I don't write GUI AHK programs, so I'm already at a disadvantage for best practices etc., especially when compared to the contributors of this thread alone
- Writing easily reusable code isn't my jam, either...
- just me actually appears to know how the clipboard stuff works, unlike me
- I'm hella sleep deprived
- I really want to watch this week's Orphan Black
Last edited by qwerty12 on 28 May 2016, 08:30, edited 2 times in total.
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Wow, this is great stuff Very impressing qwerty12
Best regards
zcooler
Best regards
zcooler
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi qwerty12!
I had hopes Just Me would jump on this and finish off the nicest AHK D&D interface up to date, but he doesnt seem to be motivated to do so. This COM stuff is something of a beast, makes my head spinn of dizyness and I get nowhere. Few folks seem to understand this stuff and since you are one of those I wanna hear with ya if you might be interested to have an another go at it?
To clarify for others that might wanna have a go, your alterations of Just Me`s interface works if Ctrl-C (Copy to clipboard) the file in the Explorer and then drag the same file from the AHK app. Then the dragimage pops-up alright. Although its the file icon showing instead of (if a movie file) the thumbnail. Also getting the DROPDESCRIPTION and the new cursors working along with the little counter image (white digit on a blue background) that pops-up if there are multiple files being D&D would be fantastic.
Regards
zcooler
I had hopes Just Me would jump on this and finish off the nicest AHK D&D interface up to date, but he doesnt seem to be motivated to do so. This COM stuff is something of a beast, makes my head spinn of dizyness and I get nowhere. Few folks seem to understand this stuff and since you are one of those I wanna hear with ya if you might be interested to have an another go at it?
To clarify for others that might wanna have a go, your alterations of Just Me`s interface works if Ctrl-C (Copy to clipboard) the file in the Explorer and then drag the same file from the AHK app. Then the dragimage pops-up alright. Although its the file icon showing instead of (if a movie file) the thumbnail. Also getting the DROPDESCRIPTION and the new cursors working along with the little counter image (white digit on a blue background) that pops-up if there are multiple files being D&D would be fantastic.
Regards
zcooler
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hey zcooler,
I have to be honest here; GUI stuff doesn't interest me, which is why this isn't really holding my attention. I don't know the first thing about making a GUI in AHK and that suits me because I don't have a need for GUI scripts personally.
I have to be honest here; GUI stuff doesn't interest me, which is why this isn't really holding my attention. I don't know the first thing about making a GUI in AHK and that suits me because I don't have a need for GUI scripts personally.
I'm not one of them, either. I knew nothing of COM before AutoHotkey, and even then what I know even now are only the basics. Another issue is that I'm not a programmer, so I'm really not good at producing reusable code. Tell you what, a personal project of mine is to write a simple shell extension in C (can't stand C++, ATL or MFC and all that bollox). If I finish that, I'll try to have a go at this as then I'll be in a far better state to understand more of what's going on. But, still, asking somebody other than me would be your best course of action, so I'll happily sit aside if someone else who has a penchant for seeing pretty images with their drag operations wants to do it.zcooler wrote:Few folks seem to understand this stuff and since you are one of those I wanna hear with ya if you might be interested to have an another go at it?
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hey qwerty12,
Thanks for the swift and honest answer. I understand your position, Im in it myself Its not mainly about pretty images, but more about the tiny AHK drag&drop cursors and lack of dropdescription which makes it hard to even visually detect if a Drag operation is carried through at all in a styled TreeView GUI. Well, this was unfortunate, the D&D interface seem to remain unfinished.
Thanks for the swift and honest answer. I understand your position, Im in it myself Its not mainly about pretty images, but more about the tiny AHK drag&drop cursors and lack of dropdescription which makes it hard to even visually detect if a Drag operation is carried through at all in a styled TreeView GUI. Well, this was unfortunate, the D&D interface seem to remain unfinished.
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi querty12,
I missed your post from May 2016 but now noticed your last answer. Sorry, and thanks for your contribution!
Now I tried your changes in my test environment with no success at first. Then I noticed the key phrase:
After analyzing your changes I found that even my original code (at least my local version) actually shows the drag image on suitable applications. The E_FAIL returned by IDragSourceHelper.InitializeFromBitmap seems to be caused by the limits of the data object returned from OleGetClipboard().
Some remarks:
Thanks again,
just me
I missed your post from May 2016 but now noticed your last answer. Sorry, and thanks for your contribution!
Now I tried your changes in my test environment with no success at first. Then I noticed the key phrase:
And after copying a file to the clipboard in the shell the drag image was shown.IDataSource needs to be implemented to move arbitrary things. Currently this is limited to what's on the clipboard (I test by Ctrl-Cing a file before running Untitled.ahk).
After analyzing your changes I found that even my original code (at least my local version) actually shows the drag image on suitable applications. The E_FAIL returned by IDragSourceHelper.InitializeFromBitmap seems to be caused by the limits of the data object returned from OleGetClipboard().
Some remarks:
- IDropSource VTBL:
S_FAIL was being returned because the IDropSource's vtable wasn't being properly created.
The way I did it might look strange, but it is actually working when used like I do it in DoDragDrop(). - IDropSource_Free():
Assuming VTBL is kept static (which I'm doing because of possible scope issues, and the freeing ones that arise from that), this wouldn't work well if multiple IDropSources are present.
I agree that 'freeing' the VTBL is useless. But it's rather impractical to have more than one IDropSource which would mean more than one DoDragDrop() running at the same time in AHK v1.1. - IDropSource_QueryInterface():
This method is'nt called for IID_IUnknown and even not for IID_IDropSource in this scenario.
I'm not really interested in the 'visual effects' provided by IDragSourceHelper. I just wanted to know why it wasn't working. IDropSource seems to be working with suitable applications. When used by an AHK script, everyone will know what he's currently dragging.GUI stuff doesn't interest me, which is why this isn't really holding my attention.
Thanks again,
just me
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi just me,
Best regards,
qwerty12
Thanks But thanks to you for the immense work you put into this (the only experience I have with this sort of thing is my quick implementation of the IMMNotificationClient interface - I know I wouldn't have been able to come up with something as clean as this)just me wrote:I missed your post from May 2016 but now noticed your last answer. Sorry, and thanks for your contribution!
OK, that's good, my changes weren't much and, it seems, unnecessary. Despite what I told zcooler, I did actually spend a couple of nights again on this to no avail...After analyzing your changes I found that even my original code (at least my local version) actually shows the drag image on suitable applications. The E_FAIL returned by IDragSourceHelper.InitializeFromBitmap seems to be caused by the limits of the data object returned from OleGetClipboard().
You're absolutely right. Back then, I was thinking in one dimension: you create one struct for the vtable and another struct for the object itself, which holds just the pointer to the vtable. This time, when I downloaded all the code again (from your Git repository, this post, zcooler's demo and my changes), I realised now (with a bit more experience - I'm really not a programmer! ) after looking over the differences at every step of doing that the superior method you were using to construct the vtable and actually kept your code the same instead of changing it:The way I did it might look strange, but it is actually working when used like I do it in DoDragDrop()
- One struct is used to hold both the vtable and the pointer to the vtable that would ordinarily, by convention, be in a separate struct. But the code that gets a pointer to the COM object isn't going to look beyond the beginning of the struct, reading at most A_PtrSize bytes for the vtable pointer. Said vtable pointer points to the first COM function, like usual
- (Methods.Length() + 2) confused me - I mean, I understood why +1 would be needed for the extra pointer, but an additional +1 atop that and zeroing of the struct (when it was going to get filled with pointers right after that) seemed unneeded. So I changed it, watched AutoHotkey crash sometimes after I dragged a file and then when I looked at IDropSource_Free(), I understood everything
Sorry, I was talking rubbish. In My Attempt Mk2 I got rid of that inane comment and kept your code there the same.I agree that 'freeing' the VTBL is useless. But it's rather impractical to have more than one IDropSource which would mean more than one DoDragDrop() running at the same time in AHK v1.1.
Thanks. I still ended up changing that one blindly. I should've checked, but I just wanted to be sure, I guess, that it wasn't the little things :\This method is'nt called for IID_IUnknown and even not for IID_IDropSource in this scenario.
Does this mean you're working on this again? I'm evidently not the right person for this and zcooler will be happy to hear thatI'm not really interested in the 'visual effects' provided by IDragSourceHelper. I just wanted to know why it wasn't working. IDropSource seems to be working with suitable applications. When used by an AHK script, everyone will know what he's currently dragging.
Best regards,
qwerty12
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Yes of course, everyone will know what he's currently dragging when used by an AHK script. In the AHK script im using the COM D&D interface with, sometimes the Drag operation never gets initialized at all and then it's hard to visually detect it with the default AHK cursor. The small square under the mouse cursor is the only sign when a Move Drag operation is initiated. That square easily gets masked by the underlying TV row. Like this: If sitting a few meters from the TV/Monitor (HTPC system) you ought to have quite sharp vision to even see it and often I have to drag the cursor to an empty part of the treeview to make sure the Drag operation has been initialized alright.just me wrote:I'm not really interested in the 'visual effects' provided by IDragSourceHelper. I just wanted to know why it wasn't working. IDropSource seems to be working with suitable applications. When used by an AHK script, everyone will know what he's currently dragging.
That is why I think the 'visual effects' provided by the IDragSourceHelper, with DropDescriptions, would silverline this already fantastic D&D interface.
Not only me, the whole AHK community would be very happy to be using a D&D interface that completely measures up with the MS Shell counterpartqwerty12 wrote:I'm evidently not the right person for this and zcooler will be happy to hear that
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi querty12,
don't worry. I didn't want to blame you. Though I am a programmer it's still very COMplicated stuff for me. I think that every further step would require a fully featured implementation of the IDataObject interface. That's still far beyond my skills.
Best regards!
just me
don't worry. I didn't want to blame you. Though I am a programmer it's still very COMplicated stuff for me. I think that every further step would require a fully featured implementation of the IDataObject interface. That's still far beyond my skills.
Best regards!
just me
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Hi zcooler,
I know which application you are talking about. And I already tried to explain you why it's 'unstable' sometimes.
You cannot use a drag image or a drop description in this case. But it's easy to use user-defined cursors while dragging. I'll update the GitHub repository as soon as I'm ready. Might be better than nothing.
Regards,
just me
I know which application you are talking about. And I already tried to explain you why it's 'unstable' sometimes.
You cannot use a drag image or a drop description in this case. But it's easy to use user-defined cursors while dragging. I'll update the GitHub repository as soon as I'm ready. Might be better than nothing.
Regards,
just me
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Greetings Oh Great One
Best regards
zcooler
Sorry Just me, our discussions about this script/app have been so plentyful I cannot remember em all. It was quite some time ago also. A very long break from AHK doesnt make things clearer either. Took up golfing instead To not stray too much off topic I can only say its not unstable, more like a smaller quirk. Im aware how to aviod a drag operation initialization failure with my script now. Key is "slow dragging". To be clear (for others that are interested in using Just me`s COM D&D interface) this is an AHK related quirk in my script and has nothing to do with the COM D&D interface or the Demo I posted above.just me wrote:I know which application you are talking about. And I already tried to explain you why it's 'unstable' sometimes.
Yes that is obvious, no drag image or a drop description (upon pressing SHIFT or Ctrl when dragging within source window/folder as the Shell) when a drag operation initialization fails or are you referring to something else?just me wrote:You cannot use a drag image or a drop description in this case.
That is fantastic news, Just mejust me wrote:But it's easy to use user-defined cursors while dragging. I'll update the GitHub repository as soon as I'm ready. Might be better than nothing.
Best regards
zcooler
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Ok, updated package available on GitHub.
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Greetings Just me,
Wow, you move swift like the wind Maybe adding user-defined cursors was just as easy as you were stating earlier...well easy for you, but really hard for the rest of us
I kept the IDC_CROSS for DROPEFFECT_COPY which also is used on the DROPESCRIPTION in the Shell (to display adding/copy).
I exchanged the UpArrow to IDC_SIZEALL Four-pointed white arrow pointing north, south, east, and west for DROPEFFECT_MOVE operations. The Shell DROPESCRIPTION uses a black rightpointed arrow here, but that one wasnt available amongst the Win10 cursors. Although white cursors are preferrable cuz the contrast/visibility is better on top of black text.
Pretty nice additions indeed It is now easier to see the drag initialization even though I will keep on dreaming about those cool drag images displaying movie and series thumbnails Maybe some day
Thank you, Just me
Best regards
zcooler
Wow, you move swift like the wind Maybe adding user-defined cursors was just as easy as you were stating earlier...well easy for you, but really hard for the rest of us
I kept the IDC_CROSS for DROPEFFECT_COPY which also is used on the DROPESCRIPTION in the Shell (to display adding/copy).
I exchanged the UpArrow to IDC_SIZEALL Four-pointed white arrow pointing north, south, east, and west for DROPEFFECT_MOVE operations. The Shell DROPESCRIPTION uses a black rightpointed arrow here, but that one wasnt available amongst the Win10 cursors. Although white cursors are preferrable cuz the contrast/visibility is better on top of black text.
Pretty nice additions indeed It is now easier to see the drag initialization even though I will keep on dreaming about those cool drag images displaying movie and series thumbnails Maybe some day
Thank you, Just me
Best regards
zcooler
Re: [COM] Help with the IDropSource and IDropTarget interfaces
It's not restricted to available system cursors. You also could create your own cursors and use (e.g.) LoadCursorFromFile().
Re: [COM] Help with the IDropSource and IDropTarget interfaces
Well, I finally found a simple solution providing basic support for drag images with an AHK drop source on Win Vista+. It's a combination of SHDoDragDrop() - API and SHCreateDataObject() - Win Vista+.
On Win Vista+ SHCreateDataObject() is able to create the sufficient data object and to embed the contents of the data object returned by OleGetClipboard(). So SHDoDragDrop() - API is able to use a user-defined drag image or to create a default image on demand. There are three options:
Changes:
But using it passing a data object retrieved by OleGetClipboard() it doesn't. Apparently, this data object lacks a sufficient implementation of IDataObject.SetData() needed to add a drag image.SHDoDragDrop - API wrote:As of Windows Vista, if a drag image is not already stored in the data object pdtobj and a drag image cannot be obtained from the window specified by hwnd, the Shell provides a generic drag image. A drag image can fail to be obtained from the specified window either because hwnd is NULL or the specified window does not support the DI_GETDRAGIMAGE message.
On Win Vista+ SHCreateDataObject() is able to create the sufficient data object and to embed the contents of the data object returned by OleGetClipboard(). So SHDoDragDrop() - API is able to use a user-defined drag image or to create a default image on demand. There are three options:
- If you pass NULL in the HWND parameter or if the window/control specified by the passed HWND doesn't create a drag image a default (blank) drag image and a textual drop description will be used.
- If the windoe/control specified by the passed HWND creates a drag image (e.g. ListView and TreeView controls are able to do it), this drag image will be used in combination with a 'modern' drag cursor.
- If you pass a handle of a bitmap (HBITMAP) in DragImage SHDoDragDrop() - AHK tries to create an own drag image. On success, it will be used in combination with a 'modern' drag cursor.
Changes:
- Added SHDataObject.ahk
- Added SHDoDragDrop.ahk
- Added SHDoDragDrop_sample.ahk
- Changed IDataObject.ahk (added some auxiliary functions).
- Changed IDropTarget.ahk to respect the 'preferred drop effect'
Re: [COM] Help with the IDropSource and IDropTarget interfaces
OHHHH JEEEZ....a test blowout coming up during the weekend
Re: [COM] Help with the IDropSource and IDropTarget interfaces
SWEET MOTHER OF GOD That is a huge breakthrough and you are fantastic, Just me Truely ROCKSTAR level stuff
In a broader D&D interface implementation perspective into my app it is no problem to retrieve the icon associated with the file which is usually used as the DragImage. However I have no idea how to retrieve thumbnail paths for media files programatically. The shell D&D interface do prioritize thumbnail dragimages before icons. Well, I will have to investigate that one since it is not within scope of your D&D interface.
Chockingly well done, Just me Praise the Just me
Best regards
zcooler
In a broader D&D interface implementation perspective into my app it is no problem to retrieve the icon associated with the file which is usually used as the DragImage. However I have no idea how to retrieve thumbnail paths for media files programatically. The shell D&D interface do prioritize thumbnail dragimages before icons. Well, I will have to investigate that one since it is not within scope of your D&D interface.
Chockingly well done, Just me Praise the Just me
Best regards
zcooler