Improved SendTo dialog Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Improved SendTo dialog

24 Apr 2020, 15:40

Hi all,

I was thinking along the following lines. Out of the entire filesystem and folder structure of my hard drive, about 30 folders are important. I have 1 START folder that contains a shortcut to several files and also to these 30 folders. The list can change, though.

So when I need to copy or paste files to a certain folder, I now press Win-e for the File Explorer, then launch the START folder, then right-click the target folder shortcut and Paste.

I think that can be faster/easier and I don't need all the File Explorer windows. The idea is to have a popup userform with a listbox containing the 30 folders. Basically, we need to loop through the START folder and keep (1) all shortcuts, that (2) point to a valid folder. Then I can click on a folder and the selected files can be moved to the target folder.

The path of the START folder can he hardcoded in the script, but a loop would be good to loop through its contents.

Does anyone have something along these lines ? I can do the rest of the scripting based on an example for example. I could add the Desktop location / Downloads / USB key location to the list, and so on.

Thank you in advance !
GEV
Posts: 1002
Joined: 25 Feb 2014, 00:50

Re: Improved SendTo dialog  Topic is solved

25 Apr 2020, 11:06

Try something like this;

Code: Select all

#NoEnv
#SingleInstance Force

; Create a GUI with checkboxes to save the path of the last copied files and 
; copy/move selected items to other directories shown in a Menu:

; Add in this list the path of the directories
folder_paths =
(
%A_MyDocuments%
%A_Desktop%
D:\my folder
)

; Create the Menu
Loop, Parse, folder_paths, `n
{
	Menu copy_files, Add, %A_LoopField%, copy_files
	Menu move_files, Add, %A_LoopField%, move_files
}

; save the path of the last copied files
OnClipboardChange("files_copied")
return

; Create the GUI
files_copied(type){
    global
    If  (type == 1)  ; the Clipboard contains text or files copied
    {
        If (DllCall("IsClipboardFormatAvailable", "uint", 15))  ; the Clipboard contains files
        {
            ToolTip, file(s) copied
            Gui, destroy
            Gui, Color, ControlColor, Black
            Gui, Font, CDefault, Lucida Console
            Gui,  Add, Button,   x30 y5 w100 h26 gCopy, Copy
            Gui,  Add, Button,   x230 y5 w100 h26 gMove, Move
            Gui,  Add, CheckBox, x20 y50 vCh200 gCheckAll   cYellow, Select All
            Gui,  Add, CheckBox, x20 y75 vCh201 gUnCheckAll cYellow, De-Select All
            Loop, Parse, Clipboard, `n, `r
            {
                y_pos := 100 + (A_Index * 25)
                Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %A_LoopField%
                I := A_Index ; number of copied files
            }
            Gui_height := 125+I*25
            Gui,  Show, x5 y5 w400 h%Gui_height%, Files copied ; comment out this line if you want to only show the Gui after pressing F1
            Sleep 1000
            ToolTip
        }
    }
}

; Press F1 to show the Gui if it's hidden
F1:: Gui,  Show
; or If the command in the above function is commented out
; F1:: Gui,  Show, x100 y5 w400 h%Gui_height%, Files copied

CheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 1
GuiControl,, Ch200, 1
GuiControl,, Ch201, 0
return

UnCheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 0
GuiControl,, Ch200, 0
GuiControl,, Ch201, 1
return

Copy:
Menu copy_files, show
return

Move:
Menu move_files, show
return

copy_files:
Gui, submit, nohide
Run %A_ThisMenuItem%\
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
		GuiControlGet, file,, Ch%A_Index%, Text
        FileCopy, %file%, %A_ThisMenuItem%\, 1  ; overwrite existing files
    }
}
return

move_files:
MsgBox, 262180, Move Files, Are you sure you want to move the selected files to`n`n"%A_ThisMenuItem%"?
IfMsgBox No
    return
Run %A_ThisMenuItem%\
Loop %I%
{    
	GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        FileMove, %file%, %A_ThisMenuItem%\, 1  ; overwrite existing files
    }
    else
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        New_Clipboard .= file . "`n" ; concatenate the outputs
    }
}
If (New_Clipboard := "")
    Gui, destroy
else
    ClipboardSetFiles(New_Clipboard, DropEffect := "Copy")
return

GuiClose:
Gui, Hide
return  

; Press Esc to terminate the script
Esc:: ExitApp

; https://autohotkey.com/boards/viewtopic.php?p=63914#p63914
ClipboardSetFiles(FilesToSet, DropEffect := "Copy") {
   ; FilesToSet - list of fully qualified file pathes separated by "`n" or "`r`n"
   ; DropEffect - preferred drop effect, either "Copy", "Move" or "" (empty string)
   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 False
   ; Add files to the clipboard
   If DllCall("OpenClipboard", "Ptr", A_ScriptHwnd) && DllCall("EmptyClipboard") {
      ; HDROP format 
      ; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
      hDrop := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 20 + (TotalLength + FileCount + 1) * TCS, "UPtr")
      pDrop := DllCall("GlobalLock", "Ptr" , hDrop)
      Offset := 20
      NumPut(Offset, pDrop + 0, "UInt")         ; DROPFILES.pFiles = offset of file list
      NumPut(!!A_IsUnicode, pDrop + 16, "UInt") ; DROPFILES.fWide = 0 --> ANSI, fWide = 1 --> Unicode
      For Each, File In FileArray
         Offset += StrPut(File.Path, pDrop + Offset, File.Len) * TCS
      DllCall("GlobalUnlock", "Ptr", hDrop)
      DllCall("SetClipboardData","UInt", 0x0F, "UPtr", hDrop) ; 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 True
   }
   Return False
}
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

