QuickLinks (Menu by folder)

Post your working scripts, libraries and tools
AHK_user
Posts: 33
Joined: 04 Dec 2015, 14:52
Location: Belgium

QuickLinks (Menu by folder)

23 Oct 2019, 15:32

This code is my second adaptation of QuickLinks.ahk, by Jack Dunning.
http://www.computoredge.com/AutoHotkey/AutoHotkey_Quicklinks_Menu_App.html
It is a usefull script, it takes a directory and uses it to make a menu of shortcuts.
This makes it easy to add and remove links in the menu, just change the links in the directory, even a user that does not understands ahk can use and modify the menu.

My first adaptation is made because the original script did not handled subfolders within subfolders by looping inside the directory and searching the first file that contained "\%A_ThisMenu%\%A_ThisMenuItem%" .

Recently it became possible to call functions with parameters with a menuitem.
This makes the handling of subfolders within subfolders even more simple, so the code is short and usefull.
(BoundRun := Func("Run").Bind(A_Loopfilefullpath))

When no %A_ScriptDir%\Links folder is found, it will create it with a shortcut to itself, so you get a menu of one item.

Previous limitation is fixed, the subfolders may not have the same name now

Special thanks to swagfag, who taught me how to use a BoundFunc Object for menus.

Updates 2019-10-24:
- Added name of menu as variable
- Changed functions to V2 compatible
- Submenu's with the same name are allowed now

Updates 2019-12-9:
- Changed Icon_Add function to check if icon is present.
- Changed Icon_Add function to be more flexible.

Code: Select all

;
; This code is an adaptation of QuickLinks.ahk, by Jack Dunning.
; http://www.computoredge.com/AutoHotkey/AutoHotkey_Quicklinks_Menu_App.html
; Update 2019-10-24:
;- Added name of menu as variable 
;- Changed functions to V2 compatible
;- Submenu's with the same name are allowed now

#NoEnv
#SingleInstance, Force

QL_CreateMenu()

Space::
QL_Show()
return

