commit 55b289a77a59170f10373356ee84621c1e381250 Author: jolheiser Date: Thu Apr 21 15:14:50 2022 -0500 Initial commit Signed-off-by: jolheiser diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62c8935 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..64e99ab --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,27 @@ +pipeline: + compile-util: + image: jolheiser/woodpecker-ahk:latest + settings: + script: src/NavigatorUtil.ahk + icon: icon.ico + out: src/NavigatorUtil.exe + compile-main: + image: jolheiser/woodpecker-ahk:latest + settings: + script: src/Navigator.ahk + icon: icon.ico + out: src/Navigator.exe + compile-installer: + image: jolheiser/woodpecker-ahk:latest + settings: + script: src/Navigator-Install.ahk + icon: icon.ico + out: src/Navigator-Install.exe + release-main: + image: jolheiser/drone-gitea-main:latest + settings: + base: https://git.jojodev.com + token: + from_secret: gitea_token + files: + - "src/Navigator-Install.exe" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ec2045e --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2022 John Olheiser + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..62e7e65 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Navigator + +![icon](icon.ico) + +An AHK script to allow quick movement to specified directories. + +## License + +[MIT](LICENSE) \ No newline at end of file diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000..8221de0 Binary files /dev/null and b/icon.ico differ diff --git a/src/CheckReg.ahk b/src/CheckReg.ahk new file mode 100644 index 0000000..ca5857b --- /dev/null +++ b/src/CheckReg.ahk @@ -0,0 +1,9 @@ +RegRead, k_test, HKCR, Directory\shell\Navigator_Add +If k_test = +{ + MsgBox, 36, Navigator, Would you like Navigator to add its options to folder/background context menus? + IfMsgBox, Yes + { + Run, %k_base%\NavigatorUtil.exe WriteReg + } +} \ No newline at end of file diff --git a/src/CheckReg.bat b/src/CheckReg.bat new file mode 100644 index 0000000..0e462cb --- /dev/null +++ b/src/CheckReg.bat @@ -0,0 +1,2 @@ +@ECHO OFF +start "" %~dp0\NavigatorUtil.exe CheckReg \ No newline at end of file diff --git a/src/Navigator-Install.ahk b/src/Navigator-Install.ahk new file mode 100644 index 0000000..8a29066 --- /dev/null +++ b/src/Navigator-Install.ahk @@ -0,0 +1,35 @@ +#Include options.ahk + +;CHECK CONFIG +Gosub, CheckConfig + +return + + +CheckConfig: + IfExist, %k_config% + { + IniRead, k_ver, %k_config%, System, Version, 0.0.0 + If (k_version != k_ver) + { + IfExist, %k_base%\Uninstall.bat + { + MsgBox, 16, Navigator, Please run the uninstaller before updating Navigator!`nThis ensures a clean install with no orphaned objects.`n`nUninstaller location: %k_base%\Uninstall.bat + ExitApp + } + } + } + FileCreateDir, %k_base% + FileInstall, config.ini, %k_config%, 0 + IniWrite, %k_version%, %k_config%, System, Version + FileInstall, Navigator.exe, %k_base%\Navigator.exe, 1 + FileInstall, NavigatorUtil.exe, %k_base%\NavigatorUtil.exe, 1 + FileInstall, CheckReg.bat, %k_base%\CheckReg.bat, 1 + FileInstall, Uninstall.bat, %k_base%\Uninstall.bat, 1 + #Include CheckReg.ahk + MsgBox, 36, Navigator, Installed successfully!`nWould you like to start Navigator? + IfMsgBox, Yes + { + Run, %k_base%\Navigator.exe + } +return \ No newline at end of file diff --git a/src/Navigator.ahk b/src/Navigator.ahk new file mode 100644 index 0000000..f4b19a8 --- /dev/null +++ b/src/Navigator.ahk @@ -0,0 +1,254 @@ +#SingleInstance Force +#Include options.ahk + +;INIT MENU +Menu, NavMenu, Add + +;(RE)LOAD CONFIG +Gosub, ReloadConfig + +;INIT HOTKEY +Hotkey, #n, DisplayNavMenu + +;GENERATE ADDRESS CALLBACK +k_address := RegisterCallback("ReloadConfigCallback") +IniWrite, %k_address%, %k_config%, System, Address + +;MONITOR CONFIG +FileRead, k_monitor, %k_config% +SetTimer, MonitorConfig, 1000 + +return + +MonitorConfig: + FileRead, k_monitor_temp, %k_config% + if (k_monitor_temp != k_monitor) + { + k_monitor := k_monitor_temp + GoSub, ReloadConfig + TrayTip, Navigator, Config Reloaded + } +return + +ReloadConfigCallback() { + GoSub, ReloadConfig + TrayTip, Navigator, Config Reloaded +} + +ReloadConfig: + Gosub, GenerateNavMenu + Gosub, GenerateTrayMenu +return + +GenerateTrayMenu: + Menu, TRAY, DeleteAll + Menu, TRAY, Tip, Navigator + Menu, TRAY, NoStandard + Menu, TRAY, Add, Navigator v%k_version%, Help + Menu, TRAY, Default, Navigator v%k_version% + Menu, TRAY, Add + Menu, TRAY, Add, &Navigator, :NavMenu + fn := Func("ExploreTo").Bind(k_config, true) + Menu, SystemMenu, Add, Lock Menus, Lock + If k_lock + Menu, SystemMenu, Check, Lock Menus + Menu, SystemMenu, Add, Edit Config, %fn% + ;Menu, SystemMenu, Add, Reload Config, ReloadConfig + Menu, SystemMenu, Add, Close, Close + Menu, TRAY, Add, &System, :SystemMenu +return + + +Lock: + If k_lock { + k_lock := false + } else { + k_lock := true + } + Menu, SystemMenu, ToggleCheck, Lock Menus + GoSub, GenerateNavMenu +return + +GenerateNavMenu: + Gui, Menu + Menu, NavMenu, DeleteAll + CreateMenu("NavMenu") +return + +CreateMenu(menu_name) { + global k_config, k_lock, k_ext_pattern + IniRead, k_nav_config, %k_config%, %menu_name% + If (k_nav_config == "") + { + MsgBox,, %menu_name%, No paths found for this sub-menu, please add some to the config. + } + StringSplit, k_nav, k_nav_config, `n + Loop, %k_nav0% + { + StringSplit, k_nav_array, k_nav%A_Index%, `, + If (k_nav_array0 > 1) + { + ; We have a path to work with + fn := Func("ExploreTo").Bind(k_nav_array2) + k_pos := RegExMatch(k_nav_array2, k_ext_pattern) + If (k_pos > 0) + { + Menu, %menu_name%, Add, %k_nav_array1%, %fn%, +Radio + Menu, %menu_name%, Check, %k_nav_array1% + } else { + Menu, %menu_name%, Add, %k_nav_array1%, %fn% + } + + } else { + ; No path, so this is a new menu + Menu, %k_nav_array1%, Add + Menu, %k_nav_array1%, DeleteAll + k_menu_name := k_nav_array1 + k_menu_ini := k_nav_array1 + StringSplit, k_menu_array, k_menu_name, | + If (k_menu_array0 > 1) + { + k_menu_name := k_menu_array2 + k_menu_ini := k_menu_array1 + } + CreateMenu(k_menu_ini) + Menu, %menu_name%, Add, %k_menu_name%, :%k_menu_ini% + } + } + If not k_lock { + Menu, %menu_name%, Add + fn := Func("AddMenuItem").Bind(menu_name) + Menu, %menu_name%, Add, Add, %fn% + } +} + +DisplayNavMenu: + WinGet, k_window_id, ID, A + WinGetClass, k_class, ahk_id %k_window_id% + ControlGetFocus, k_control, ahk_id %k_window_id% + if k_class in ExploreWClass,CabinetWClass + { + ; Try to activate the edit, this sometimes fixes weird issues + SendInput, {AltDown}d{AltUp}{Esc} + ControlGetPos, k_Edit1Pos,,,, Edit1, ahk_id %k_window_id% + } else if k_class in #32770 + { + ControlGetPos, k_Edit2Pos,,,, Edit2, ahk_id %k_window_id% + } + if k_class in #32770,ExploreWClass,CabinetWClass ; Dialog or Explorer. + { + if k_Edit1Pos = ; The control doesn't exist + { + WinActivate, ahk_id %k_window_id% + if k_class in ExploreWClass,CabinetWClass + ControlGetPos, k_Edit1Pos,,,, Edit1, ahk_id %k_window_id% + else if k_class in #32770 + ControlGetPos, k_Edit2Pos,,,, Edit2, ahk_id %k_window_id% + } + } + Menu, NavMenu, Show +return + +AddMenuItem(menu_name) { + global + InputBox, k_label, Navigator, %menu_name%`nPlease enter the name of the new menu item + InputBox, k_path, Navigator, %menu_name%`nPlease enter the path to the new menu item`nIf this is a new sub-menu`, leave this blank + If (k_path == "") + IniWrite, %k_label%, %k_config%, %menu_name% + Else + IniWrite, %k_label%`,%k_path%, %k_config%, %menu_name% + GoSub, ReloadConfig +} + +ExploreTo(location) { + global + RegExMatch(location, k_branch_pattern, k_match) + if k_match <> + { + InputBox, k_replacement, Navigator, %k_match2% + if k_replacement <> + { + location := StrReplace(location, k_match1, k_replacement) + } else { + return + } + } + if k_class = #32770 ; It's a dialog. + { + if k_Edit2Pos <> ; And it has an Edit2 control. + { + ; Activate the window so that if the user is middle-clicking + ; outside the dialog, subsequent clicks will also work: + WinActivate ahk_id %k_window_id% + ; Retrieve any filename that might already be in the field so + ; that it can be restored after the switch to the new folder: + ControlGetText, k_text, Edit1, ahk_id %k_window_id% + ControlSetText, Edit2, %location%, ahk_id %k_window_id% + ControlSend, Edit2, {Enter}, ahk_id %k_window_id% + Sleep, 100 ; It needs extra time on some dialogs or in some cases. + ControlSetText, Edit1, %k_text%, ahk_id %k_window_id% + GoSub, Cleanup + return + } + ; else fall through to the bottom of the subroutine to take standard action. + } + else if k_class in ExploreWClass,CabinetWClass ; In Explorer, switch folders. + { + if k_Edit1Pos <> ; And it has an Edit1 control. + { + ControlSetText, Edit1, %location%, ahk_id %k_window_id% + ; Tekl reported the following: "If I want to change to Folder L:\folder + ; then the addressbar shows http://www.L:\folder.com. To solve this, + ; I added a {right} before {Enter}": + ControlSend, Edit1, {Right}{Enter}, ahk_id %k_window_id% + GoSub, Cleanup + return + } + ; else fall through to the bottom of the subroutine to take standard action. + } + else if k_class in ConsoleWindowClass,TMobaXTermForm,mintty ; In a console window, CD to that directory + { + WinActivate, ahk_id %k_window_id% ; Because sometimes the mclick deactivates it. + SetKeyDelay, 0 ; This will be in effect only for the duration of this thread. + IfInString, k_path, : ; It contains a drive letter + { + StringLeft, k_path_drive, location, 1 + Send %k_path_drive%:{enter} + } + Send, cd "%location%"{Enter} + GoSub, Cleanup + return + } + else if InStr(k_control, "Edit") == 1 ; If Navigator is called from an edit control, modify it + { + ControlSetText, %k_control%, %location%, ahk_id %k_window_id% + ControlSend, %k_control%, {Right}{Enter}, ahk_id %k_window_id% + GoSub, Cleanup + return + } + GoSub, Cleanup + Run, Explorer %location% +} + +Cleanup: + k_window_id= + k_class= + k_control= +return + +Help: + RegRead, k_reg, HKCR, Directory\shell\Navigator_Add + k_reg_help= + if k_reg <> + { + k_reg_help = `n`nRegistry Extension:`n- New context menu options to add a file/folder to Navigator`n- New context menu options to copy a file/folder's path to the clipboard + } + MsgBox, 32, Navigator, Navigator v%k_version%`nThis program was created to aid in quickly navigating to common directories/files.`n`nFeatures:`n- Infinite sub-menus`; Organize your navigations as much as you want.`n- Simple naming`; To rename a folder, simple add a pipe (|) followed by the preferred name`n- Window detection`; In File Explorer`, switch to the directory. In command prompt`, cd to the directory. `n- Hotkey support`; Press the windows key + N to bring up the navigator menu.`n- If the path is a file instead of a directory, Navigator will open the file.*`n`n* File paths will be denoted by a radio mark next to their entry.%k_reg_help% +return + +;This is the only way to actually close the application +Close: + MsgBox, 52, Navigator, Are you sure you want to close the Navigator? + IfMsgBox, YES + ExitApp +return \ No newline at end of file diff --git a/src/NavigatorUtil.ahk b/src/NavigatorUtil.ahk new file mode 100644 index 0000000..14f2ffb --- /dev/null +++ b/src/NavigatorUtil.ahk @@ -0,0 +1,171 @@ +#SingleInstance Off +#Include options.ahk + +;INIT MENU +Menu, NavMenu, Add + +k_args_len=%0% + +if (k_args_len == 0) +{ + MsgBox,, NavigatorUtil, No arguments given. Specify "CheckReg", "AddItem", "Copy", or "Uninstall" + ExitApp +} + +Loop, %0% +{ + if (A_Index > 1) + { + k_arg := %A_Index% + k_index := A_Index - 1 + k_args%k_index% := k_arg + } +} +GoSub, %1% +ExitApp + +CheckReg: + #Include CheckReg.ahk +return + +WriteReg: + if not (A_IsAdmin) + { + ; Circular loop for admin privileges + Run *RunAs "%A_ScriptFullPath%" WriteReg /restart + ExitApp + } + + ; Add to Navigator contexts + + ; Folder context + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Add,, Add folder to Navigator + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Add, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Add\command,, "%A_ScriptFullPath%" "AddItem" "`%V" + + ; Background context + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Add,, Add folder to Navigator + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Add, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Add\command,, "%A_ScriptFullPath%" "AddItem" "`%V" + + ; File context + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Add,, Add file to Navigator + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Add, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Add\command,, "%A_ScriptFullPath%" "AddItem" "`%1" + + + ; Copy location contexts + + ; Folder context + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Copy,, Copy folder location + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Copy, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, Directory\shell\Navigator_Copy\command,, "%A_ScriptFullPath%" "Copy" "`%V" + + ; Background context + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Copy,, Copy folder location + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Copy, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, Directory\Background\shell\Navigator_Copy\command,, "%A_ScriptFullPath%" "Copy" "`%V" + + ; File context + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Copy,, Copy file location + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Copy, Icon, "%A_ScriptFullPath%" + RegWrite, REG_SZ, HKCR, *\shell\Navigator_Copy\command,, "%A_ScriptFullPath%" "Copy" "`%1" + +return + +Uninstall: + RegRead, k_reg, HKCR, Directory\shell\Navigator_Add + if k_reg <> + { + if not (A_IsAdmin) + { + ; Circular loop for admin privileges + Run *RunAs "%A_ScriptFullPath%" Uninstall /restart + ExitApp + } + } + + MsgBox, 35, Navigator, Uninstalling Navigator...`nWould you like to keep your config.ini? + IfMsgBox, Cancel + { + ExitApp + } else { + RegDelete, HKCR, Directory\shell\Navigator_Add + RegDelete, HKCR, Directory\Background\shell\Navigator_Add + RegDelete, HKCR, *\shell\Navigator_Add + RegDelete, HKCR, Directory\shell\Navigator_Copy + RegDelete, HKCR, Directory\Background\shell\Navigator_Copy + RegDelete, HKCR, *\shell\Navigator_Copy + } + IfMsgBox, Yes + { + Run, %ComSpec% /c "TASKKILL /f /im `"Navigator.exe`" > nul 2> nul & SLEEP 1 > nul 2> nul & DEL %A_ScriptDir%\*.bat > nul 2> nul & DEL %A_ScriptDir%\*.exe > nul 2> nul" ; Batch files and exe files + } else IfMsgBox, No + { + Run, %ComSpec% /c "TASKKILL /f /im `"Navigator.exe`" > nul 2> nul & SLEEP 1 > nul 2> nul & RD /S /Q %A_ScriptDir% > nul 2> nul" + } +return + +AddItem: + Gui, Menu + Menu, NavMenu, DeleteAll + CreateMenu("NavMenu") + Menu, NavMenu, Show +return + +Copy: + Clipboard = %k_args1% +return + +ReloadConfig: + IniRead, k_address, %k_config%, System, Address + DllCall(%k_address%) +return + +Select(menu_name) { + global k_config, k_args1 + InputBox, k_label, Navigator, %menu_name%`nPlease enter the name of the new menu item + if (k_label <> "") + { + IniWrite, %k_label%`,%k_args1%, %k_config%, %menu_name% + GoSub, ReloadConfig + } else { + MsgBox, 21, Navigator, You must enter a name for this sub-menu. + IfMsgBox, Retry + { + Select(menu_name) + } + } +} + +CreateMenu(menu_name) { + global k_config, k_ext_pattern + IniRead, k_nav_config, %k_config%, %menu_name% + If (k_nav_config == "") + { + MsgBox,, %menu_name%, No paths found for this sub-menu, please add some to the config. + } + StringSplit, k_nav, k_nav_config, `n + Loop, %k_nav0% + { + StringSplit, k_nav_array, k_nav%A_Index%, `, + If (k_nav_array0 == 1) + { + ; No path, so this is a new menu + Menu, %k_nav_array1%, Add + Menu, %k_nav_array1%, DeleteAll + k_menu_name := k_nav_array1 + k_menu_ini := k_nav_array1 + StringSplit, k_menu_array, k_menu_name, | + If (k_menu_array0 > 1) + { + k_menu_name := k_menu_array2 + k_menu_ini := k_menu_array1 + } + CreateMenu(k_menu_ini) + Menu, %menu_name%, Add, %k_menu_name%, :%k_menu_ini% + } + } + fn := Func("Select").Bind(menu_name) + Menu, %menu_name%, Add, Select This Menu, %fn% +} \ No newline at end of file diff --git a/src/Uninstall.bat b/src/Uninstall.bat new file mode 100644 index 0000000..1dc0317 --- /dev/null +++ b/src/Uninstall.bat @@ -0,0 +1,2 @@ +@ECHO OFF +start "" %~dp0\NavigatorUtil.exe Uninstall \ No newline at end of file diff --git a/src/config.ini b/src/config.ini new file mode 100644 index 0000000..ada193d --- /dev/null +++ b/src/config.ini @@ -0,0 +1,19 @@ +; To re-run the registry check, go to your config folder and run CheckReg.bat +; To uninstall, go to your config folder and run Uninstall.bat + +[NavMenu] +; Without a path, Navigator will assume the next line corresponds with a sub-menu +; Notice the pipe (|) used to rename the sub-menu. Each sub-menu needs to match a specific "header" in the ini file, but sometimes the names double up, so renaming can look better visually. +Config|NavMenu + +[Config] +; A sub-menu for the above matching line in NavMenu + +; Notice this path leads directly to a file, so Navigator will mark it in the sub-menu with a radio button mark +Config File,C:\Applications\Navigator\config.ini + +; If the path is a directory, Navigator will open to that folder. This is really the core of the project and is supported in File Explorer, Command Line, and MobaXTerm. +Config Folder,C:\Applications\Navigator + +[System] +; DO NOT MODIFY \ No newline at end of file diff --git a/src/options.ahk b/src/options.ahk new file mode 100644 index 0000000..f94fc41 --- /dev/null +++ b/src/options.ahk @@ -0,0 +1,9 @@ +;SYSTEM +k_version=2.0.5 ; Version number + +;OPTIONS +k_base=C:\Applications\Navigator ; Base path to the application folder +k_config=%k_base%\config.ini ; Where to store the config file +k_lock=true ; Whether to show the 'Add' options on menus/sub-menus initially +k_branch_pattern=.*(<(.+)>).* ; The regex pattern to match for branching paths +k_ext_pattern=\w+\.\w+$ ; The regex pattern to match for file extensions \ No newline at end of file