25 Apr 2020, 12:39

Many many thanks !
I will review this today and post my feedback.
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

25 Apr 2020, 12:49

Amazing ! :clap:
I will extend the logic and post updates here.
Thank you very much.
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

25 Apr 2020, 13:59

I added the option to provide paths of directories that contain shortcuts. The targeted directories of the shortcuts are used in the script too.

Code: Select all

; Add in this list the path of the directories
folder_paths =
(
%A_Desktop%
%A_MyDocuments%
)

; Add in this list the path of the directories that contain shortcuts
; The targeted directories are used in the script too
folder_paths_to_loop =
(
D:\OneDrive\............\Shortcuts & backups\START
)

; Create the Menu
Loop, Parse, folder_paths, `n
{
	Menu copy_files, Add, %A_LoopField%, copy_files
	Menu move_files, Add, %A_LoopField%, move_files
}
Loop, Parse, folder_paths_to_loop, `n
{
    Loop, Files, %A_LoopField%\*, F
    {
        extensions := "lnk,"
        if A_LoopFileExt in %extensions%	  
        {
            FileGetShortcut, %A_LoopFileFullPath%, shortcutTargetPath
            If( InStr( FileExist(shortcutTargetPath), "D") )
            {
	           Menu copy_files, Add, %shortcutTargetPath%, copy_files
	           Menu move_files, Add, %shortcutTargetPath%, move_files
            }
        }
    }
}
Last edited by Wigi on 25 Apr 2020, 16:03, edited 1 time in total.
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

25 Apr 2020, 15:22

I marked the topic as solved but I will continue to make the script even more functional. Thanks.
GEV
Posts: 1002
Joined: 25 Feb 2014, 00:50

Re: Improved SendTo dialog

25 Apr 2020, 16:14

If you only want to copy selected files, you could also create a shortcut of your favorites folders in the sendto folder.

To open the sendto folder use the command:

Code: Select all

Run shell:sendto
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

25 Apr 2020, 20:38

- Version where I have reformatted the file paths
- Also the directories are loaded in the menu at runtime, rather than when the script is refreshed. It could be that in the meantime locations are added so I want to have them included.
- Sanity checks were added, such that at least 1 file should be selected to do the copy or cut.
- If the user copies a file to the same folder, I add ' - Copy' as a suffix in the filename.

For now, the user needs to do a copy or cut so the clipboard is filled with files. Then hit F1 to show the userform and proceed.

Code: Select all

#NoEnv
#SingleInstance Force

; Create a GUI with checkboxes to save the path of the last copied files and
; copy/move selected items to other directories shown in a Menu:

; Add in this list the path of the directories
folder_paths =
(
Desktop `t[%A_Desktop%]
My Documents `t[%A_MyDocuments%]

)

; Add in this list the path of the directories that contain shortcuts.
; The targeted directories of the shortcuts are used in the script too.
folder_paths_to_loop =
(
D:\OneDrive\O....ups\COPY_CUT
)

; save the path of the last copied files
OnClipboardChange("files_copied")
return