QL_CreateMenu(QL_Link_Dir := "Links") { ; Just run it one time at the start.
	If !InStr(QL_Link_Dir, "\"){
		QL_Link_Dir := A_ScriptDir "\" QL_Link_Dir 
	}
	
	SplitPath, QL_Link_Dir,QL_Menu
	
	If !FileExist(QL_Link_Dir){
		FileCreateDir, %QL_Link_Dir%
	}
	FileCreateShortcut, %QL_Link_Dir%, %QL_Link_Dir%\%QL_Menu%.lnk
	
	Loop, %QL_Link_Dir%\*.*, 1 , 1
	{	
		if InStr(A_LoopFileAttrib, "H") or InStr(A_LoopFileAttrib, "R"),  or InStr(A_LoopFileAttrib, "S") ;Skip any file that is H, R, or S (System).
			continue
		
		Folder1  := RegExReplace(A_Loopfilefullpath, "(.*\\[^\\]*)\\([^\\]*)\\([^\\]*)", "$2")
		Folder1Menu  := QL_Menu StrReplace(StrReplace(RegExReplace(A_Loopfilefullpath, "(.*\\[^\\]*\\[^\\]*)\\([^\\]*)", "$1"), QL_Link_Dir), "\")
		Folder2Menu  := QL_Menu StrReplace(StrReplace(RegExReplace(A_Loopfilefullpath, "(.*\\[^\\]*)\\([^\\]*)\\([^\\]*)", "$1"), QL_Link_Dir), "\")
		
		BoundRun := Func("Run").Bind(A_Loopfilefullpath)
		Linkname := StrReplace(A_LoopFileName, ".lnk")
		
		Menu, %Folder1Menu%, Add, %Linkname% , %BoundRun%
		Icon_Add(Folder1Menu,Linkname,A_LoopFileFullPath) ; icon
		Menu , %Folder2Menu%, Add, %Folder1%, :%Folder1Menu% ; Create submenu
		Menu , %Folder2Menu% , Icon , %Folder1% , C:\Windows\syswow64\SHELL32.dll , 5 ; icon for folder
	}
	return
}

QL_Show(Link_Name:= "Links") {
	Menu, %Link_Name%, Show
}

Run( a) {
	run, %a%
}

Icon_Add(menuitem,submenu,LoopFileFullPath){ ; add icon based on extention or name
	If InStr(LoopFileFullPath, ".lnk"){
		FileGetShortcut,%LoopFileFullPath% ,File, , , , OutIcon, OutIconNum
		if (OutIcon!=""){
			Menu, %menuitem%, Icon, %submenu%, %OutIcon%,%OutIconNum%
			return
		}
	}
	Else{
		File := LoopFileFullPath
	}
	
	RegExReplace(File,"\.", "",Number_Dots) ;counts the dots
	Extension  := RegExReplace(File, "([^\.]*)(\.[^\.]*).*", "$2")
	Extension2  := RegExReplace(LoopFileFullPath, "([^\.]*)(\..*)", "$2")
	Icon_nr = 0
	If (Extension = ".exe"){
		Menu, %menuitem%, Icon, %submenu%, %file%,1
		return
	}
	
	IconFile := StrSplit(getExtIcon(StrReplace(Extension, ".")), ",")
	
	If InStr(Extension, "\"){
		Menu, %menuitem%, Icon, %submenu%, C:\Windows\syswow64\SHELL32.dll , 5
	}
	Else If (Extension = ".ahk")
		Menu, %menuitem%, Icon, %submenu%, autohotkey.exe,2
	Else If (Extension = ".jpg")
		Menu, %menuitem%, Icon, %submenu%, %A_Windir%\system32\SHELL32.dll, 140,
	Else If (Extension = ".pdf" and FileExist(A_ScriptDir "\Icons\PDF.ico"))
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\PDF.ico
	Else If InStr(Extension,  ".xls") and FileExist(A_ScriptDir "\Icons\xlicons.exe")
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\xlicons.exe, 0
	Else If InStr(Extension,  ".doc") and FileExist(A_ScriptDir "\Icons\wordicon.exe")
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\wordicon.exe, 0
	Else If InStr(Extension,  ".ppt") and FileExist(A_ScriptDir "\Icons\ppticon.exe")
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\ppticon.exe, 0
	Else If InStr(A_LoopFileFullPath, ".website") and FileExist(A_ScriptDir "\Icons\ielowutil.exe")
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\ielowutil.exe, 0
	Else If (Extension = ".txt")
		Menu, %menuitem%, Icon, %submenu%, C:\Windows\syswow64\Notepad.exe , 0
	Else If (Extension = ".pro")
		Menu, %menuitem%, Icon, %submenu%, C:\Windows\syswow64\Notepad.exe , 0
	Else If (Extension2 = ".url") and InStr(A_LoopFileLongPath,"Windchill") and FileExist(A_ScriptDir "\Icons\Windchill.ico"){
		Menu, %menuitem%, Icon, %submenu%, %A_ScriptDir%\Icons\Windchill.ico	
	}
	Else If (IconFile[1]!=""){
		Menu, %menuitem%, Icon, %submenu%, % IconFile[1] ,  % IconFile[2] 
		return
	}
    ; Find hidden icons in the following directory: C:\Windows\Installer
	Return
}

getExtIcon(Ext) {
   I1 := I2:= ""
   RegRead, from, HKEY_CLASSES_ROOT, .%Ext%
   RegRead, DefaultIcon, HKEY_CLASSES_ROOT, %from%\DefaultIcon
   StringReplace, DefaultIcon, DefaultIcon, `",,all
   StringReplace, DefaultIcon, DefaultIcon, `%SystemRoot`%, %A_WinDir%,all
   StringReplace, DefaultIcon, DefaultIcon, `%ProgramFiles`%, %A_ProgramFiles%,all
   StringReplace, DefaultIcon, DefaultIcon, `%windir`%, %A_WinDir%,all
   StringSplit, I, DefaultIcon, `,
Return I1 "," IndexOfIconResource( I1, RegExReplace(I2, "[^\d]+")  )
}

IndexOfIconResource(Filename, ID){
    hmod := DllCall("GetModuleHandle", "str", Filename, "PTR")
    ; If the DLL isn't already loaded, load it as a data file.
    loaded := !hmod
        && hmod := DllCall("LoadLibraryEx", "str", Filename, "PTR", 0, "uint", 0x2)
    
    enumproc := RegisterCallback("IndexOfIconResource_EnumIconResources","F")
    VarSetCapacity(param,12,0)
    NumPut(ID,param,0)
    ; Enumerate the icon group resources. (RT_GROUP_ICON=14)
    DllCall("EnumResourceNames", "uint", hmod, "uint", 14, "uint", enumproc, "PTR", &param)
    DllCall("GlobalFree", "PTR", enumproc)
    
    ; If we loaded the DLL, free it now.
    if loaded
        DllCall("FreeLibrary", "PTR", hmod)
    
    return NumGet(param,8) ? NumGet(param,4) : 0
}

ResourceIdOfIcon_EnumIconResources(hModule, lpszType, lpszName, lParam){
    index := NumGet(lParam+4)
    if (index = NumGet(lParam+0))
    {   ; for named resources, lpszName might not be valid once we return (?)
        ; if (lpszName >> 16 == 0), lpszName is an integer resource ID.
        NumPut(lpszName, lParam+4)
        NumPut(1, lParam+8)
        return false    ; break
    }
    NumPut(index+1, lParam+4)
    return true
}
QuickLinks.png
QuickLinks.png (39.99 KiB) Viewed 775 times
Last edited by AHK_user on 09 Dec 2019, 16:17, edited 5 times in total.
User avatar
sumon
Posts: 24
Joined: 01 Oct 2013, 14:05
Location: Sweden

Re: QuickLinks (Menu by folder)

24 Oct 2019, 09:54

Very cool! I think this can be useful.

One thing I noticed though, is that I would want to be able to pass an input parameter (the folder for which to create a menu) and return the menu name as an output of that function. That way, the QL_Show can be called using that parameter (right now, the menu name is always "Links").
AHK_user
Posts: 33
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: QuickLinks (Menu by folder)

24 Oct 2019, 14:56

sumon wrote:
24 Oct 2019, 09:54
Very cool! I think this can be useful.

One thing I noticed though, is that I would want to be able to pass an input parameter (the folder for which to create a menu) and return the menu name as an output of that function. That way, the QL_Show can be called using that parameter (right now, the menu name is always "Links").
Thanks :D

I have added as parameter the linkfolder, this gives you even more flexibility, the name of the menu is the same as the name of the folder so the return is not necessary.
I also cleaned up the legacy syntax so it should also work in AHK V2.

I use it a lot, I actually like to identify one key as a "script key" (XButton1 is preferred), that is used to display a menu to access usefull scripts based on the active program and selection.
This Link menu displays when I press this key on the taskbar.
AHK_user
Posts: 33
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: QuickLinks (Menu by folder)

24 Oct 2019, 15:20

New update, I fixed the problem of not allowing subfolders with the same name.

One thing I am still wondering, would it be possible to remove the Run(a) function and directy create a Boundobject with the standard Run function?
godofOOF
Posts: 26
Joined: 22 Dec 2018, 06:03

Re: QuickLinks (Menu by folder)

07 Dec 2019, 10:03

i keep getting a cant load icon error
burque505
Posts: 1073
Joined: 22 Jan 2017, 19:37

Re: QuickLinks (Menu by folder)

07 Dec 2019, 17:25

@AHK_user , this is great stuff. Definitely a startup script for me from now on. This will save me a lot of time every single day.

(Thanks to @jack dunning as well, of course!)

I just noticed you can add accelerator keys.

The menu itself with shortcuts added:
example.PNG
example.PNG (7.11 KiB) Viewed 254 times
The "Links" folder showing shortcuts:
example 2.PNG
example 2.PNG (8.78 KiB) Viewed 254 times
Adding accelerator to Amazon link:
example3.PNG
example3.PNG (35.93 KiB) Viewed 254 times
Je wordt hartstikke bedankt!!!!

Best regards,
burque505
AHK_user
Posts: 33
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: QuickLinks (Menu by folder)

09 Dec 2019, 06:41

godofOOF wrote:
07 Dec 2019, 10:03
i keep getting a cant load icon error
If you read the code, you can see that I use a subfolder that holds some icons for me.
(You can normally find them in C:\Windows\Installer)

I modified the code a little bit, normally you would not recieve an error anymore, it will just not show the icon if not found.

If somebody knows a nice code to directly use the icon of the file, feel free to share it.
User avatar
jackdunning
Posts: 83
Joined: 01 Aug 2016, 18:17
Facebook: https://www.facebook.com/ComputorEdgeEBooks/posts
Contact:

Re: QuickLinks (Menu by folder)

09 Dec 2019, 15:07

If you check out the script QuickLinks.ahk script at the ComputorEdge Free AutoHotkey Scripts page, you'll find a number of different techniques for adding icons to the menu items. It's been a while but the line:

Code: Select all

IfInString, QL_OutTarget, exe, Menu, %QL_MainMenu%, Icon, %QL_NewName%, %QL_OutTarget%, 1
takes the icon from the Windows shortcut for EXE targets.

The blog "Using Unique Icons for Specific Windows Shortcuts" might help.
AHK_user
Posts: 33
Joined: 04 Dec 2015, 14:52
Location: Belgium

Re: QuickLinks (Menu by folder)

09 Dec 2019, 17:13

jackdunning wrote:
09 Dec 2019, 15:07
If you check out the script QuickLinks.ahk script at the ComputorEdge Free AutoHotkey Scripts page, you'll find a number of different techniques for adding icons to the menu items. It's been a while but the line:

Code: Select all

IfInString, QL_OutTarget, exe, Menu, %QL_MainMenu%, Icon, %QL_NewName%, %QL_OutTarget%, 1
takes the icon from the Windows shortcut for EXE targets.

The blog "Using Unique Icons for Specific Windows Shortcuts" might help.
Thanks for the tips, Jack, I feel honoured to get a reply from you, I learned a lot of your examples.

I added 3 extra icon retrieval methods as improvements:
- Retrieval of the icons with the function FileGetShortcut, It appears only to work when you changed the icon of the link, but can be usefull in that way.
- Using the exe file, also seems to works quite well.
- I have also added another function that should try to retrieve the icons, this should eliminate the need to retrieve the icons manually in a subfolder. This works well, except for txt files, probably the iconnumber is wrong in that case... (source: https://autohotkey.com/board/topic/87161-path-icon/)

Return to “Scripts and Functions”

Who is online

Users browsing this forum: aifritz and 54 guests