; Create the GUI
files_copied(type){
    global
    If  (type == 1)  ; the Clipboard contains text or files copied
    {
        If (DllCall("IsClipboardFormatAvailable", "uint", 15))  ; the Clipboard contains files
        {
            ToolTip, file(s) copied/cut
            Gui, destroy
            Gui, Color, ControlColor, Black
            Gui, Font, CDefault, Lucida Console
            Gui,  Add, Button,   x30 y5 w100 h26 gMove, Move
            Gui,  Add, Button,   x230 y5 w100 h26 gCopy, Copy
            Gui,  Add, CheckBox, x20 y50 vCh200 gCheckAll   cYellow, Select All
            Gui,  Add, CheckBox, x20 y75 vCh201 gUnCheckAll cYellow, De-Select All
            Loop, Parse, Clipboard, `n, `r
            {
                y_pos := 100 + (A_Index * 25)
                ; Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %A_LoopField%
                SplitPath, A_LoopField, filename, path
                Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %filename% `t[%path%]
                I := A_Index ; number of copied files
            }
            Gui_height := 125+I*25
            ; ; ; Gui,  Show, x5 y5 w600 h%Gui_height%, SendTo ; comment out this line if you want to only show the Gui after pressing F1
            GoSub CheckAll
            Sleep 1000
            ToolTip
        }
    }
}

; Press F1 to show the Gui if it's hidden
; ; ; F1:: Gui,  Show
; or If the command in the above function is commented out
F1:: 


Menu copy_files, UseErrorLevel
Menu move_files, UseErrorLevel

Menu copy_files, DeleteAll
Menu move_files, DeleteAll

Menu copy_files, UseErrorLevel, OFF
Menu move_files, UseErrorLevel, OFF

; Create the Menu
Loop, Parse, folder_paths, `n
{
    Menu copy_files, Add, %A_LoopField%, copy_files
    Menu move_files, Add, %A_LoopField%, move_files
}
Loop, Parse, folder_paths_to_loop, `n
{
    Loop, Files, %A_LoopField%\*, F
    {
        extensions := "lnk,"
        if A_LoopFileExt in %extensions%
        {
            FileGetShortcut, %A_LoopFileFullPath%, shortcutTargetPath
            If( InStr( FileExist(shortcutTargetPath), "D") )
            {
               SplitPath, A_LoopFileLongPath, , , , OutNameNoExt
               Menu copy_files, Add, %OutNameNoExt% `t[%shortcutTargetPath%], copy_files
               Menu move_files, Add, %OutNameNoExt% `t[%shortcutTargetPath%], move_files
            }
        }
    }
}

Gui,  Show, x100 y5 w600 h%Gui_height%, SendTo

CheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 1
GuiControl,, Ch200, 1
GuiControl,, Ch201, 0
return

UnCheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 0
GuiControl,, Ch200, 0
GuiControl,, Ch201, 1
return

Copy:
Menu copy_files, show
return

Move:
Menu move_files, show
return

copy_files:
Gui, submit, nohide

; make sure at least 1 file is selected
n := 0
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) { ; if the control is checked
         n := 1
         Break
    }
}
if (n = 0)
{
   Msgbox Please select at least 1 file.
   return
}
vTarget_Path := RegexReplace( A_ThisMenuItem, ".*\[(.+?)\].*", "$1")
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
        filename := StrSplit(file, "[", " `t")[1]
        ; if we copy files to the same folder, add a Copy suffix
        If (filepath = vTarget_Path) {
           SplitPath, filename, , , OutExtension, OutNameNoExt
           FileCopy, %filepath%\%filename%, %vTarget_Path%\%OutNameNoExt% - Copy.%OutExtension%, 1  ; overwrite existing files
           }
        Else
           FileCopy, %filepath%\%filename%, %vTarget_Path%\, 1  ; overwrite existing files
    }
}
Run %vTarget_Path%\
Gui, destroy
return

move_files:

; make sure at least 1 file is selected
n := 0
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) { ; if the control is checked
         n := 1
         Break
    }
}
if (n = 0)
{
   Msgbox Please select at least 1 file.
   return
}

vTarget_Path := RegexReplace( A_ThisMenuItem, ".*\[(.+?)\].*", "$1")
MsgBox, 262180, Move files, Are you sure you want to move the selected file(s) to:`n`n`t%vTarget_Path%
IfMsgBox No
    return

Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
        filename := StrSplit(file, "[", " `t")[1]
        If (filepath != vTarget_Path)
           FileMove, %filepath%\%filename%, %vTarget_Path%\, 1  ; overwrite existing files
    }
    else
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        New_Clipboard .= file . "`n" ; concatenate the outputs
    }
}
Run %vTarget_Path%\
Gui, destroy
If (New_Clipboard := "")
    Gui, destroy
else
    ClipboardSetFiles(New_Clipboard, DropEffect := "Copy")
return

GuiClose:
Gui, Hide
return

; Press Esc to terminate the script
Esc:: ExitApp

; https://autohotkey.com/boards/viewtopic.php?p=63914#p63914
ClipboardSetFiles(FilesToSet, DropEffect := "Copy") {
   ; FilesToSet - list of fully qualified file pathes separated by "`n" or "`r`n"
   ; DropEffect - preferred drop effect, either "Copy", "Move" or "" (empty string)
   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 False
   ; Add files to the clipboard
   If DllCall("OpenClipboard", "Ptr", A_ScriptHwnd) && DllCall("EmptyClipboard") {
      ; HDROP format
      ; 0x42 = GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
      hDrop := DllCall("GlobalAlloc", "UInt", 0x42, "UInt", 20 + (TotalLength + FileCount + 1) * TCS, "UPtr")
      pDrop := DllCall("GlobalLock", "Ptr" , hDrop)
      Offset := 20
      NumPut(Offset, pDrop + 0, "UInt")         ; DROPFILES.pFiles = offset of file list
      NumPut(!!A_IsUnicode, pDrop + 16, "UInt") ; DROPFILES.fWide = 0 --> ANSI, fWide = 1 --> Unicode
      For Each, File In FileArray
         Offset += StrPut(File.Path, pDrop + Offset, File.Len) * TCS
      DllCall("GlobalUnlock", "Ptr", hDrop)
      DllCall("SetClipboardData","UInt", 0x0F, "UPtr", hDrop) ; 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 True
   }
   Return False
}
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

26 Apr 2020, 18:38

Hi all,

While the previous solution is very good, I still want to have as few actions as possible. So I changed the code to the following:
- user selects 1 or more files in either Windows Explorer, either the Desktop. Selecting 0 files is allowed too, then you treat all files.
- user presses the Menu key (AppsKey) in either Windows Explorer, either the Desktop
- the userform is shown, listing the files/folders that were selected
- same as before, copy or move a selection of files

Additionally, the code can treat environment variables to shorten paths. I use %HO% to point to the folder: "D:\OneDrive\OneDrive - Aexis NV"
So this is honoured to, both in contracting long OneDrive paths to %HO% as well as translating again %HO% to OneDrive at the time of copying/moving files/folders.

Ctrl-c and Ctrl-x are not captured anymore in the sense that clipboard changes are not monitored by the script below.

I will keep this version of the code but it is still not fully optimized I think. In the end, I just want to:
- select 1 or more files in either Windows Explorer, either the Desktop
- hit the Menu key
- hit the button "Copy" or "Move" and select the target location. No other logic is needed. GEV: I do admire your first code a lot, though !

In essence, a simple ListView of target paths will do. A left-mouse click can be "Move files", a right-mouse click "Copy files". Or something like that. Maybe multi-select will be allowed to copy to multiple target folders. I will probably not show the source files anymore in the userform to conserve space.

To be continued :-)

Code: Select all

#SingleInstance Force

; Create a GUI with checkboxes to save the path of the last copied files and
; copy/move selected items to other directories shown in a Menu:

; Add in this list the path of the directories
folder_paths =
(
Desktop `t[%A_Desktop%]

)
; My Documents `t[%A_MyDocuments%]

; Add in this list the path of the directories that contain shortcuts.
; The targeted directories of the shortcuts are used in the script too.
folder_paths_to_loop =
(
%HO%\........\Shortcuts & backups\COPY_CUT
)

; Press the Menu key to show the Gui if it's hidden
; ; ; AppsKey:: Gui,  Show
; or If the command in the above function is commented out

AppsKey::

Menu copy_files, UseErrorLevel
Menu move_files, UseErrorLevel

Menu copy_files, DeleteAll
Menu move_files, DeleteAll

Menu copy_files, UseErrorLevel, OFF
Menu move_files, UseErrorLevel, OFF

; Create the GUI
Gui, destroy
Gui, Color, ControlColor, Black
Gui, Font, CDefault, Lucida Console
Gui,  Add, Button,   x30 y5 w100 h26 gMove, Move
Gui,  Add, Button,   x230 y5 w100 h26 gCopy, Copy
Gui,  Add, CheckBox, x20 y50 vCh200 gCheckAll   cYellow, Select All
Gui,  Add, CheckBox, x20 y75 vCh201 gUnCheckAll cYellow, De-Select All

; retrieve the selection of the user: either files/folders, either the path in the File Explorer
v =
v := GetSelectedFileFolderNames_UnderCursor()
if !v
{
    v := GetSelectedFileFolderNames_FolderYouSee()
    if !v
      return
}
Loop, Parse, v, `n
{
    y_pos := 100 + (A_Index * 25)
    ; Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %A_LoopField%
    SplitPath, A_LoopField, filename, path
    path := InsertEnvVars(path)
    Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %filename% `t[%path%]
    I := A_Index ; number of copied files
}
Gui_height := 125+I*25
GoSub CheckAll
; Sleep 1000

; Create the Menu
Loop, Parse, folder_paths, `n
{
    Menu copy_files, Add, %A_LoopField%, copy_files
    Menu move_files, Add, %A_LoopField%, move_files
}
Loop, Parse, folder_paths_to_loop, `n
{
    Loop, Files, % ReplaceEnvVars(A_LoopField) . "\*", F
    {
        extensions := "lnk,"
        if A_LoopFileExt in %extensions%
        {
            FileGetShortcut, %A_LoopFileFullPath%, shortcutTargetPath
            If( InStr( FileExist(shortcutTargetPath), "D") )
            {
               SplitPath, A_LoopFileLongPath, , , , OutNameNoExt
               vFolder_Short := InsertEnvVars(shortcutTargetPath)
               Menu copy_files, Add, %OutNameNoExt% `t[%vFolder_Short%], copy_files
               Menu move_files, Add, %OutNameNoExt% `t[%vFolder_Short%], move_files
            }
        }
    }
}

Gui,  Show, x100 y5 w600 h%Gui_height%, SendTo

CheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 1
GuiControl,, Ch200, 1
GuiControl,, Ch201, 0
return

UnCheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 0
GuiControl,, Ch200, 0
GuiControl,, Ch201, 1
return

Copy:
Menu copy_files, show
return

Move:
Menu move_files, show
return

copy_files:
Gui, submit, nohide

; make sure at least 1 file is selected
n := 0
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) { ; if the control is checked
         n := 1
         Break
    }
}
if (n = 0)
{
   Msgbox Please select at least 1 file.
   return
}
vTarget_Path := RegexReplace( A_ThisMenuItem, ".*\[(.+?)\].*", "$1")
vTarget_Path := ReplaceEnvVars(vTarget_Path)
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
        filepath := ReplaceEnvVars(filepath)
        filename := StrSplit(file, "[", " `t")[1]
        ; if we copy files to the same folder, add a Copy suffix
        If (filepath = vTarget_Path)
        {
           SplitPath, filename, , , OutExtension, OutNameNoExt
           FileCopy, %filepath%\%filename%, %vTarget_Path%\%OutNameNoExt% - Copy.%OutExtension%, 1  ; overwrite existing files
        }
        Else
        {
           vFullName = %filepath%\%filename%
           If( InStr( FileExist(vFullName), "D") )
               FileCopyDir, %vFullName%, %vTarget_Path%\%filename%, 1 ; overwrite existing files
           Else
               FileCopy, %vFullName%, %vTarget_Path%, 1 ; overwrite existing files
        }
    }
}
; Run %vTarget_Path%\
Gui, destroy
return

move_files:

; make sure at least 1 file is selected
n := 0
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) { ; if the control is checked
         n := 1
         Break
    }
}
if (n = 0)
{
   Msgbox Please select at least 1 file.
   return
}

vTarget_Path := RegexReplace( A_ThisMenuItem, ".*\[(.+?)\].*", "$1")
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
        GuiControlGet, file,, Ch%A_Index%, Text
        filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
        filepath := ReplaceEnvVars(filepath)
        filename := StrSplit(file, "[", " `t")[1]
        If (filepath != vTarget_Path)
           {
           vFullName = %filepath%\%filename%
           If( InStr( FileExist(vFullName), "D") )
               FileMoveDir, %filepath%\%filename%, %vTarget_Path%\%filename%, 1  ; overwrite existing files
           Else
               FileMove, %filepath%\%filename%, %vTarget_Path%, 1  ; overwrite existing files
           }
    }
}
; Run %vTarget_Path%\
Gui, destroy

GuiClose:
Gui, Hide
return

; Press Esc to terminate the script
Esc:: ExitApp

ExpandEnvVars(sEV)
{
    VarSetCapacity(dest, 2000)
    DllCall("ExpandEnvironmentStrings", "str", sEV, "str", dest, int, 1999, "Cdecl int")
    return dest
}
return

ReplaceEnvVars(sLocation)
{
    if ( InStr( sLocation, "%HO%", false ) > 0 )
       sLocation := StrReplace(sLocation, "%HO%", ExpandEnvVars("%HO%"))
    return sLocation
}
return

InsertEnvVars(sLocation)
{
    if ( InStr( sLocation, ExpandEnvVars("%HO%"), false ) > 0 )
       sLocation := StrReplace(sLocation, ExpandEnvVars("%HO%"), "%HO%")
    return sLocation
}
return

GetSelectedFileFolderNames_UnderCursor()
{
    ToReturn =
    hwnd := hwnd ? hwnd : WinExist("A")
    WinGetClass class, ahk_id %hwnd%
    if (class="CabinetWClass" or class="ExploreWClass")
    {
        for window in ComObjCreate("Shell.Application").Windows
            if (window.hwnd==hwnd)
            {
               sel := window.Document.SelectedItems
               for item in sel
               {
                  v := % item.path
                  ToReturn .= v "`n"
               }
            }
    }
    else
    {
       files:={}    
       sel:=""
       if (class="Progman" or class="WorkerW")
          FolderPath = %A_Desktop%
       else if WinActive("ahk_class CabinetWClass")
          ; http://ahkscript.org/boards/viewtopic.php?p=28751#p28751
          for window in ComObjCreate("Shell.Application").Windows
              try FolderPath := window.Document.Folder.Self.Path
       ControlGet, win, HWND,, SysListView321, A
       ControlGet, sel, List, Selected Col1,,ahk_id %win%
       Loop, parse, sel, `n, `r
          files.push(A_LoopField)
       for each, file in files
       {
          FileGetAttrib, FileAttrib, %FolderPath%\%file%
          If InStr(FileAttrib, "D") ; a DIRECTORY
          {
             Loop, %FolderPath%\%file%, 2,0 ; only folders
             {
                StringReplace, FileFullPath, A_LoopFileFullPath, :\\ ,:\
                ; MsgBox, DIRECTORY: "%FileFullPath%"
                v = %FileFullPath%
                ToReturn .= v "`n"
             }
          }
          else
          {
             Loop, %FolderPath%\%file%.*
             {
                ; MsgBox, FILE: "%A_LoopFileFullPath%"
                v := % A_LoopFileFullPath
                ToReturn .= v "`n"
             }
          }
       }
    }
    ToReturn := Trim(ToReturn,"`n")
    return ToReturn
}

GetSelectedFileFolderNames_FolderYouSee()
{
    ToReturn =
    ; WinGetClass, class, A
    hwnd := hwnd ? hwnd : WinExist("A")
    WinGetClass class, ahk_id %hwnd%
    if (class="CabinetWClass" or class="ExploreWClass")
    {
        ControlGetText, currentpath, ToolbarWindow323, ahk_class %class%
        StringTrimLeft, currentpath, currentpath, 9
        if( SubStr( currentpath, 0, 1) == "\")
           currentpath := SubStr(currentpath, 1, StrLen( currentpath ) - 1 )
        ToReturn .= currentpath "`n"
        ToReturn := Trim(ToReturn,"`n")
        Return ToReturn
    }
    else if (class="Progman" or class="WorkerW")
    {
        currentpath = %A_Desktop%
        ToReturn .= currentpath "`n"
        ToReturn := Trim(ToReturn,"`n")
        Return ToReturn
    }
    Return
}
GEV
Posts: 1002
Joined: 25 Feb 2014, 00:50

Re: Improved SendTo dialog

27 Apr 2020, 02:44

You can compile such a script

Code: Select all

#NoEnv
#SingleInstance Ignore

If !WinActive("ahk_exe explorer.exe")
	ExitApp

WinGetActiveTitle, WinTitle

target_folders =
(
%A_Desktop%
%A_MyDocuments%
D:\my folder\subfolder1
)

Loop, Parse, target_folders, `n
	If !FileExist(A_LoopField)
		FileCreateDir, %A_LoopField%

Gui, destroy
Gui, Color, ControlColor, Black
Gui, Font, CDefault, Lucida Console
Gui,  Add, Button,   x30 y5 w100 h26 gCopy, Copy
Gui,  Add, Button,   x230 y5 w100 h26 gMove, Move
Gui,  Add, CheckBox, x20 y50 vCh200 gCheckAll   cYellow, Select All
Gui,  Add, CheckBox, x20 y75 vCh201 gUnCheckAll cYellow, De-Select All
Loop, Parse, target_folders, `n, `r
{
	y_pos := 100 + (A_Index * 25)
	Gui,  Add, CheckBox, x20 y%y_pos%  vCh%A_Index% cYellow, %A_LoopField%
	I := A_Index ; number of target_folders
}
Gui_height := 125+I*25
Gui,  Show, x5 y5 w400 h%Gui_height%, SendTo
return

CheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 1
GuiControl,, Ch200, 1
GuiControl,, Ch201, 0
return

UnCheckAll:
Loop, %I%
    GuiControl,, Ch%A_Index%, 0
GuiControl,, Ch200, 0
GuiControl,, Ch201, 1
return

Copy:
Gui, submit
Activate(WinTitle " ahk_exe explorer.exe")
Selection := Explorer_GetSelection()
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
		GuiControlGet, folder,, Ch%A_Index%, Text
		{
			If !WinExist(folder " ahk_exe explorer.exe")
				Run %folder%
			Loop, Parse, Selection, `n
			{
				If InStr(FileExist(A_LoopField), "D")
				{
					SplitPath, A_LoopField, SourceFolderName
					FileCopyDir, %A_LoopField%, %folder%\%SourceFolderName%, 1
				}
				else
					FileCopy, %A_LoopField%, %folder%\, 1
			}
		}
		; Sleep, 500
    }
}
ExitApp
return

Move:
Gui, submit
Activate(WinTitle " ahk_exe explorer.exe")
Selection := Explorer_GetSelection()
Loop %I%
{
    GuiControlGet, checked,, Ch%A_Index%, Value
    If (checked = 1) ; if the control is checked
    {
		GuiControlGet, folder,, Ch%A_Index%, Text
		MsgBox, 262180, Move Files, Are you sure you want to move the selected files to`n`n"%folder%"?
		IfMsgBox No
			ExitApp
		If !WinExist(folder " ahk_exe explorer.exe")
			Run %folder%
		Loop, Parse, Selection, `n
		{
			If InStr(FileExist(A_LoopField), "D")
			{
				SplitPath, A_LoopField, SourceFolderName
				FileMoveDir, %A_LoopField%, %folder%\%SourceFolderName%, 1
			}
			else
				FileMove, %A_LoopField%, %folder%\, 1	
		}
		ExitApp  ; you can move the selected items in only one directory
    }
}
return

GuiClose:
ExitApp
return  

Activate(title){
	WinActivate %title%
	WinWaitActive %title%, , 2
	If ErrorLevel
	{
		MsgBox '%title%' not found.`n`nExiting script...
		ExitApp
	}
}

; https://www.autohotkey.com/boards/viewtopic.php?f=76&t=60403&p=255273#p255256
Explorer_GetSelection(){
   WinGetClass, winClass, % "ahk_id" . hWnd := WinExist("A")	; %
   if !(winClass ~="Progman|WorkerW|(Cabinet|Explore)WClass")
      Return	  
   shellWindows := ComObjCreate("Shell.Application").Windows
   if (winClass ~= "Progman|WorkerW")
      shellFolderView := shellWindows.FindWindowSW(0, 0, SWC_DESKTOP := 8, 0, SWFO_NEEDDISPATCH := 1).Document
   else {
      for window in shellWindows
       try  if (hWnd = window.HWND) && (shellFolderView := window.Document)
            break
   }
   for item in shellFolderView.SelectedItems
      result .= (result = "" ? "" : "`n") . item.Path
   if !result
      result := shellFolderView.Folder.Self.Path
   Return result
}

and add it to the explorer context menu.

_register Copy_Move_Files_To.reg

Code: Select all

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\AllFilesystemObjects\shell\Copy_Move selected files to ...]
@="Copy_Move selected files to ..."
"Icon"="C:\\WINDOWS\\System32\\SHELL32.dll,68"
"Position"="Top"

[HKEY_CLASSES_ROOT\AllFilesystemObjects\shell\Copy_Move selected files to ...\command]
@="\"D:\\Scripts\\EXPLORER\\Kontextmenue\\Copy_Move_Files\\Copy_Move_Files_To.exe\" \"%1\""

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Copy_Move all files to ...]
"MUIVerb"="Copy_Move all files to ..."
"Icon"="C:\\WINDOWS\\System32\\SHELL32.dll,68"
"Position"="Top"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\Copy_Move all files to ...\command]
@="\"D:\\Scripts\\EXPLORER\\Kontextmenue\\Copy_Move_Files\\Copy_Move_Files_To.exe\" \"%W\""
Replace
D:\\Scripts\\EXPLORER\\Kontextmenue\\Copy_Move_Files\\Copy_Move_Files_To.exe
with the path of your compiled script.


_unregister Copy_Move_Files_To.ahk

Code: Select all

RegDelete, HKEY_CLASSES_ROOT, AllFilesystemObjects\shell\Copy_Move selected files to ...

RegDelete, HKEY_CLASSES_ROOT, Directory\Background\Shell\Copy_Move all files to ...
or define a shortcut in your main script to run the script

or create a shortcut of the script on your desktop and assign a custom combination in its properties.
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

27 Apr 2020, 17:11

Incredible ! That's a very neat idea :clap:
I will test this soon, I first need to compile the AHK script.
On my work laptop I cannot do it, so need to use a different PC.
Wigi
Posts: 140
Joined: 05 Jun 2017, 10:52
Contact:

Re: Improved SendTo dialog

17 Jul 2020, 18:26

Hi,

After some time, coming back with my final approach and script.

Select the files / folders to be copied, press F6.
A ListView will appear with all folders as the target of shortcuts that are stored in a certain dedicated folder.
Then in the ListView, right-click a line and the files/folders will be moved to the selected folder. Double-click a line and the files/folders will be copied to the selected folder.

Here's the code, only in the beginning you should make some changes. %HO% is an environment variable on my PC and points to D:\OneDrive\..., it's the root directory where many of my folders and files reside.

Thanks again GEV !

Code: Select all

#SingleInstance Force

F6::
{
; Create the ListView with 3 columns: Nr, Long Name, Short name
Gui, Destroy
Gui, Add, ListView, vMyListView w600 gMyListView AltSubmit -Multi, Nr|Long|Short
Gui, +Resize +ToolWindow +Border

; Add in this list the path of the directories
folder_paths =
(
Desktop`t%A_Desktop%
)

; Add in this list the path of the directories that contain shortcuts.
; The targeted directories of the shortcuts are used in the script too.
folder_paths_to_loop =
(
%HO%\Wim\COPY_CUT
)


; PART 1: retrieve the selection of files
; retrieve the selection of the user: either files/folders, either the path in the File Explorer
v =
v := GetSelectedFileFolderNames_UnderCursor()
if !v
{
    v := GetSelectedFileFolderNames_FolderYouSee()
    if !v
      return
}
Loop, Parse, v, `n
{
    SplitPath, A_LoopField, filename, path
    path := InsertEnvVars(path)
    Gui,  Add, CheckBox, Checked Hidden x20 vCh%A_Index%, %filename% `t[%path%]
    J := A_Index ; number of selected files
}

; PART 2: list all target paths
i = 0
Loop, Parse, folder_paths, `n
{
    i++
    MyArray := StrSplit( A_LoopField, "`t" )
	LV_Add(, i, MyArray[1], MyArray[2] )
}
Loop, Parse, folder_paths_to_loop, `n
{
    Loop, Files, % ReplaceEnvVars(A_LoopField) . "\*", F
    {
        extensions := "lnk,"
        if A_LoopFileExt in %extensions%
        {
            FileGetShortcut, %A_LoopFileFullPath%, shortcutTargetPath
            If( InStr( FileExist(shortcutTargetPath), "D") )
            {
               SplitPath, A_LoopFileLongPath, , , , OutNameNoExt
               vFolder_Short := InsertEnvVars(shortcutTargetPath)
               i++
               LV_Add(, i, OutNameNoExt, vFolder_Short )
            }
        }
    }
}

LV_ModifyCol()  ; Auto-size each column to fit its contents
LV_ModifyCol(1, "Integer")  ; For sorting purposes, indicate that column 1 is an integer

hoogte := 20 * i
Gui, Show, h%hoogte%, Copy | Move to...
return

GUISize:
	width:=A_GuiWidth-15
	height:=A_GuiHeight-15
	guicontrol, move, MyListView, w%width% h%height%
return

MyListView:
Gui, submit, nohide
LV_GetText(shortcut, A_EventInfo, 3)
; RIGHTCLICK MEANS MOVE THE SELECTED FILES / FOLDERS
if A_GuiEvent = RightClick
{
   ; make sure at least 1 file is selected
   n := 0
   Loop %J%
   {
       GuiControlGet, checked,, Ch%A_Index%, Value
       If (checked = 1) { ; if the control is checked
            n := 1
            Break
       }
   }
   if (n = 0)
   {
      Msgbox Please select at least 1 file.
      return
   }
   
   vTarget_Path := RegexReplace( shortcut, ".*\[(.+?)\].*", "$1")
   Loop %J%
   {
       GuiControlGet, checked,, Ch%A_Index%, Value
       If (checked = 1) ; if the control is checked
       {
           GuiControlGet, file,, Ch%A_Index%, Text
           filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
           filepath := ReplaceEnvVars(filepath)
           filename := StrSplit(file, "[", " `t")[1]
           If (filepath != vTarget_Path)
              {
              vFullName = %filepath%\%filename%
              If( InStr( FileExist(vFullName), "D") )
                  FileMoveDir, %filepath%\%filename%, %vTarget_Path%\%filename%, 1  ; overwrite existing files
              Else
                  FileMove, %filepath%\%filename%, %vTarget_Path%, 1  ; overwrite existing files
              }
       }
   }
   Gui, Destroy
   return
}

; DOUBLECLICK MEANS COPY THE SELECTED FILES / FOLDERS
else if A_GuiEvent = DoubleClick
{
   ; make sure at least 1 file is selected
   n := 0
   Loop %J%
   {
       GuiControlGet, checked,, Ch%A_Index%, Value
       If (checked = 1) { ; if the control is checked
            n := 1
            Break
       }
   }
   if (n = 0)
   {
      Msgbox Please select at least 1 file.
      return
   }
   vTarget_Path := RegexReplace( shortcut, ".*\[(.+?)\].*", "$1")
   vTarget_Path := ReplaceEnvVars(vTarget_Path)
   Loop %J%
   {
       GuiControlGet, checked,, Ch%A_Index%, Value
       If (checked = 1) ; if the control is checked
       {
           GuiControlGet, file,, Ch%A_Index%, Text
           filepath := RegexReplace( file, ".*\[(.+?)\].*", "$1")
           filepath := ReplaceEnvVars(filepath)
           filename := StrSplit(file, "[", " `t")[1]
           ; if we copy files to the same folder, add a Copy suffix
           If (filepath = vTarget_Path)
           {
              SplitPath, filename, , , OutExtension, OutNameNoExt
              FileCopy, %filepath%\%filename%, %vTarget_Path%\%OutNameNoExt% - Copy.%OutExtension%, 1  ; overwrite existing files
           }
           Else
           {
              vFullName = %filepath%\%filename%
              If( InStr( FileExist(vFullName), "D") )
                  FileCopyDir, %vFullName%, %vTarget_Path%\%filename%, 1 ; overwrite existing files
              Else
                  FileCopy, %vFullName%, %vTarget_Path%, 1 ; overwrite existing files
           }
       }
   }
   Gui, Destroy
   return
}
return
}

ExpandEnvVars(sEV)
{
    VarSetCapacity(dest, 2000)
    DllCall("ExpandEnvironmentStrings", "str", sEV, "str", dest, int, 1999, "Cdecl int")
    return dest
}
return

ReplaceEnvVars(sLocation)
{
    if ( InStr( sLocation, "%HO%", false ) > 0 )
       sLocation := StrReplace(sLocation, "%HO%", ExpandEnvVars("%HO%"))
    return sLocation
}
return

InsertEnvVars(sLocation)
{
    if ( InStr( sLocation, ExpandEnvVars("%HO%"), false ) > 0 )
       sLocation := StrReplace(sLocation, ExpandEnvVars("%HO%"), "%HO%")
    return sLocation
}
return

GetSelectedFileFolderNames_UnderCursor()
{
    ToReturn =
    hwnd := hwnd ? hwnd : WinExist("A")
    WinGetClass class, ahk_id %hwnd%
    if (class="CabinetWClass" or class="ExploreWClass")
    {
        for window in ComObjCreate("Shell.Application").Windows
            if (window.hwnd==hwnd)
            {
               sel := window.Document.SelectedItems
               for item in sel
               {
                  v := % item.path
                  ToReturn .= v "`n"
               }
            }
    }
    else
    {
       files:={}    
       sel:=""
       if (class="Progman" or class="WorkerW")
          FolderPath = %A_Desktop%
       else if WinActive("ahk_class CabinetWClass")
          ; http://ahkscript.org/boards/viewtopic.php?p=28751#p28751
          for window in ComObjCreate("Shell.Application").Windows
              try FolderPath := window.Document.Folder.Self.Path
       ControlGet, win, HWND,, SysListView321, A
       ControlGet, sel, List, Selected Col1,,ahk_id %win%
       Loop, parse, sel, `n, `r
          files.push(A_LoopField)
       for each, file in files
       {
          FileGetAttrib, FileAttrib, %FolderPath%\%file%
          If InStr(FileAttrib, "D") ; a DIRECTORY
          {
             Loop, %FolderPath%\%file%, 2,0 ; only folders
             {
                StringReplace, FileFullPath, A_LoopFileFullPath, :\\ ,:\
                v = %FileFullPath%
                ToReturn .= v "`n"
             }
          }
          else
          {
             Loop, %FolderPath%\%file%.*
             {
                v := % A_LoopFileFullPath
                ToReturn .= v "`n"
             }
          }
       }
    }
    ToReturn := Trim(ToReturn,"`n")
    return ToReturn
}

GetSelectedFileFolderNames_FolderYouSee()
{
    ToReturn =
    ; WinGetClass, class, A
    hwnd := hwnd ? hwnd : WinExist("A")
    WinGetClass class, ahk_id %hwnd%
    if (class="CabinetWClass" or class="ExploreWClass")
    {
        ControlGetText, currentpath, ToolbarWindow323, ahk_class %class%
        StringTrimLeft, currentpath, currentpath, 9
        if( SubStr( currentpath, 0, 1) == "\")
           currentpath := SubStr(currentpath, 1, StrLen( currentpath ) - 1 )
        ToReturn .= currentpath "`n"
        ToReturn := Trim(ToReturn,"`n")
        Return ToReturn
    }
    else if (class="Progman" or class="WorkerW")
    {
        currentpath = %A_Desktop%
        ToReturn .= currentpath "`n"
        ToReturn := Trim(ToReturn,"`n")
        Return ToReturn
    }
    Return
}

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Assquatch23, Descolada, Google [Bot] and 348 guests