├── docs ├── Images │ ├── UsingManyCommands.png │ ├── UsingPresetParameters.png │ ├── RunWindowWithShellStartup.png │ ├── UsingManyCommandsWithPrefix.png │ ├── AHKCommandPicker-AllCommands.png │ ├── AHKCommandPicker-FilteredCommands.png │ └── UsersStartupDirectoryWithShortcut.png ├── Contributing.md ├── DocumentationHomePage.md ├── WhyUseAhkCommandPicker.md ├── BestPractices.md ├── MigrateFromV1ToV2.md ├── Changelog.md ├── UsingCommandsWithParameters.md ├── TipsAndTricks.md ├── AddingMultipleCommandsAtOnce.md └── GettingStarted.md ├── .editorconfig ├── .github └── FUNDING.yml ├── .gitattributes ├── src ├── UserCommands │ ├── MyHotkeys.ahk │ └── MyCommands.ahk ├── DefaultCommands │ ├── DefaultHotkeys.ahk │ ├── UtilityFunctions.ahk │ └── DefaultCommands.ahk └── AHKCommandPicker.ahk ├── License.md └── ReadMe.md /docs/Images/UsingManyCommands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/UsingManyCommands.png -------------------------------------------------------------------------------- /docs/Images/UsingPresetParameters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/UsingPresetParameters.png -------------------------------------------------------------------------------- /docs/Images/RunWindowWithShellStartup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/RunWindowWithShellStartup.png -------------------------------------------------------------------------------- /docs/Images/UsingManyCommandsWithPrefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/UsingManyCommandsWithPrefix.png -------------------------------------------------------------------------------- /docs/Images/AHKCommandPicker-AllCommands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/AHKCommandPicker-AllCommands.png -------------------------------------------------------------------------------- /docs/Images/AHKCommandPicker-FilteredCommands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/AHKCommandPicker-FilteredCommands.png -------------------------------------------------------------------------------- /docs/Images/UsersStartupDirectoryWithShortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deadlydog/AHKCommandPicker/HEAD/docs/Images/UsersStartupDirectoryWithShortcut.png -------------------------------------------------------------------------------- /docs/Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Feel free to open an issue or pull request. 4 | 5 | ## Issues 6 | 7 | If you open an issue, please: 8 | 9 | - Indicate whether it is a bug or feature enhancement. 10 | - Provide a detailed description, including: 11 | - any error messages 12 | - log output or files 13 | - screenshots, gifs, or videos 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file should only include settings that affect the physical contents of the file, not just how it appears in an editor. 2 | # Do not include personal preference meta-data settings like a tab's `indent_size` in this file; those should be specified in a parent .editorconfig file outside of the repository. 3 | # v1.2 4 | 5 | # Ensure that personal preference meta-settings can be inherited from parent .editorconfig files. 6 | root = false 7 | 8 | [*] 9 | indent_style = tab 10 | end_of_line = crlf 11 | trim_trailing_whitespace = true 12 | 13 | [*.{md,psd1,pp,yml,yaml}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: deadlydog # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: https://www.paypal.me/deadlydogDan # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Documents 5 | *.doc diff=astextplain 6 | *.DOC diff=astextplain 7 | *.docx diff=astextplain 8 | *.DOCX diff=astextplain 9 | *.dot diff=astextplain 10 | *.DOT diff=astextplain 11 | *.pdf diff=astextplain 12 | *.PDF diff=astextplain 13 | *.rtf diff=astextplain 14 | *.RTF diff=astextplain 15 | *.md text 16 | *.adoc text 17 | *.textile text 18 | *.mustache text 19 | *.csv text 20 | *.tab text 21 | *.tsv text 22 | *.sql text 23 | 24 | # Graphics 25 | *.png binary 26 | *.jpg binary 27 | *.jpeg binary 28 | *.gif binary 29 | *.tif binary 30 | *.tiff binary 31 | *.ico binary 32 | # SVG treated as an asset (binary) by default. If you want to treat it as text, comment-out the following line and uncomment the line after. 33 | #*.svg binary 34 | *.svg text 35 | *.eps binary 36 | -------------------------------------------------------------------------------- /docs/DocumentationHomePage.md: -------------------------------------------------------------------------------- 1 | # Documentation Table Of Contents 2 | 3 | AHK Command Picker requires [AutoHotkey v1.1][AutoHotkeyDownloadPageUrl] (AutoHotkey_L) to be installed, as it uses features that are only available in this version of AutoHotkey. 4 | 5 | 1. [Getting Started With AHK Command Picker][GettingStartedPage] 6 | 1. [Using Commands With Parameters][UsingCommandsWithParametersPage] 7 | 1. [Adding Multiple Commands At Once][AddingMultipleCommandsAtOncePage] 8 | 1. [Tips and Tricks][TipsAndTricksPage] 9 | 1. [Best Practices][BestPracticesPage] 10 | 11 | 12 | [AddingMultipleCommandsAtOncePage]: AddingMultipleCommandsAtOnce.md 13 | [AutoHotkeyDownloadPageUrl]: https://www.autohotkey.com/download/ 14 | [BestPracticesPage]: BestPractices.md 15 | [GettingStartedPage]: GettingStarted.md 16 | [TipsAndTricksPage]: TipsAndTricks.md 17 | [UsingCommandsWithParametersPage]: UsingCommandsWithParameters.md 18 | -------------------------------------------------------------------------------- /src/UserCommands/MyHotkeys.ahk: -------------------------------------------------------------------------------- 1 | ; =============================================== 2 | ; Add your custom Hotkeys to this file. 3 | ; If you want to break your commands up over multiple files, simply include a reference to them here. 4 | ; e.g. #Include %A_ScriptDir%\UserCommands\WorkRelatedHotkeys.ahk 5 | ; WorkRelatedHotkeys.ahk should be in the UserCommands directory along with this MyHotkeys.ahk file. 6 | ; 7 | ; After modifying this file (or any included files), run the `ReloadAHKScript` Command to apply the changes. 8 | ; =============================================== 9 | 10 | ; Example of including another script that contains hotkeys and/or hotstrings. 11 | ;#Include %A_ScriptDir%\UserCommands\WorkRelatedHotkeys.ahk 12 | 13 | ; Example hotkey. Press Windows Key + O to see the message box. Feel free to delete this. 14 | ;#o::MsgBox, You pressed the Windows Key + O 15 | 16 | ; Example hotstring. When you type "btw" it will be replaced with "by the way". Feel free to delete this. 17 | ;::btw:by the way 18 | -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | MIT 2 | Copyright (c) 2012 Daniel Schroeder 3 | 4 | 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: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | 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. -------------------------------------------------------------------------------- /src/UserCommands/MyCommands.ahk: -------------------------------------------------------------------------------- 1 | ; =============================================== 2 | ; Add your custom Commands to this file. 3 | ; If you want to break your commands up over multiple files, simply include a reference to them here. 4 | ; e.g. #Include %A_ScriptDir%\UserCommands\WorkRelatedCommands.ahk 5 | ; WorkRelatedCommands.ahk should be in the UserCommands directory along with this MyCommands.ahk file. 6 | ; 7 | ; After modifying this file (or any included files), run the `ReloadAHKScript` Command to apply the changes. 8 | ; =============================================== 9 | 10 | ; Example of including another script that contains Commands. 11 | ;#Include %A_ScriptDir%\UserCommands\WorkRelatedCommands.ahk 12 | 13 | ; Example of creating a command that does not take any parameters. Feel free to delete this. 14 | AddCommand("DisplayLoggedInUser", "Displays the username of the logged in user.") 15 | DisplayLoggedInUser() 16 | { 17 | MsgBox, % "You are logged in as: " . A_UserName 18 | } 19 | 20 | ; Example of creating a command that takes a parameter. Feel free to delete this. 21 | AddCommand("DisplayMessage", "Displays a message box.", "These, are, preset, parameter, values", "Default parameter value") 22 | DisplayMessage(message) 23 | { 24 | MsgBox % "Parameter passed in was: " . message 25 | } 26 | -------------------------------------------------------------------------------- /src/DefaultCommands/DefaultHotkeys.ahk: -------------------------------------------------------------------------------- 1 | ;========================================================== 2 | ; Use Windows Key + Left Mouse Button to move a window 3 | ;========================================================== 4 | LWin & LButton:: 5 | CoordMode, Mouse ; Go by absolute coordinates 6 | MouseGetPos, MouseStartX, MouseStartY, MouseWin ; Get the mouses' starting position within the window, as an anchor. 7 | SetTimer, WatchMouse, 10 ; Track the mouse as it is dragged, and run the WatchMouse event every 10 ms. 8 | 9 | WatchMouse: 10 | GetKeyState, LButtonState, LButton, P 11 | if LButtonState = U ; If Left Mouse Button is released, stop dragging 12 | { 13 | SetTimer, WatchMouse, off 14 | return 15 | } 16 | ; Otherwise, reposition the window as the mouse moves 17 | CoordMode, Mouse ; Get coordinates relative to the Mouse 18 | MouseGetPos, MouseX, MouseY ; Get current mouse X and Y 19 | WinGetPos, WinX, WinY,,, ahk_id %MouseWin% ; Get current window X and Y, and the ID of the window that must be moved 20 | SetWinDelay, -1 ; Makes the move faster/smoother 21 | NewWinX := WinX + MouseX - MouseStartX 22 | NewWinY := WinY + MouseY - MouseStartY 23 | WinMove, ahk_id %MouseWin%,, %NewWinX%, %NewWinY% ; Move the window by ID to the new location 24 | MouseStartX := MouseX ; Set a new mouse anchor. 25 | MouseStartY := MouseY 26 | return 27 | -------------------------------------------------------------------------------- /docs/WhyUseAhkCommandPicker.md: -------------------------------------------------------------------------------- 1 | # Why Use AHK Command Picker 2 | 3 | AutoHotkey is such a powerful language and can be used to do so many things. 4 | Because of this you will likely have many AHK scripts/hotkeys to accomplish a variety of different tasks, some that you use frequently and others not so frequently. 5 | 6 | AHK Command Picker solves the following problems: 7 | 8 | - Trying to remember which hotkeys (i.e. key combinations) are used to launch which scripts (e.g. "Which script does Win+A launch again?"). 9 | - Trying to find new hotkeys that aren't already used by other applications (e.g. "I hit Ctrl+N to open a new tab in my application, but it launched one of my scripts as well. Oops"). 10 | - Accidentally launching scripts by unintentionally hitting a hotkey key combination (e.g. "My fingers weren't on home-row as I thought they were", or "My cat walked across the keyboard"). 11 | 12 | AHK Command Picker also allows you to: 13 | 14 | - Browse your list of commands (i.e. functions / scripts), each with an optional user-friendly description. 15 | - Easily provide parameters to your commands, which can be used to alter a command's functionality. 16 | - You can also provide preset parameter values that get shown in the command picker. 17 | - Still execute your AHK code very quickly by [CamelCase][CamelCaseExplanationUrl] filtering the commands and preset parameters as you type. 18 | - Automate more tasks. 19 | 20 | If you are trying to determine if something is worth taking the time to automate, [consult this handy chart][XkcdComicAboutDeterminingIfSomethingIsWorthAutomatingUrl]. 21 | 22 | 23 | [CamelCaseExplanationUrl]: http://en.wikipedia.org/wiki/CamelCase 24 | [XkcdComicAboutDeterminingIfSomethingIsWorthAutomatingUrl]: http://xkcd.com/1205/ 25 | -------------------------------------------------------------------------------- /docs/BestPractices.md: -------------------------------------------------------------------------------- 1 | # Best Practices 2 | 3 | ## Use multiple files to organize your commands 4 | 5 | While you could dump all of your commands into the `UserCommands\MyCommands.ahk` file, that file may soon become large and unwieldy. 6 | You may instead want to create new .ahk files in the `UserCommands` folder and `#Include` them from the `UserCommands\MyCommands.ahk` file. 7 | For example, put commands you typically use at work in a `UserCommands\Work.ahk` file, and home ones in `UserCommands\Home.ahk`. 8 | However you want to organize them is up to you, but new files should always be created in the `UserCommands` directory. 9 | 10 | ## Do not add Hotkeys int the Command files 11 | 12 | One important thing to note is that whenever a `hotkey` (e.g. _^j::_) or `hotstring` (e.g. _::btw::by the way_) is encountered, any commands that may have been defined after it will not be processed and added to the AHK Command Picker's list of commands. 13 | So it is important that all hotkeys and hotstrings be declared **AFTER** all commands. 14 | **To do this, ensure that hotkeys and hotstrings are defined or `#Include`d in the `UserCommands\MyHotkeys.ahk` file.** 15 | 16 | ## Do not edit the Default Commands 17 | 18 | Do not edit the `AhkCommandPicker.ahk` file or any files in the `DefaultCommands` directory, as they may be updated when new versions of AHK Command Picker are released, so you may run into conflicts (or lose your customizations) when updating these files. 19 | Only edit the `UserCommands\MyCommands.ahk` and `UserCommands\MyHotkeys.ahk` files, as well as any other files you create in the `UserCommands` directory. 20 | 21 | ## Next Steps 22 | 23 | That's it. 24 | Congrats! 25 | You've read all of the documentation! 🎉👏 26 | 27 | Return to [the table of contents][DocumentationTableOfContents]. 28 | 29 | 30 | [DocumentationTableOfContents]: DocumentationHomePage.md 31 | -------------------------------------------------------------------------------- /docs/MigrateFromV1ToV2.md: -------------------------------------------------------------------------------- 1 | # Migrating from AHK Command Picker v1 to v2 2 | 3 | There were some breaking changes made in v2, so if you are upgrading from v1 to v2, you will need to make the following changes. 4 | 5 | ## Script files location changed 6 | 7 | In v1 all command scripts were placed in a `Commands` directory. 8 | In v2 we have separated the built-in default commands from the user commands to make updating to new versions easier in the future. 9 | The built-in default commands are now located in the `DefaultCommands` directory, and these should not be modified. 10 | All user commands should now be placed in the `UserCommands` directory. 11 | 12 | The `CommandScriptsToInclude.ahk` file was also removed in v2; instead those include statements should be moved to the `MyCommands.ahk` and `MyHotkeys.ahk` files. 13 | 14 | To migrate your customizations from v1 to v2: 15 | 16 | 1. Copy the contents of your `Commands\MyCommands.ahk` file to the new `UserCommands\MyCommands.ahk` file. 17 | 1. Copy the contents of your `Commands\MyHotkeys.ahk` file to the new `UserCommands\MyHotkeys.ahk` file. 18 | 1. Copy any additional scripts you had in the `Commands` directory to the `UserCommands` directory. 19 | 1. In `UserCommands\MyCommands.ahk` remove the `EditMyCommands` and `EditMyHotkeys` commands (if they are present) that were brought over from the old `Commands\MyCommands.ahk` file. 20 | These commands are now defined directly in the application. 21 | If you do not remove these commands from your `MyCommands.ahk` file, you will get a "duplicate function definition" error when the script is reloaded. 22 | 1. If you had added `#Include` statements to the `CommandScriptsToInclude.ahk` file, you will need to move those include statement lines to: 23 | - `UserCommands\MyCommands.ahk`: For including scripts containing Commands. 24 | - `UserCommands\MyHotkeys.ahk`: For including scripts containing hotkeys and hotstrings. 25 | 1. If you were using the `#Include` command to reference other scripts, you will need to update the path of the included script to use the `UserCommands` directory instead of the `Commands` directory. 26 | So for every ahk script that is now in the `UserCommands` directory, you will want to find `\Commands\` and replace it with `\UserCommands\`. 27 | e.g. 28 | 29 | ```AutoHotkey 30 | #Include %A_ScriptDir%\Commands\WorkRelatedCommands.ahk 31 | ``` 32 | 33 | Should be changed to: 34 | 35 | ```AutoHotkey 36 | #Include %A_ScriptDir%\UserCommands\WorkRelatedCommands.ahk 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/Changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This page is a list of _notable_ changes made in each version. 4 | 5 | ## v2.1.0 - March 16, 2025 6 | 7 | ### Utility functions 8 | 9 | Features: 10 | 11 | - Allow `PutWindowInFocus` function to detect hidden windows, such as apps running in the system tray. 12 | 13 | ## v2.0.1 - February 28, 2023 14 | 15 | ### App 16 | 17 | Features: 18 | 19 | - Updated file structure to separate User Commands from the built-in Default Commands to make updating to new versions easier in the future (BREAKING CHANGE). 20 | - Add examples to the `MyCommands.ahk` and `MyHotkeys.ahk` files to help new users get started faster. 21 | 22 | Fixes: 23 | 24 | - Fix issue where the wrong command is sometimes selected when typing quickly ([GitHub issue #3](https://github.com/deadlydog/AHKCommandPicker/issues/3)). 25 | 26 | Breaking changes ([see the v1 to v2 migration guide](MigrateFromV1ToV2.md)): 27 | 28 | - Removed the `CommandScriptsToInclude.ahk` file. 29 | If you had added lines to that file to include other scripts, you will need to move those include statements to the `MyCommands.ahk` file. 30 | - Moved the `MyCommands.ahk` and `MyHotkeys.ahk` files to the `UserCommands` directory. 31 | If you had customized the `MyCommands.ahk` or `MyHotkeys.ahk` files, you will need to copy their contents into the new equivalent files in the `UserCommands` directory. 32 | If you had added lines to include other scripts, you will need to update the include statement's directory path from `Commands` to `UserCommands`. 33 | 34 | ### Default Commands 35 | 36 | Features: 37 | 38 | - Added support for Outlook 2016. 39 | - Added commands for creating an Outlook appointment and opening the Outlook calendar. 40 | - Added `ExploreMyDocuments` and `ExploreDesktop` Commands. 41 | 42 | Fixes: 43 | 44 | - Make Outlook commands more resilient. 45 | 46 | Breaking changes: 47 | 48 | - Renamed Commands `eMyComputer` to `ExploreMyComputer`, `eRecycleBin` to `ExploreRecycleBin`, and `eC` to `ExploreCDrive`. 49 | - Changed the Default Hotkey for moving a window with your mouse by grabbing it anywhere (not just the title bar) from `Alt`+`MouseDrag` to `LeftWin`+`MouseDrag`. 50 | 51 | ## v1.3.2 - May 4, 2016 52 | 53 | Fixes: 54 | 55 | - Changed file paths to use an absolute path (via `A_ScriptDir`) instead of a relative path to fix issues when the working directory is not the same as the directory that the AHKCommandPicker.ahk script is in. 56 | 57 | ## v1.3.1 - January 12, 2014 58 | 59 | Features: 60 | 61 | - Renamed General.ahk to DefaultCommands.ahk and GeneralHotKeys.ahk to DefaultHotkeys.ahk. 62 | - Added new MyCommands.ahk and MyHotkeys.ahk files, and associated EditMyCommands and EditMyHotkeys commands, to help new users get started faster. 63 | 64 | ## v1.3.0 - November 24, 2013 65 | 66 | Features: 67 | 68 | - Added new setting to have Escape key kill all current Commands and reload script (i.e. panic kill). 69 | Escape key only has an effect if a command is currently running. 70 | - Added Sleep timer to allow hotkeys and hotstrings to still be processed when in a long-running loop in a user's command. 71 | 72 | ## CodePlex to GitHub migration 73 | 74 | This project was originally created in January 2012 using TFVC (Team Foundation Version Control) and hosted on CodePlex, which is now defunct. 75 | The project was migrated to git and moved to GitHub in September 2017. 76 | 77 | Unfortunately I did not keep a proper changelog for a long time, so for earlier versions and changes you will need to view the git commit history. 78 | -------------------------------------------------------------------------------- /docs/UsingCommandsWithParameters.md: -------------------------------------------------------------------------------- 1 | # Using Parameters With Your Commands 2 | 3 | You may also configure your commands to take optional parameters. 4 | To do this, simply define that your function takes in an optional string parameter. 5 | 6 | For example, we could re-write the _ExploreCDrive_ Command and function to open any directory path that is passed in as a parameter like so: 7 | 8 | ```AutoHotkey 9 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", "", "C:\") 10 | ExploreDirectory(directoryToOpen = "") 11 | { 12 | Run, explore "%directoryToOpen%" 13 | } 14 | ``` 15 | 16 | Notice that after the `functionName` and `descriptionOfWhatFunctionDoes` parameters, we supply an empty string for the `parameterList` and "C:\" for the `defaultParameterValue`. 17 | Technically both of these parameters are optional, but in this case we want to provide a default parameter value in case the user does not supply a parameter when selecting to run this Command; in this example if no parameters are supplied then "C:\" will be passed into the function and will be opened. 18 | 19 | Because AHK Command Picker supports passing multiple parameters into a command, we would probably want to re-write our function to support opening each directory path that is passed in like so: 20 | 21 | ```AutoHotkey 22 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", "", "C:\") 23 | ExploreDirectory(directoriesToOpen = "") 24 | { 25 | Loop, Parse, directoriesToOpen, CSV 26 | { 27 | directoryToOpen := A_LoopField 28 | Run, explore "%directoryToOpen%" 29 | } 30 | } 31 | ``` 32 | 33 | Here we added a loop that will iterate over each directory path in the comma-separated value (CSV) list of directories that is passed in, and open each one up. 34 | 35 | In addition to supporting multiple parameters, you may have preset parameters for your commands as well; this is what the `parameterList` parameter is used for, and allows preset parameters to show up in the AHK Command Picker GUI. 36 | For example, to have "C:\" and "C:\MyDir" show up in the GUI when the user is entering parameter values, you could use: 37 | 38 | ```AutoHotkey 39 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", "C:\,C:\MyDir", "C:\") 40 | ``` 41 | 42 | You can also give user-friendly names to the preset parameter values as well that will show up in the GUI instead of the actual value passed into the function; simply separate the `Name` from the `Value` with a pipe character (`|`). 43 | For example, we could give user-friendly names to these preset parameters by using: 44 | 45 | ```AutoHotkey 46 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", "C Drive|C:\,My Directory|C:\MyDir", "C:\") 47 | ``` 48 | 49 | If you want to have a long list of preset parameters, you may want to consider storing the list in a variable and passing that variable into the `AddCommand()` function, like so: 50 | 51 | ```AutoHotkey 52 | directories = "C Drive|C:\,My Directory|C:\MyDir,Other Directory|C:\Other,C:\Some\Other\Directory,Program Files|C:\Program Files" 53 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", directories, "C:\") 54 | ``` 55 | 56 | The `DefaultCommands\UtilityFunctions.ahk` file provided by AHK Command Picker (and included by default) provides a `AddParameterToString` function to help make building these long lists of parameters easier and more readable: 57 | 58 | ```AutoHotkey 59 | // Add all of our preset parameters to the directories variable, which will actually be a comma-separated string list. 60 | AddParameterToString(directories, "C Drive|C:\") 61 | AddParameterToString(directories, "My Directory|C:\MyDir") 62 | AddParameterToString(directories, "Other Directory|C:\Other") 63 | AddParameterToString(directories, "C:\Some\Other\Directory") 64 | AddParameterToString(directories, "Program Files|C:\Program Files") 65 | AddCommand("ExploreDirectory", "Opens the directory supplied in the parameters", directories, "C:\") 66 | ``` 67 | 68 | Here is an example of what the GUI might look like if we had added many more directories: 69 | 70 | ![Using preset parameters][UsingPresetParametersImage] 71 | 72 | ## Passing Parameters Into The Selected Command From The GUI 73 | 74 | When using the GUI to select a command to run, you can pass a parameter to the selected command to run by placing a comma (`,`) after the name of the command to run and then typing in the parameter value. 75 | If any preset parameters have been defined for the command then they will show up in the GUI list. 76 | You may pass multiple parameters into the command by using a comma-separated list; that is, every comma specifies that the following text is a new parameter value. 77 | 78 | Going back to our _ExploreDirectory_ command we defined above, you could have it open the _C:\SomeDir_ directory by typing "ExploreDirectory, C:\SomeDir". 79 | You could also have it open many directories by passing in a comma separated list, such as "ExploreDirectory, C:\SomeDirectory, C:\Some\Other\Directory", or if the command has a preset parameter with a user-friendly name, you can use that too. 80 | From the example above, we could type, "ExploreDirectory, My Directory, C Drive, C:\Some\Other\Directory". 81 | 82 | Note too that you do not have to type in the entire command and parameter name; only enough so that it gets selected in the list box. 83 | So in the above example you might be able to simply type, "Ex, MyD, CD, C:\Some" to open all of those same directories. 84 | 85 | ## Next Steps 86 | 87 | Proceed to the [Adding Multiple Commands At Once][AddingMultipleCommandsAtOncePage] page, or return to [the table of contents][DocumentationTableOfContents]. 88 | 89 | 90 | [AddingMultipleCommandsAtOncePage]: AddingMultipleCommandsAtOnce.md 91 | [DocumentationTableOfContents]: DocumentationHomePage.md 92 | [UsingPresetParametersImage]: Images/UsingPresetParameters.png 93 | -------------------------------------------------------------------------------- /docs/TipsAndTricks.md: -------------------------------------------------------------------------------- 1 | # Tips And Tricks 2 | 3 | ## How To Reference The Currently Active Window 4 | 5 | If you try to reference the currently active window to perform a command on it (e.g. using _WinActive("A")_), you will instead get a reference to the AHK Command Picker window since it was the last one opened (in order for you to run the command). 6 | Instead you need to use the `_cpAciveWindowID` global variable. 7 | Here is an example of creating a command to use this global variable to close the currently active window: 8 | 9 | ```AutoHotkey 10 | AddCommand("CloseWindow", "Closes the currently active window") 11 | CloseWindow() 12 | { global _cpActiveWindowID 13 | WinClose, ahk_id %_cpActiveWindowID% 14 | } 15 | ``` 16 | 17 | In this example you can see that the global variable `_cpActiveWindowID` contains the AHK ID of the window that was active before the AHK Command Picker window was opened. 18 | Use this variable whenever you need to reference the last active window from a AHK Command Picker command. 19 | 20 | ## How To Send A Virtual Escape Key Press Without Reloading The Script 21 | 22 | If you have the `Allow the Escape key to kill all currently running commands` setting enabled, but need one of your commands to send an Escape key press to a window (e.g. _SendInput, {Esc}_), then use: 23 | 24 | ```AutoHotkey 25 | _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := true ; Prevent Escape key from reloading the script. 26 | SendInput, {Esc} ; Send an Escape key press to the active window. 27 | _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := false ; Allow Escape key to reload the script again. 28 | ``` 29 | 30 | This will prevent the virtual Escape key press from reloading the script, and then will re-enable the Escape key press to kill currently running commands again. 31 | 32 | If you just use `_cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := true`, but never set it back to false, it will automatically be set back to false once there are no longer any commands running. 33 | Keep in mind though, the Escape key will not be able to kill any running commands until this happens. 34 | 35 | ## Reporting Errors Or Extra Information From A Command 36 | 37 | After a command runs, its name and description are displayed for a short period to give the user confirmation that the actions were indeed performed. 38 | If a command returns some text, this text will also be displayed. 39 | 40 | For example, if we wanted our _ExploreDirectory_ command to tell us which directories it is opening, we could add the line "return Opening %directoriesToOpen%" to the bottom of the _ExploreDirectory()_ function, like so: 41 | 42 | ```AutoHotkey 43 | ExploreDirectory(directoriesToOpen = "") 44 | { 45 | Loop, Parse, directoriesToOpen, CSV 46 | { 47 | directoryToOpen := A_LoopField 48 | Run, explore "%directoryToOpen%" 49 | } 50 | return Opening %directoriesToOpen% 51 | } 52 | ``` 53 | 54 | ## Have AHK Command Picker Automatically Start When You Log Into Windows 55 | 56 | It is fairly straight forward to have AHK Command Picker (or any program) start when you log into Windows: 57 | 58 | 1. Press `Windows Key` + `R` to open the Run window. 59 | 1. Type `shell:startup` and hit enter. 60 | - If you want AHK Command Picker to start whenever _anybody_ logs into Windows, not just you, use `shell:common startup`. 61 | 1. File Explorer should open at a location like, `C:\Users\[Your Username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`, or `C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp` if you used the common startup command. 62 | 1. Paste a shortcut to the `AHKCommandPicker.ahk` script into this directory. 63 | 64 | ![Run window with shell startup command][RunWindowWithShellStartupImage] 65 | ![Users Startup directory with shortcut][UsersStartupDirectoryWithShortcutImage] 66 | 67 | That's it; the script will now launch whenever you (or any user depending which startup directory you used) logs into Windows. 68 | 69 | ## Have AHK Command Picker Run As An Administrator 70 | 71 | In order to launch the AHK Command Picker GUI when an application running as administrator has focus (such as the Control Panel or Windows Explorer / File Explorer, in Windows 8), either the [AutoHotkey executable will need to be signed][BlogShowingHowToSignAutoHotkeyUrl] (preferred method), or the `AHKCommandPicker.ahk` script will also need to be running as an administrator. 72 | 73 | You can check out [my blog post][BlogShowingHowToHaveAutoHotkeyStartAsAdminAtStartupUrl] to see how to have the script automatically run as an administrator at login. 74 | If you want to run the script as an admin, but don't want it to automatically start when you log into Windows, you can simply right-click on the `AHKCommandPicker.ahk` file and choose Run As Admin. 75 | However, it will likely be more convenient for you to create a shortcut to that file, place the shortcut somewhere easily accessible (such as on your desktop), and set the shortcut properties to always launch the script as an admin. 76 | Part of [my other blog post][BlogShowingHowToHaveAutoHotkeyInteractWithAdminWindowsUrl] contains similar instructions and a screenshot on how to do this. 77 | 78 | ## Next Steps 79 | 80 | Proceed to the [Best Practices][BestPracticesPage] page, or return to [the table of contents][DocumentationTableOfContents]. 81 | 82 | 83 | [BestPracticesPage]: BestPractices.md 84 | [BlogShowingHowToSignAutoHotkeyUrl]: http://blog.danskingdom.com/get-autohotkey-to-interact-with-admin-windows-without-running-ahk-script-as-admin/ 85 | [BlogShowingHowToHaveAutoHotkeyStartAsAdminAtStartupUrl]: http://blog.danskingdom.com/get-autohotkey-script-to-run-as-admin-at-startup/ 86 | [BlogShowingHowToHaveAutoHotkeyInteractWithAdminWindowsUrl]: http://blog.danskingdom.com/autohotkey-cannot-interact-with-windows-8-windowsor-can-it/ 87 | [DocumentationTableOfContents]: DocumentationHomePage.md 88 | [RunWindowWithShellStartupImage]: Images/RunWindowWithShellStartup.png 89 | [UsersStartupDirectoryWithShortcutImage]: Images/UsersStartupDirectoryWithShortcut.png 90 | -------------------------------------------------------------------------------- /docs/AddingMultipleCommandsAtOnce.md: -------------------------------------------------------------------------------- 1 | # Add Multiple Commands At The Same Time 2 | 3 | This is an alternative to [Using Preset Parameters][UsingCommandsWithParametersPage] 4 | 5 | So even though you can have your commands show a preset list of parameters, you may instead want each command-parameter combination show up as its own command. 6 | So essentially instead of getting this with using preset parameters: 7 | 8 | ![Using Preset Parameters][UsingPresetParametersImage] 9 | 10 | You would prefer to have this instead: 11 | 12 | ![Using Many Commands][UsingManyCommandsImage] 13 | 14 | Where each command-parameter combination shows up in the base list of commands; that is, the user does not have to type in some other command plus a comma to see the list of that command's preset parameters. 15 | 16 | Maybe you don't like having to press the comma (`,`) key after typing the command name, or maybe you just prefer to see ALL of your options without hiding any of them as parameters. 17 | Of course we could accomplish this by adding a command for each command-parameter combination one by one, like so: 18 | 19 | ```AutoHotkey 20 | AddCommand("ExploreCDrive", "Explore C:\") 21 | ExploreCDrive() 22 | { 23 | Run, explore C:\ 24 | } 25 | 26 | ; ... Add a bunch more commands to open up other folders here ... 27 | 28 | AddCommand("ExploreProgramFiles", "Explore Program Files") 29 | ExploreProgramFiles() 30 | { 31 | Run, explore "C:\Program Files" 32 | } 33 | ``` 34 | 35 | But as you can see, even on this very basic and simple operation, there is a lot of very similar code and code duplication happening, and it would take a while to write all of it out. 36 | 37 | To help alleviate this tedious repetition you can use the `AddCommands()` or `AddCommandsWithPrePostfix()` functions. 38 | Here are their prototypes: 39 | 40 | ```AutoHotkey 41 | AddCommands(functionName, descriptionOfWhatFunctionDoes = "", commandList = "") 42 | 43 | AddCommandsWithPrePostfix(functionName, descriptionOfWhatFunctionDoes = "", commandList = "", prefix = "", postfix = "") 44 | ``` 45 | 46 | These both call the `AddNamedCommand()` function for each command in the commandList. 47 | Each command in the list will call the given functionName, supplying the command's specific value as a parameter to the function. 48 | 49 | - `commandList`: The commands to show up in the picker that will call the function, separated with a comma. 50 | Separate the command name that appears in the picker and the value to pass to the function with a pipe character (`|`). 51 | If no pipe character is provided, the given value will be both shown in the picker and passed to the function. 52 | - `prefix`: The prefix to add to the beginning of all the command names in the commandList. 53 | - `postfix`: The postfix to add to the end of all the command names in the commandList. 54 | 55 | So to use the `AddCommands()` function to get the same type of look that you would get from manually adding each command individually you could do: 56 | 57 | ```AutoHotkey 58 | ; Add all of our preset parameters to the directories variable, which will actually be a comma-separated string list. 59 | AddParameterToString(directories, "C Drive|C:\") 60 | AddParameterToString(directories, "My Directory|C:\MyDir") 61 | AddParameterToString(directories, "Other Directory|C:\Other") 62 | AddParameterToString(directories, "C:\Some\Other\Directory") 63 | AddParameterToString(directories, "Program Files|C:\Program Files") 64 | AddCommands("ExploreDirectory", "Open directory", directories) 65 | ExploreDirectory(directoriesToOpen = "") 66 | { 67 | Loop, Parse, directoriesToOpen, CSV 68 | { 69 | directoryToOpen := A_LoopField 70 | Run, explore "%directoryToOpen%" 71 | } 72 | } 73 | ``` 74 | 75 | Here we used the same technique as in the [Preset Parameters][UsingCommandsWithParametersPage] documentation to build a comma-separated list of directories to add. 76 | We also used the exact same _ExploreDirectory()_ function. 77 | The only thing different here is that we used the `AddCommands()` function, which will essentially loop through each command in the commandList (i.e. the _directories_ variable above) and call `AddCommand()` for you, passing in "ExploreDirectory" as the function to call and "Open directory" as the user-friendly description. 78 | 79 | One more time, this is what the result might look like if we added many more directories and had some other commands defined: 80 | 81 | ![Using Many Commands][UsingManyCommandsImage] 82 | 83 | Notice that you no longer need to type "ExploreDirectory," to see the list of directories to explore; they are listed along-side all of the other commands. 84 | For example, you could now just type "C Drive" to open _C:\\_. 85 | 86 | ## Prepend Command Names 87 | 88 | So that's nice, but what if you want to group all of these related commands that essentially do the same operation. 89 | That's what the `AddCommandsWithPrePostfix()` function is for. 90 | To prepend all of our "ExploreDirectory" commands with the letter "e", we could have used: 91 | 92 | ```AutoHotkey 93 | AddCommandsWithPrePostfix("ExploreDirectory", "Open directory", directories, "e") 94 | ``` 95 | 96 | Here we specified that all of these commands should have a prefix of "e" (and we omitted the postfix parameter, which is essentially the same as saying don't add a postfix). 97 | So in the GUI, this is what our list might now look like: 98 | 99 | ![Using many commands with prefix][UsingManyCommandsWithPrefixImage] 100 | 101 | Note the "e" at the beginning of our commands that call _ExploreDirectory()_. 102 | 103 | ## Next Steps 104 | 105 | Proceed to the [Tips and Tricks][TipsAndTricksPage] page, or return to [the table of contents][DocumentationTableOfContents]. 106 | 107 | 108 | [DocumentationTableOfContents]: DocumentationHomePage.md 109 | [TipsAndTricksPage]: TipsAndTricks.md 110 | [UsingCommandsWithParametersPage]: UsingCommandsWithParameters.md 111 | [UsingPresetParametersImage]: Images/UsingPresetParameters.png 112 | [UsingManyCommandsImage]: Images/UsingManyCommands.png 113 | [UsingManyCommandsWithPrefixImage]: Images/UsingManyCommandsWithPrefix.png 114 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # AHK Command Picker [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/deadlydog/AHKCommandPicker/blob/main/License.md) [![Number of GitHub downloads](https://img.shields.io/github/downloads/deadlydog/AHKCommandPicker/total)](https://github.com/deadlydog/AHKCommandPicker/releases) 2 | 3 | An [AutoHotkey][AutoHotkeyWebsiteUrl] (AHK) script that allows you to easily call AHK functions and run other AHK scripts. 4 | (Requires [AutoHotkey v1.1][AutoHotkeyDownloadPageUrl] to be installed). 5 | 6 | Instead of having to remember what hotkey maps to each of your AHK scripts (as you could have hundreds), this displays a list of Commands in a light-weight GUI that allows you to quickly and easily run your scripts. 7 | Simply type part of the command name and hit enter to run your script. 8 | You can also provide parameters to your commands, allowing you to change the functionality of a command with a few keystrokes. 9 | 10 | For more reasons to use this with your AHK scripts, see [Why Use AHK Command Picker][WhyUseAhkCommandPickerPage]. 11 | 12 | > [!IMPORTANT] 13 | > This project does not yet support AutoHotkey v2. 14 | > Ensure you have v1.1 installed. 15 | 16 | ## 🚀 How to use AHK Command Picker 17 | 18 | Start by [downloading the latest release][DownloadLatestReleaseUrl]. 19 | To launch AHK Command Picker run the `AHKCommandPicker.ahk` script. 20 | 21 | Press the `Caps Lock` key to bring up the AHK Command Picker GUI. 22 | From there, just type the name of the Command that you want to run and hit Enter to run it. 23 | 24 | Note: You can still toggle Caps Lock on and off by pressing `Shift`+`Caps Lock`. 25 | 26 | ### ✍ Adding your own Commands, hotkeys, and hotstrings 27 | 28 | AHK Command Picker comes with many Commands out of the box, but the real power comes from adding your own Commands, hotkeys, and hotstrings. 29 | 30 | - `UserCommands\MyCommands.ahk`: Add your own Commands here. 31 | - `UserCommands\MyHotkeys.ahk`: Add your own hotkeys and hotstrings here. 32 | 33 | You can use AHK Command Picker to open these files for editing by running the `EditMyCommands` and `EditMyHotkeys` Commands. 34 | After you have modified a file, run the `ReloadAHKScript` Command to apply your changes. 35 | 36 | For more information, see [the documentation][DocumentationPage]. 37 | 38 | ### Additional tidbits 39 | 40 | You will likely want to [have AHKCommandPicker start automatically when you log into Windows][AutomaticallyStartAtLogin]. 41 | 42 | ### Upgrading versions 43 | 44 | If you are upgrading from v1 to v2, see [the migration guide][MigrateFromV1ToV2Page]. 45 | 46 | ## 🖼 Screenshots 47 | 48 | All commands: 49 | 50 | ![All Commands][AllCommandsImage] 51 | 52 | Commands filtered as you type: 53 | 54 | ![Filtered Commands][FilteredCommandsImage] 55 | 56 | ## 🎦 Videos 57 | 58 | Get started with AHK Command Picker in under 2 minutes: 59 | 60 | [![AHK Command Picker in under 2 minutes video][AhkCommandPickerInUnder2MinutesYouTubeImageUrl]][AhkCommandPickerInUnder2MinutesYouTubeUrl] 61 | 62 | Some out of the box functionality provided by AHK Command Picker: 63 | 64 | [![Out of the box functionality video][OutOfTheBoxFunctionalityProvidedByAhkCommandPickerYouTubeImageUrl]][OutOfTheBoxFunctionalityProvidedByAhkCommandPickerYouTubeUrl] 65 | 66 | Motivation for creating AHK Command Picker and some AHK problems it solves: 67 | 68 | [![Motivation for creating AHK Command Picker video][MotivationForCreatingAhkCommandPickerYouTubeImageUrl]][MotivationForCreatingAhkCommandPickerYouTubeUrl] 69 | 70 | ## 💬 Quotes / Testimonials 71 | 72 | > I've found that by not having to find and assign a specific hotkey (i.e. keyboard combination) to each of my scripts, I am more likely to automate many more of my tasks. 73 | > Before I would worry about having to remember too many keyboard shortcuts and the overhead involved (remembering which shortcut launches what, accidentally triggering them, etc.), so I would only use AHK to automate the tasks that I did all of the time. 74 | > Now with AHK Command Picker these problems are gone, so I automate everything; even the tasks that I might only do once a month. 75 | 76 | > A great tool for any AHK user: the interface is intuitive, adding your own commands\hotkeys and interacting with other AHK programs is easy, and the source code is well written and modifiable. 77 | > Most of all, Command picker does what it claims to do: make windows automation easy by removing the need to remember hotkeys. 78 | 79 | ## ➕ How to contribute 80 | 81 | Issues and Pull Requests are welcome. 82 | See [the Contributing page](docs/Contributing.md) for more details. 83 | 84 | ## 📃 Changelog 85 | 86 | See what's changed in the application over time by viewing [the Changelog](docs/Changelog.md). 87 | 88 | ## 💳 Donate 89 | 90 | Buy me a pastry 🍰 for providing this script open source and for free 🙂 91 | 92 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D7PW6YBWNDLXW) 93 | 94 | 95 | [AutoHotkeyWebsiteUrl]: https://www.autohotkey.com 96 | [AutoHotkeyDownloadPageUrl]: https://www.autohotkey.com/download/ 97 | 98 | [AhkCommandPickerInUnder2MinutesYouTubeUrl]: https://www.youtube.com/watch?v=gevnQAwYLAg,type=youtube 99 | [AhkCommandPickerInUnder2MinutesYouTubeImageUrl]: https://img.youtube.com/vi/gevnQAwYLAg/0.jpg 100 | 101 | [OutOfTheBoxFunctionalityProvidedByAhkCommandPickerYouTubeUrl]: https://www.youtube.com/watch?v=kr5nBVOXVkE,type=youtube 102 | [OutOfTheBoxFunctionalityProvidedByAhkCommandPickerYouTubeImageUrl]: https://img.youtube.com/vi/kr5nBVOXVkE/0.jpg 103 | 104 | [MotivationForCreatingAhkCommandPickerYouTubeUrl]: https://www.youtube.com/watch?v=E0LnMtWVVuA,type=youtube 105 | [MotivationForCreatingAhkCommandPickerYouTubeImageUrl]: https://img.youtube.com/vi/E0LnMtWVVuA/0.jpg 106 | 107 | [DownloadLatestReleaseUrl]: https://github.com/deadlydog/AHKCommandPicker/releases 108 | 109 | [WhyUseAhkCommandPickerPage]: docs/WhyUseAhkCommandPicker.md 110 | [DocumentationPage]: docs/DocumentationHomePage.md 111 | [AutomaticallyStartAtLogin]: docs/TipsAndTricks.md#have-ahk-command-picker-automatically-start-when-you-log-into-windows 112 | [MigrateFromV1ToV2Page]: docs/MigrateFromV1ToV2.md 113 | 114 | [AllCommandsImage]: docs/Images/AHKCommandPicker-AllCommands.png 115 | [FilteredCommandsImage]: docs/Images/AHKCommandPicker-FilteredCommands.png 116 | -------------------------------------------------------------------------------- /docs/GettingStarted.md: -------------------------------------------------------------------------------- 1 | # Getting Started With AHK Command Picker 2 | 3 | ## How To Launch AHK Command Picker 4 | 5 | To launch AHK Command Picker run the `AHKCommandPicker.ahk` script. 6 | 7 | Once the AHKCommandPicker.ahk script is running, press the `Caps Lock` key to bring up the AHK Command Picker GUI. 8 | From there just type the name of the command that you want to run, and hit enter to run it. 9 | 10 | If you want to turn Caps Lock on, use `Shift` + `Caps Lock` to toggle it on and off. 11 | 12 | ## Adding New Commands 13 | 14 | Open the `UserCommands\MyCommands.ahk` file for editing; this can be done quickly by using `Caps Lock` to open the AHK Command Picker and running the `EditMyCommands` command. 15 | From here you can either write your custom commands directly in `MyCommands.ahk`, or create a new ahk file and `#Include` its path in `MyCommands.ahk`, or a mix of the two approaches. 16 | 17 | A `Command` is simply a function pointer (i.e. delegate) and collection of parameters. 18 | So when you run a command it simply calls a function that you've defined, and optionally passes parameters to that function. 19 | 20 | There are 2 different (but similar) functions that may be used to add a command to the AHK Command Picker: `AddCommand()` and `AddNamedCommand()`. 21 | Both of these functions have optional parameters. 22 | Here are the function prototypes, and descriptions of each parameter: 23 | 24 | ```AutoHotkey 25 | AddCommand(functionName, descriptionOfWhatFunctionDoes = "", parameterList = "", defaultParameterValue = "") 26 | 27 | AddNamedCommand(commandName, functionName, descriptionOfWhatFunctionDoes = "", parameterList = "", defaultParameterValue = "") 28 | ``` 29 | 30 | - `commandName`: The name of the command to appear in the Command Picker. 31 | - `functionName`: The function to call when this command is selected to run. 32 | Unless the `AddNamedCommand()` function is used, this will also be the name of the command that appears in the Command Picker. 33 | - `descriptionOfWhatFunctionDoes`: A user-friendly message that will appear in the Command Picker telling what the command does. 34 | - `parameterList`: A pre-set list of parameters to choose from in the Command Picker when this command is selected. 35 | Parameter values should be separated by a comma, and if you would like your parameter to show a different name in the GUI, separate the name from the value with a pipe character (`|`) (e.g. "Name1|Value1, Value2, Name3|Value3"). 36 | - `defaultParameterValue`: The parameter value that will be passed to the function when no other parameter is given. 37 | 38 | For example, to create a command to explore the C drive, you could use: 39 | 40 | ```AutoHotkey 41 | AddCommand("ExploreCDrive", "Explore C:\") 42 | ExploreCDrive() 43 | { 44 | Run, explore C:\ 45 | } 46 | ``` 47 | 48 | Here you can see the command specifies that the _ExploreCDrive_ function should be called when this command runs, and that the command's user-friendly description is "Explore C:\\". 49 | Next it actually defines the _ExploreCDrive_ function and what it should do. 50 | 51 | If we wanted to leave the function name as _ExploreCDrive_, but have it show up in the Command Picker as _Open C_, we would use the `AddNamedCommand()` function as follows: 52 | 53 | ```AutoHotkey 54 | AddNamedCommand("Open C", "ExploreCDrive", "Explore C:\") 55 | ExploreCDrive() 56 | { 57 | Run, explore C:\ 58 | } 59 | ``` 60 | 61 | ## Reload AHK Command Picker to apply changes 62 | 63 | Anytime you edit one of the script files to add or modify a Command, hotkey, or hotstring, the changes will not be applied in AHK Command Picker until you reload it. 64 | 65 | To reload AHK Command Picker, simply press `Caps Lock` to open the AHK Command Picker GUI, and run the `ReloadAHKScript` Command. 66 | 67 | Alternatively, you can reload the script like you would any other AHK script by right-clicking on the AHK Command Picker icon in the system tray and selecting `Reload This Script`. 68 | 69 | If you receive an error message when trying to reload the script, it is likely because you have a syntax error in one of your script files, such as having two functions with the same name. 70 | 71 | ## Where To Create Hotkeys and Hotstrings 72 | 73 | Both `hotkeys` (e.g. _^j::_) and `hotstrings` (e.g. _::btw::by the way_) should be added to (or referenced from) the `UserCommands\MyHotkeys.ahk` file. 74 | The `MyHotkeys.ahk` file can be opened for editing by using `Caps Lock` to open the AHK Command Picker and running the `EditMyHotkeys` command. 75 | From here you can either write your custom hotkeys and hotstrings directly in `MyHotkeys.ahk`, or create a new ahk file in the `UserCommands` directory and `#Include` it's path in `MyHotkeys.ahk`, or a mix of the two approaches. 76 | 77 | This is an example of the code you would add to the `UserCommands\MyHotkeys.ahk` file to include the `UserCommands\WorkRelatedHotkeys.ahk` file: 78 | 79 | ```AutoHotkey 80 | #Include %A_ScriptDir%\UserCommands\WorkRelatedHotkeys.ahk 81 | ``` 82 | 83 | **Any commands defined after a hotkey or hotstring will not show up in the AHK Command Picker**. 84 | So if you add a hotkey or hotstring to the `MyCommands.ahk` file, then none of your custom commands will show up in the AHK Command Picker. 85 | This is why it is vital that hotkeys and hotstrings are defined or `#Include`d in the `UserCommands\MyHotkeys.ahk` file, since `MyCommands.ahk` is included before `MyHotkeys.ahk`. 86 | 87 | ## How To Convert An Existing Hotkey Into a Command 88 | 89 | Typically existing AHK scripts are bound to a hotkey so that you can quickly launch them using a keyboard shortcut. 90 | For example, you might have the following hotkey to open up _C:\SomeFolder_ whenever the _Windows Key_ + _Z_ is pressed: 91 | 92 | ```AutoHotkey 93 | #z:: 94 | Run, explore C:\SomeFolder 95 | return 96 | ``` 97 | 98 | To convert this into a command that will show up in the AHK Command Picker, you would change the code to something like: 99 | 100 | ```AutoHotkey 101 | AddCommand("OpenSomeFolder", "Opens C:\SomeFolder in Windows Explorer") 102 | OpenSomeFolder() 103 | { 104 | Run, explore C:\SomeFolder 105 | } 106 | ``` 107 | 108 | If you still wanted to have the hotkey, be sure you define it in the `UserCommands\MyHotkeys.ahk` file. 109 | You could leave the code as-is, but it would be better practice to have it call the new function to avoid duplicate code. 110 | So you could change it to: 111 | 112 | ```AutoHotkey 113 | #z:: 114 | OpenSomeFolder() 115 | return 116 | ``` 117 | 118 | Typically your AHK scripts/hotkeys are probably more than one line long, but this shows the general concept. 119 | 120 | ## Additional Info 121 | 122 | All of the out-of-the-box commands and hotkeys are provided in the `DefaultCommands\DefaultCommands.ahk` and `DefaultCommands\DefaultHotkeys.ahk` files respectively; feel free to look at them for examples. 123 | Editing them is not recommended, as any changes you make may be overwritten when they are updated in future versions. 124 | 125 | ## Next Steps 126 | 127 | Proceed to the [Using Commands With Parameters][UsingCommandsWithParametersPage] page, or return to [the table of contents][DocumentationTableOfContents]. 128 | 129 | 130 | [DocumentationTableOfContents]: DocumentationHomePage.md 131 | [UsingCommandsWithParametersPage]: UsingCommandsWithParameters.md 132 | -------------------------------------------------------------------------------- /src/DefaultCommands/UtilityFunctions.ahk: -------------------------------------------------------------------------------- 1 | ;========================================================== 2 | ; Create the window if necessary and put it in focus. 3 | ; If found or created, the window's unique ID will be returned; 0 if not. 4 | ; 5 | ; windowName = Name of the window to put in focus. 6 | ; applicationPath = Path to the application to launch if the windowName window is not found. 7 | ; titleMatchMode = The title match mode to use when looking for the windowName window. 8 | ; 1 = Match the start of window's title. 9 | ; 2 = Match any part of the window's title. 10 | ; 3 = Match the window's title exactly. 11 | ; New = Open a new instance of the application, ignoring currently running instances. 12 | ;========================================================== 13 | PutWindowInFocus(windowName, applicationPath = "", titleMatchMode = "") 14 | { 15 | ; Store the current values for the global modes, since we will be overwriting them. 16 | previousTitleMatchMode := A_TitleMatchMode 17 | previousDetectHiddenWindowsMode := A_DetectHiddenWindows 18 | 19 | ; Used to tell when we have succeeded and stop trying. 20 | windowActivated := false 21 | 22 | ; Turn on searching for hidden windows, like apps running in the system tray. 23 | DetectHiddenWindows, On 24 | 25 | ; If the user did not specify a specific match mode to use, try them all starting with the most specific ones. 26 | if (titleMatchMode = "") 27 | { 28 | SetTitleMatchMode, 3 ; Start by trying to match the window's title exactly. 29 | gosub, PWIFTryActivateWindow 30 | 31 | if (!windowActivated) 32 | { 33 | SetTitleMatchMode, 1 ; Next try to match the start of the window's title. 34 | gosub, PWIFTryActivateWindow 35 | } 36 | 37 | if (!windowActivated) 38 | { 39 | SetTitleMatchMode, 2 ; Next try to match any part of the window's title. 40 | gosub, PWIFTryActivateWindow 41 | } 42 | 43 | if (!windowActivated) 44 | { 45 | DetectHiddenWindows, On ; Lastly try searching hidden windows as well. 46 | gosub, PWIFTryActivateWindow 47 | } 48 | } 49 | else 50 | { 51 | ; If we do want to try and match against an existing window. 52 | if (titleMatchMode != "New") 53 | { 54 | SetTitleMatchMode, %titleMatchMode% ; Try to activate the window using the specified match mode. 55 | gosub, PWIFTryActivateWindow 56 | } 57 | } 58 | 59 | ; If the window is not already open. 60 | if (!windowActivated) 61 | { 62 | ; If we were given a program to launch as a backup in case the wanted window wasn't found, then try and launch it. 63 | if (applicationPath != "") 64 | { 65 | ; Create the window. 66 | Run, %applicationPath% 67 | 68 | ; Make sure this window is in focus before sending commands. 69 | WinWaitActive, %windowName%,, 30 70 | 71 | ; If the window wasn't opened for some reason. 72 | IfWinNotExist, %windowName% 73 | { 74 | ; Display an error message that the window couldn't be opened. 75 | MsgBox, There was a problem opening "%windowName%" 76 | } 77 | ; Else the program was launched and the window opened. 78 | else 79 | { 80 | WinShow ; Show the window. 81 | windowActivated := true ; Record that the window was successfully brought into focus. 82 | } 83 | } 84 | } 85 | 86 | ; Restore the previous global modes that we might have changed. 87 | SetTitleMatchMode, %previousTitleMatchMode% 88 | DetectHiddenWindows, %previousDetectHiddenWindowsMode% 89 | 90 | ; Return the handle of the window that was activated. 91 | if (windowActivated) 92 | { 93 | return WinExist("A") 94 | } 95 | 96 | ; Else the window was not activated, so return 0 (i.e. false). 97 | return 0 98 | 99 | ; Tries to put the window in focus if it already exists. 100 | PWIFTryActivateWindow: 101 | ; If the window is already open. 102 | IfWinExist, %windowName% 103 | { 104 | ; Put the window in focus. 105 | WinActivate 106 | WinShow 107 | 108 | ; Not all apps Restore properly when using WinShow and the app is Minimized (e.g. apps minimized using TrayIt!), so explicitly restore windows that are still minimized. 109 | WinGet, isMinimized, MinMax 110 | if (isMinimized = -1) 111 | WinRestore 112 | 113 | ; Record success. 114 | windowActivated := true 115 | } 116 | return 117 | } 118 | 119 | ;========================================================== 120 | ; Returns true if the given targetItem is found in the itemList, false if not. 121 | ; 122 | ; itemList = a string containing a list of items, each separated by the itemDelimiter. This is passed ByRef in order to allow us to modify the string list. 123 | ; NOTE: It is not possible to pass Clipboard, built-in variables, or environment variables to a function's ByRef parameter 124 | ; targetItem = the string that we are checking is in the list. 125 | ; itemDelimiter = the string or character separating each item in the itemList. If none is provided the _cpParameterDelimiter will be used (a comma by default). 126 | ; removeTargetItem = if true and the targetItem is found, it will be removed from the itemList (which is passed in by reference). 127 | ;========================================================== 128 | StringListContains(ByRef itemList, targetItem, itemDelimiter = "", removeTargetItem = false) 129 | { 130 | global _cpParameterDelimiter 131 | 132 | ; If no delimiter was supplied, use the default parameter delimiter. 133 | if (itemDelimiter = "") 134 | itemDelimiter := _cpParameterDelimiter 135 | 136 | ; Trim whitespace off of the target item 137 | targetItem := Trim(targetItem) 138 | 139 | ; Loop through each item in the list and return true if the target is found in it. 140 | Loop, Parse, itemList, %itemDelimiter% 141 | { 142 | if (Trim(A_LoopField) = targetItem) 143 | { 144 | ; Remove the item from the list if specified to do so. 145 | if (removeTargetItem) 146 | { 147 | ; We don't know if this item is first, last, or the only item in the list, so try and remove the itemDelimiter with the item. 148 | ; ErrorLevel will be set to 1 if the string to replace is not found. 149 | StringReplace, itemList, itemList, %A_LoopField%%itemDelimiter% 150 | if (ErrorLevel = 1) 151 | StringReplace, itemList, itemList, %itemDelimiter%%A_LoopField% 152 | if (ErrorLevel = 1) 153 | StringReplace, itemList, itemList, %A_LoopField% 154 | } 155 | return true 156 | } 157 | } 158 | 159 | ; The item was not found in the list, so return false. 160 | return false 161 | } 162 | 163 | StringListContainsAndRemove(ByRef itemList, targetItem, itemDelimiter = "") 164 | { 165 | return StringListContains(itemList, targetItem, itemDelimiter, true) 166 | } 167 | 168 | ;========================================================== 169 | ; Returns true if the given targetItem is found in the itemList, false if not. 170 | ; 171 | ; itemList = an array containing a list of items. 172 | ; targetItem = the item that we are checking is in the list. 173 | ;========================================================== 174 | ArrayContains(itemList, targetItem) 175 | { 176 | global _cpParameterDelimiter 177 | 178 | ; If no delimiter was supplied, use the default parameter delimiter. 179 | if (itemDelimiter = "") 180 | itemDelimiter := _cpParameterDelimiter 181 | 182 | ; Trim whitespace off of the target item 183 | targetItem := Trim(targetItem) 184 | 185 | ; Loop through each item in the list and return true if the target is found in it. 186 | For index, value in parameters 187 | { 188 | if (Trim(value) = targetItem) 189 | return true 190 | } 191 | 192 | ; The item was not found in the list, so return false. 193 | return false 194 | } 195 | 196 | ;========================================================== 197 | ; Pastes the given text into the currently active window. 198 | ; This can be better than just using "SendInput, Text to paste", especially for long strings, because the entire string will pasted at once rather than waiting for it all to be typed out. 199 | ; 200 | ; textToPaste = the text to paste to the window. 201 | ; pasteKeys = the keys to simulate pressing in order to paste text. Ctrl+v is the default, but other windows may have different keys to paste text (e.g. Git Bash uses the Insert key to paste text). 202 | ;========================================================== 203 | PasteText(textToPaste = "", pasteKeys = "^v") 204 | { 205 | if (pasteKeys = "git") 206 | pasteKeys = {Insert} 207 | else if (pasteKeys = "gitEnter") 208 | pasteKeys = {Insert}{Enter} 209 | else if (pasteKeys = "cmd") 210 | pasteKeys = !{Space}ep 211 | else if (pasteKeys = "cmdEnter") 212 | pasteKeys = !{Space}ep{Enter} 213 | 214 | clipboardBackup := ClipboardAll ; Backup whatever is currently on the Clipboard, including pictures and anything else. 215 | while (Clipboard != textToPaste) ; Make sure the Clipboard text has been updated before pasting it, as sometimes it does not get updated instantly. 216 | Clipboard := textToPaste 217 | SendInput, %pasteKeys% ; Paste from the clipboard so all the text appears there instantly. 218 | Sleep, 200 ; Have to sleep so that we don't overwrite the Clipboard text before we've pasted it. 219 | Clipboard := clipboardBackup ; Restore whatever was on the Clipboard. 220 | clipboardBackup := "" ; Clear the variable's contents to free memory, as there could be lots of data on the clipboard. 221 | } 222 | 223 | ;========================================================== 224 | ; Displays the given message in a Message Box, but only if the global variable DebugMsgBox_ShowMessages is not false. 225 | ; Toggling the DebugMsgBox_ShowMessages variable to true/false is a quick way to show/not show messages sent to this function (e.g. messages used for debugging). 226 | ;========================================================== 227 | DebugMsgBox(message = "", showMsgBox = true) 228 | { global DebugMsgBox_ShowMessages 229 | if (%DebugMsgBox_ShowMessages% != false && showMsgBox == true) 230 | MsgBox, %message% 231 | } 232 | -------------------------------------------------------------------------------- /src/DefaultCommands/DefaultCommands.ahk: -------------------------------------------------------------------------------- 1 | ;========================================================== 2 | ; Commands that will work without any additional things installed. 3 | ;========================================================== 4 | AddCommand("ReloadAHKScript", "Reloads this AutoHotKey script") 5 | ReloadAHKScript() 6 | { 7 | Run, %A_ScriptFullPath% 8 | } 9 | 10 | AddCommand("ExitAHKScript", "Stops and closes this AutoHotKey script") 11 | ExitAHKScript() 12 | { 13 | ExitApp 14 | } 15 | 16 | AddCommand("PauseAHKScript", "Pauses this AutoHotKey script") 17 | PauseAHKScript() 18 | { 19 | Pause 20 | } 21 | 22 | AddCommand("PCShutdown", "Turns the computer off") 23 | PCShutdown() 24 | { 25 | Run, shutdown.exe -s -t 00 26 | } 27 | 28 | AddCommand("PCRestart", "Restarts the computer") 29 | PCRestart() 30 | { 31 | Run, shutdown.exe -r -t 00 32 | } 33 | 34 | AddCommand("ExploreMyComputer", "Explore My Computer") 35 | ExploreMyComputer() 36 | { 37 | Run, ::{20d04fe0-3aea-1069-a2d8-08002b30309d} ; Opens the "My Computer" folder. 38 | } 39 | 40 | AddCommand("ExploreRecycleBin", "Explore the Recycle Bin") 41 | ExploreRecycleBin() 42 | { 43 | Run, ::{645ff040-5081-101b-9f08-00aa002f954e} ; Opens the Recycle Bin. 44 | } 45 | 46 | AddCommand("ExploreMyDocuments", "Explore the user's My Documents folder.") 47 | ExploreMyDocuments() 48 | { 49 | Run, explore %A_MyDocuments% 50 | } 51 | 52 | AddCommand("ExploreDesktop", "Explore the user's Desktop folder.") 53 | ExploreDesktop() 54 | { 55 | Run, explore %A_Desktop% 56 | } 57 | 58 | AddCommand("ExploreCDrive", "Explore C:\") 59 | ExploreCDrive() 60 | { 61 | Run, explore C:\ 62 | } 63 | 64 | AddCommand("OpenClipboard", "Opens whatever file/folder/url path is in the Clipboard, if it is valid") 65 | OpenClipboard() 66 | { 67 | ; Trim any whitespace, tabs, single-quotes and double-quotes off of the clipboard text before processing it. 68 | clipboardText := Trim(clipboard, " `t`'`"`"") 69 | 70 | ; If the file/folder path exists, open it. 71 | IfExist, %clipboardText% 72 | { 73 | Run, %clipboardText% 74 | } 75 | else 76 | { 77 | ; Determine if the clipboard contains a URL. 78 | urlRegex := "((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+-=\\\.&]*)" 79 | foundPosition := RegExMatch(clipboardText, urlRegex) 80 | 81 | ; If the start of the clipboard is a URL, open it. 82 | if (foundPosition = 1) 83 | Run, %clipboardText% 84 | ; Else this is not a file/folder path or a URL, so return error. 85 | else 86 | { 87 | return, "PATH DOES NOT EXIST:`r`n" . clipboardText 88 | } 89 | } 90 | } 91 | 92 | AddCommand("NewEmail", "Opens a new email in the default email program") 93 | NewEmail() 94 | { 95 | Run, mailto: 96 | } 97 | 98 | AddCommand("WindowClose", "Closes the currently active window") 99 | WindowClose() 100 | { global _cpActiveWindowID 101 | WinClose, ahk_id %_cpActiveWindowID% 102 | } 103 | 104 | AddNamedCommand("WindowCloseAll", "CloseAllWindows", "Closes all open windows") 105 | CloseAllWindows() 106 | { 107 | MatchList = AutoHotKey Help, Any Other Window Names To Leave Open 108 | 109 | WinGet, ID, List, , , Program Manager 110 | Loop, %ID% 111 | { 112 | StringTrimRight, This_ID, ID%A_Index%, 0 113 | WinGetTitle, This_Title, ahk_id %This_ID% 114 | If This_Title in %MatchList% 115 | Continue 116 | WinClose, %This_Title% 117 | } 118 | } 119 | 120 | AddCommand("WindowMinimize", "Minimizes the currently active window") 121 | WindowMinimize() 122 | { global _cpActiveWindowID 123 | WinMinimize, ahk_id %_cpActiveWindowID% 124 | } 125 | 126 | AddCommand("WindowMaximize", "Maximizes the currently active window") 127 | WindowMaximize() 128 | { global _cpActiveWindowID 129 | WinMaximize, ahk_id %_cpActiveWindowID% 130 | } 131 | 132 | AddCommand("WindowAlwaysOnTop", "Sets the active window to always be on top of others") 133 | WindowAlwaysOnTop() 134 | { global _cpActiveWindowID 135 | WinSet, AlwaysOnTop, On, ahk_id %_cpActiveWindowID% 136 | } 137 | 138 | AddCommand("WindowNotAlwaysOnTop", "Sets the active window to no longer always be on top of others") 139 | WindowNotAlwaysOnTop() 140 | { global _cpActiveWindowID 141 | WinSet, AlwaysOnTop, Off, ahk_id %_cpActiveWindowID% 142 | } 143 | 144 | AddCommand("ContextMenu", "Simulates a right-click by using Shift+F10") 145 | ContextMenu() 146 | { 147 | SendInput, +{F10} ; Shift + F10 to simulate right mouse click 148 | } 149 | 150 | AddCommand("WebBrowser", "Opens the default internet browser and searches for any comma-separated queries") 151 | WebBrowser(queries = "") 152 | { 153 | ; If queries were supplied, run them. 154 | if (queries != "") 155 | querySupplied := DoWebSearch(queries) 156 | ; Otherwise the user didn't supply a query, so just open the browser up to Google. 157 | else 158 | Run, www.google.com 159 | } 160 | 161 | ; Sends each of the supplied queries to the default web browser and returns if any queries were supplied or not (true/false). 162 | DoWebSearch(queries = "") 163 | { 164 | ; Loop through each of the terms to search for. 165 | Loop, Parse, queries, CSV 166 | { 167 | query := A_LoopField 168 | 169 | ; If this query is actually a URL, just go to the URL directly. 170 | if (RegExMatch(query, "^(https?://|www\.)|([a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?)$")) 171 | { 172 | address := query 173 | } 174 | ; Else the query is not a URL, so Google it. 175 | else 176 | { 177 | ; If the query starts with a "1", then do an "I'm feeling lucky" search. 178 | firstChar := SubStr(query, 1, 1) 179 | imFeelingLucky := false 180 | if (firstChar = 1) 181 | { 182 | StringTrimLeft, query, query, 1 183 | imFeelingLucky = true 184 | } 185 | 186 | ; Construct the address and then go to. 187 | address = www.google.ca/search?q=%query% 188 | 189 | ; If we should use the I'm Feeling Lucky, enable it. 190 | if (imFeelingLucky) 191 | address .= "&btnI=745" ; Adding &btnI=745 to the end of the URL uses Google's I'm Feeling Lucky. 192 | } 193 | 194 | ; Open up the address in a new tab. 195 | Run, %address% 196 | } 197 | } 198 | 199 | AddCommand("MonitorOff", "Turns the monitor off") 200 | MonitorOff() 201 | { 202 | Sleep 500 ; if you use this with a hotkey, not sleeping will make it so your keyboard input wakes up the monitor immediately. 203 | SendMessage 0x112, 0xF170, 2,,Program Manager ; send the monitor into standby (off) mode. 204 | } 205 | 206 | AddCommand("MuteSpeakersToggle", "Mutes/Un-mutes the volume on your computer") 207 | MuteSpeakersToggle() 208 | { 209 | ;SoundSet, +1, , mute ; Toggle volume mute on and off. 210 | SendInput, {Volume_Mute} 211 | } 212 | 213 | AddCommand("MediaNext", "Moves to the next track") 214 | MediaNext() 215 | { 216 | SendInput, {Media_Next} 217 | } 218 | 219 | AddCommand("MediaPrevious", "Moves to the previous track") 220 | MediaPrevious() 221 | { 222 | SendInput, {Media_Prev} 223 | } 224 | 225 | AddCommand("MediaPlayPause", "Plays/Pauses the current track") 226 | MediaPlayPause() 227 | { 228 | SendInput, {Media_Play_Pause} 229 | } 230 | 231 | AddCommand("MediaStop", "Stops the current track from playing") 232 | MediaStop() 233 | { 234 | SendInput, {Media_Stop} 235 | } 236 | 237 | AddCommand("ShowClipboardText", "Shows the text that is currently in the clipboard. Parameter specifies how many seconds before auto-closing it.", "3 seconds|3") 238 | ShowClipboardText(displayLengthInSeconds = 0) 239 | { 240 | MsgBox, , Clipboard Text (other content such as images are not shown here), %Clipboard%, %displayLengthInSeconds% 241 | } 242 | 243 | AddCommand("URLShortenAndPaste", "Replaces the long URL in the clipboard with a shortened one and pastes it") 244 | URLShortenAndPaste() 245 | { 246 | ; Get the URL from the clipboard 247 | longURL = %clipboard% 248 | 249 | ; Try and shorten the URL 250 | URL = http://tinyurl.com/api-create.php?url=%longURL% 251 | UrlDownloadToVar(URL,shortURL) 252 | 253 | ; If the URL was shortened, go to Success, otherwise try again 254 | StringLeft, prefix, shortURL, 4 255 | if (%prefix% = http) 256 | Goto, ShortenURL_Shortened 257 | 258 | ; Try and shorten the URL 259 | URL = http://is.gd/api.php?longurl=%longURL% 260 | UrlDownloadToVar(URL,shortURL) 261 | 262 | ; If the URL was shortened, go to Success, otherwise try again 263 | if (%prefix% = http) 264 | Goto, ShortenURL_Shortened 265 | 266 | ; Try and shorten the URL 267 | URL = http://api.tr.im/api/trim_simple?url=%longURL% 268 | UrlDownloadToVar(URL,shortURL) 269 | 270 | ; If the URL was shortened, go to Success, otherwise try again 271 | StringLeft, prefix, shortURL, 4 272 | if (%prefix% = http) 273 | Goto, ShortenURL_Shortened 274 | 275 | 276 | ShortenURL_ERROR: 277 | msg = Could not shorten the URL: `r`n %longURL% 278 | return %msg% 279 | 280 | ShortenURL_Shortened: 281 | Clipboard = %shortURL% 282 | SendInput, %shortURL% 283 | msg = Shortened %longURL% `r`n to %shortURL% 284 | return %msg% 285 | } 286 | UrlDownloadToVar(URL, ByRef Result, UserAgent = "", Proxy = "", ProxyBypass = "") { 287 | ; Requires Windows Vista, Windows XP, Windows 2000 Professional, Windows NT Workstation 4.0, 288 | ; Windows Me, Windows 98, or Windows 95. 289 | ; Requires Internet Explorer 3.0 or later. 290 | pFix:=a_isunicode ? "W" : "A" 291 | hModule := DllCall("LoadLibrary", "Str", "wininet.dll") 292 | 293 | AccessType := Proxy != "" ? 3 : 1 294 | ;INTERNET_OPEN_TYPE_PRECONFIG 0 // use registry configuration 295 | ;INTERNET_OPEN_TYPE_DIRECT 1 // direct to net 296 | ;INTERNET_OPEN_TYPE_PROXY 3 // via named proxy 297 | ;INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY 4 // prevent using java/script/INS 298 | 299 | io := DllCall("wininet\InternetOpen" . pFix 300 | , "Str", UserAgent ;lpszAgent 301 | , "UInt", AccessType 302 | , "Str", Proxy 303 | , "Str", ProxyBypass 304 | , "UInt", 0) ;dwFlags 305 | 306 | iou := DllCall("wininet\InternetOpenUrl" . pFix 307 | , "UInt", io 308 | , "Str", url 309 | , "Str", "" ;lpszHeaders 310 | , "UInt", 0 ;dwHeadersLength 311 | , "UInt", 0x80000000 ;dwFlags: INTERNET_FLAG_RELOAD = 0x80000000 // retrieve the original item 312 | , "UInt", 0) ;dwContext 313 | 314 | If (ErrorLevel != 0 or iou = 0) { 315 | DllCall("FreeLibrary", "UInt", hModule) 316 | return 0 317 | } 318 | 319 | VarSetCapacity(buffer, 10240, 0) 320 | VarSetCapacity(BytesRead, 4, 0) 321 | 322 | Result := "" 323 | 324 | Loop 325 | { 326 | ;http://msdn.microsoft.com/library/en-us/wininet/wininet/internetreadfile.asp 327 | irf := DllCall("wininet\InternetReadFile", "UInt", iou, "UInt", &buffer, "UInt", 10240, "UInt", &BytesRead) 328 | VarSetCapacity(buffer, -1) ;to update the variable's internally-stored length 329 | 330 | BytesRead_ = 0 ; reset 331 | Loop, 4 ; Build the integer by adding up its bytes. (From ExtractInteger-function) 332 | BytesRead_ += *(&BytesRead + A_Index-1) << 8*(A_Index-1) ;Bytes read in this very DllCall 333 | 334 | ; To ensure all data is retrieved, an application must continue to call the 335 | ; InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero. 336 | If (irf = 1 and BytesRead_ = 0) 337 | break 338 | Else ; append the buffer's contents 339 | { 340 | a_isunicode ? buffer:=StrGet(&buffer, "CP0") 341 | Result .= SubStr(buffer, 1, BytesRead_ * (a_isunicode ? 2 : 1)) 342 | } 343 | 344 | /* optional: retrieve only a part of the file 345 | BytesReadTotal += BytesRead_ 346 | If (BytesReadTotal >= 30000) ; only read the first x bytes 347 | break ; (will be a multiple of the buffer size, if the file is not smaller; trim if neccessary) 348 | */ 349 | } 350 | 351 | DllCall("wininet\InternetCloseHandle", "UInt", iou) 352 | DllCall("wininet\InternetCloseHandle", "UInt", io) 353 | DllCall("FreeLibrary", "UInt", hModule) 354 | } 355 | 356 | AddCommand("PastePlainText", "Pastes the contents of the clipboard as plain text, with all special formatting removed") 357 | PastePlainText() 358 | { 359 | originalClipboardContents = %ClipBoardAll% 360 | 361 | ClipBoard = %ClipBoard% ; Convert to text 362 | Send ^v ; Paste the text 363 | 364 | Sleep 50 ; Don't change clipboard until it is done pasting 365 | ClipBoard := OriginalClipboardContents ; Restore original clipboard contents 366 | originalClipboardContents = ; Free memory 367 | } 368 | 369 | ;========================================================== 370 | ; Commands that require Outlook to be installed. 371 | ;========================================================== 372 | 373 | AddCommand("Outlook", "Opens Outlook making sure it is maximized") 374 | Outlook() 375 | { 376 | outlookExecutablePath := GetOutlookExecutablePath() 377 | 378 | ; Try finding the window using the process name first (rather than matching the window title). 379 | ; OUTLOOK.EXE is the "Outlook (classic)" executable name, olk.exe is the "Outlook (new)" executable name. 380 | ; You may need to adjust the exe name here depending on if you're using the new version or not. 381 | windowID := PutWindowInFocus("ahk_exe OUTLOOK.EXE", outlookExecutablePath . " /recycle", 2) 382 | ; windowID := PutWindowInFocus("ahk_exe olk.exe", outlookExecutablePath . " /recycle", 2) 383 | 384 | ; If we found a window using the process name, make sure it's the main Outlook window and not the Reminders or other window. 385 | if (windowID > 0) 386 | { 387 | ; If this isn't the main Outlook window, reset the windowID so we can search for it by window title. 388 | WinGetTitle, windowTitle, A 389 | IfNotInString, windowTitle, "Outlook" 390 | windowID := 0 391 | } 392 | 393 | ; If not found, try looking for Outlook 2013+. 394 | if (windowID < 1) 395 | windowID := PutWindowInFocus("- Outlook", outlookExecutablePath . " /recycle", 2) 396 | 397 | ; If not found, try looking for Outlook 2010. 398 | if (windowID < 1) 399 | windowID := PutWindowInFocus("Microsoft Outlook", outlookExecutablePath . " /recycle", 2) 400 | 401 | ; If not found, try looking for any version of Outlook. 402 | if (windowID < 1) 403 | windowID := PutWindowInFocus("Outlook", outlookExecutablePath . " /recycle", 2) 404 | 405 | ; If we have a handle to the Outlook window, make sure it is maximized. 406 | if (windowID > 0) 407 | { 408 | ; Get the active window's Title to ensure it has "Outlook" in the name so we don't maximize the Reminders or other windows. 409 | WinGetTitle, windowTitle, A 410 | IfNotInString, windowTitle, "Outlook" 411 | return 412 | 413 | ; Maximize the window if it is not already maximized. 414 | WinGet, maximized, MinMax, ahk_id %windowID% 415 | if (maximized != 1) 416 | { 417 | WinMaximize, ahk_id %windowID% 418 | } 419 | } 420 | } 421 | 422 | GetOutlookExecutablePath() 423 | { 424 | outlookExecutablePaths := [ "C:\Program Files\Microsoft Office\root\Office16\OUTLOOK.EXE" 425 | , "C:\Program Files\Microsoft Office\Office15\OUTLOOK.EXE" 426 | , "C:\Program Files\Microsoft Office\Office14\OUTLOOK.EXE" 427 | , "C:\Program Files\Microsoft Office\Office12\OUTLOOK.EXE" 428 | , "C:\Program Files\Microsoft Office\Office11\OUTLOOK.EXE" 429 | , "C:\Program Files\Microsoft Office\Office10\OUTLOOK.EXE" ] 430 | 431 | ; Look for the executable in known locations, starting with the newest version. 432 | for index, outlookExecutablePath in outlookExecutablePaths 433 | { 434 | IfExist, %outlookExecutablePath% 435 | return %outlookExecutablePath% 436 | } 437 | 438 | ; If we couldn't find it in known locations, look for it dynamically (future proofing a bit). 439 | Loop Files, %A_ProgramFiles%\Microsoft Office\*Outlook.exe, R 440 | { 441 | return %A_LoopFileFullPath% 442 | } 443 | } 444 | 445 | AddCommand("NewOutlookAppointment", "Creates a new Appointment in Outlook") 446 | NewOutlookAppointment() 447 | { 448 | outlookExecutablePath := GetOutlookExecutablePath() 449 | Run, "%outlookExecutablePath%" /recycle /c ipm.appointment 450 | } 451 | 452 | AddCommand("OutlookCalendar", "Opens Outlook to the calendar view") 453 | OutlookCalendar() 454 | { 455 | outlookExecutablePath := GetOutlookExecutablePath() 456 | Run, "%outlookExecutablePath%" /recycle /select outlook:calendar 457 | } 458 | -------------------------------------------------------------------------------- /src/AHKCommandPicker.ahk: -------------------------------------------------------------------------------- 1 | /* 2 | IDEAS: 3 | 4 | - Make it easy for users to install and update AHK Command Picker without losing their customization commands/hotkeys. 5 | - This is the main factor to consider for v2.0. 6 | - Have an installer perhaps, along with a checkbox to automatically run the script at startup. 7 | - This could be done as it's own AHK script that prompts for the install location and to start at Startup or not. 8 | - Allow DefaultCommands and DefaultHotkeys to be excluded using the Settings in the GUI. 9 | - Make the AddCommandsFromVariable a standard thing so it's easy for people to add apps, directories, and websites to launch. 10 | - Dark mode. Ideally use Windows system default. 11 | 12 | - Have the GUI optional. Instead can just press CapsLock to bring up tooltip that says "Enter Command", and then user types in the tooltip instead of popping the large GUI. Could still autocomplete the command and show it in the tooltip though. 13 | - Maybe just hide the GUI instead of actually closing it every time; this would be good for tooltip mode too, since the tooltip could show the currently selected command from the window. 14 | - Allow user to create new simple commands easily from the GUI and save them in their own file (open file/program, path, website). 15 | 16 | - Use Ctrl+Space to copy the full command/parameter into the input textbox, and Ctrl+, to copy it with a trailing comma (easier for parameters). Or maybe use Shift instead of Ctrl. 17 | 18 | */ 19 | 20 | ; Use the two following commands to debug a script. 21 | ;ListVars 22 | ;Pause 23 | ;OutputDebug, % TEXT 24 | 25 | #SingleInstance force ; Make it so only one instance of this script can run at a time (and reload the script if another instance of it tries to run). 26 | #NoEnv ; Avoid checking empty variables to see if they are environment variables (better performance). 27 | 28 | ;========================================================== 29 | ; Global Variables - prefix everything with "cp" for Command Picker, so that variable/function names are not likely to conflict with user variables/function names. 30 | ;========================================================== 31 | _cpWindowName := "AHK Command Picker v2.1.0" 32 | _cpWindowGroup := "" ; The group that will hold our Command Picker window so we can reference it from # directive statements (e.g. #IfWinExists). 33 | _cpCommandList := "" ; Will hold the list of all available commands. 34 | _cpCommandSelected := "" ; Will hold the command selected by the user. 35 | _cpSearchedString := "" ; Will hold the actual string that the user entered. 36 | _cpCommandArray := Object() ; Will hold the array of all Command objects. 37 | _cpCommandDelimiter := "|" ; The character used to separate each command in the _cpCommandList. This MUST be the pipe character in order to work with a ListBox/ComboBox/DropDownList/Tab. 38 | _cpCommandIsRunning := false ; Tells if a Command is currently being executed or not. 39 | 40 | ; Delimeters/Separators seen or used by the user. 41 | _cpParameterDelimiter := "," ; The character used to separate each parameter in the AddCommand() function's parameter list and when manually typing in custom parameters into the search box. Also used to separate and each command in the AddCommands() function's command list. This MUST be a comma for the Regex whitespace removal to work properly, and it makes it easy to loop through all of the parameters using CSV. 42 | _cpCommandParameterListSeparator := "," ; The character used to separate the command name from the parameter list when the user is typing their command. 43 | _cpCommandDescriptionSeparator := "=>" ; The character or string used to separate the command name from the description of what the command does. 44 | _cpCommandParameterSeparator := "," ; The character or string used to separate the command name from the command's preset parameter name/value in the Listbox. 45 | _cpParameterNameValueSeparator := "|" ; The character used to separate a preset parameter's name from its value, in the AddCommand() function's parameter list. 46 | _cpCommandNameValueSeparator := "|" ; The character used to separate a command's name from its value, in the AddCommands() function's command list. 47 | 48 | ;---------------------------------------------------------- 49 | ; Global Variables Used By The User In Their Code. 50 | ;---------------------------------------------------------- 51 | _cpActiveWindowID := "" ; Will hold the ID of the Window that was Active when this picker was launched. 52 | _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := false ; Variable that user can set to True to disable the Escape key from reloading the script. 53 | 54 | ;---------------------------------------------------------- 55 | ; AHK Command Picker Settings - Specify the default Command Picker Settings, then load any existing settings from the settings file. 56 | ;---------------------------------------------------------- 57 | _cpSettingsFilePath := A_ScriptDir . "\AHKCommandPicker.settings" 58 | _cpShowAHKScriptInSystemTray := true 59 | _cpWindowWidthInPixels := 700 60 | _cpFontSize := 10 61 | _cpNumberOfCommandsToShow := 20 62 | _cpCommandMatchMethod := "Type Ahead" ; Valid values are: "Type Ahead" and "Incremental". 63 | _cpShowSelectedCommandWindow := true 64 | _cpNumberOfSecondsToShowSelectedCommandWindowFor := 2.0 65 | _cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand := true 66 | _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand := 4.0 67 | _cpEscapeKeyShouldReloadScriptWhenACommandIsRunning := true 68 | CPLoadSettings() 69 | 70 | ;========================================================== 71 | ; Load Command Picker Settings From File. 72 | ;========================================================== 73 | CPLoadSettings() 74 | { 75 | ; Include any global setting variables the we need. 76 | global _cpSettingsFilePath, _cpWindowWidthInPixels, _cpNumberOfCommandsToShow, _cpShowAHKScriptInSystemTray, _cpShowSelectedCommandWindow, _cpCommandMatchMethod, _cpNumberOfSecondsToShowSelectedCommandWindowFor, _cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand, _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand, _cpEscapeKeyShouldReloadScriptWhenACommandIsRunning 77 | 78 | ; If the file exists, read in its contents and then delete it. 79 | If (FileExist(_cpSettingsFilePath)) 80 | { 81 | ; Read in each line of the file. 82 | Loop, Read, %_cpSettingsFilePath% 83 | { 84 | ; Split the string at the = sign 85 | StringSplit, setting, A_LoopReadLine, = 86 | 87 | ; If this is a valid setting line (e.g. setting=value) 88 | if (setting0 = 2) 89 | { 90 | ; Get the setting variable's value 91 | _cp%setting1% = %setting2% 92 | } 93 | } 94 | } 95 | 96 | ; Save the settings. 97 | CPSaveSettings() 98 | 99 | ; Apply any applicable settings. 100 | CPShowAHKScriptInSystemTray(_cpShowAHKScriptInSystemTray) 101 | } 102 | 103 | ;========================================================== 104 | ; Save Command Picker Settings To File. 105 | ;========================================================== 106 | CPSaveSettings() 107 | { 108 | ; Include any global setting variables the we need. 109 | global _cpSettingsFilePath, _cpWindowWidthInPixels, _cpNumberOfCommandsToShow, _cpShowAHKScriptInSystemTray, _cpShowSelectedCommandWindow, _cpCommandMatchMethod, _cpNumberOfSecondsToShowSelectedCommandWindowFor, _cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand, _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand, _cpEscapeKeyShouldReloadScriptWhenACommandIsRunning 110 | 111 | ; Delete and recreate the settings file every time so that if new settings were added to code they will get written to the file. 112 | If (FileExist(_cpSettingsFilePath)) 113 | { 114 | FileDelete, %_cpSettingsFilePath% 115 | } 116 | 117 | ; Write the settings to the file (will be created automatically if needed) 118 | ; Setting name in file should be the variable name, without the "_cp" prefix. 119 | FileAppend, CPShowAHKScriptInSystemTray=%_cpShowAHKScriptInSystemTray%`n, %_cpSettingsFilePath% 120 | FileAppend, WindowWidthInPixels=%_cpWindowWidthInPixels%`n, %_cpSettingsFilePath% 121 | FileAppend, NumberOfCommandsToShow=%_cpNumberOfCommandsToShow%`n, %_cpSettingsFilePath% 122 | FileAppend, CommandMatchMethod=%_cpCommandMatchMethod%`n, %_cpSettingsFilePath% 123 | FileAppend, ShowSelectedCommandWindow=%_cpShowSelectedCommandWindow%`n, %_cpSettingsFilePath% 124 | FileAppend, NumberOfSecondsToShowSelectedCommandWindowFor=%_cpNumberOfSecondsToShowSelectedCommandWindowFor%`n, %_cpSettingsFilePath% 125 | FileAppend, ShowSelectedCommandWindowWhenInfoIsReturnedFromCommand=%_cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand%`n, %_cpSettingsFilePath% 126 | FileAppend, NumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand=%_cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand%`n, %_cpSettingsFilePath% 127 | FileAppend, EscapeKeyShouldReloadScriptWhenACommandIsRunning=%_cpEscapeKeyShouldReloadScriptWhenACommandIsRunning%`n, %_cpSettingsFilePath% 128 | } 129 | 130 | ;========================================================== 131 | ; Add and include the commands that should be available. 132 | ; 133 | ; Example: 134 | ; AddCommand("SQL", "Launch SQL Management Studio") 135 | ; SQL() 136 | ; { 137 | ; Run "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe" 138 | ; return false ; (optional) Return false to not display the command text after running the command. 139 | ; } 140 | ;========================================================== 141 | 142 | ;---------------------------------------------------------- 143 | ; Add a Dummy command to use for debugging. 144 | ;---------------------------------------------------------- 145 | AddNamedCommand("Dummy Command", "DummyCommand", "A command that doesn't do anything, but can be useful for testing and debugging", "Parameter1Name|Parameter1Value, Parameter2Value,Param3Name|Param3Value,Param4Value") 146 | DummyCommand(parameters = "") 147 | { 148 | ; Example of how to check if parameters were provided. 149 | if (parameters != "") 150 | MsgBox, Parameters were provided! 151 | 152 | ; Example of how to loop through the parameters 153 | Loop, Parse, parameters, CSV 154 | MsgBox Item %A_Index% is '%A_LoopField%' 155 | 156 | return, "This is some text returned by the dummy command." 157 | } 158 | 159 | ;---------------------------------------------------------- 160 | ; Define commands for users to easily edit their commands and hotkeys. 161 | ;---------------------------------------------------------- 162 | AddCommand("EditMyCommands", "Opens the MyCommands.ahk script for editing in the default editor, or notepad.") 163 | EditMyCommands() 164 | { 165 | filePath = %A_ScriptDir%\UserCommands\MyCommands.ahk 166 | Run, edit %filePath%,,UseErrorLevel 167 | if (%ErrorLevel% = ERROR) 168 | Run, "notepad" "%filePath%" 169 | } 170 | 171 | AddCommand("EditMyHotkeys", "Opens the MyHotkeys.ahk script for editing in the default editor, or notepad.") 172 | EditMyHotkeys() 173 | { 174 | filePath = %A_ScriptDir%\UserCommands\MyHotkeys.ahk 175 | Run, edit %filePath%,,UseErrorLevel 176 | if (%ErrorLevel% = ERROR) 177 | Run, "notepad" "%filePath%" 178 | } 179 | 180 | ;---------------------------------------------------------- 181 | ; Include our utility functions used by some of the Default Commands first. 182 | ;---------------------------------------------------------- 183 | #Include %A_ScriptDir%\DefaultCommands\UtilityFunctions.ahk 184 | 185 | ;---------------------------------------------------------- 186 | ; Include the files with the Commands we want to include in the picker. 187 | ;---------------------------------------------------------- 188 | #Include %A_ScriptDir%\DefaultCommands\DefaultCommands.ahk 189 | #Include %A_ScriptDir%\UserCommands\MyCommands.ahk 190 | 191 | ;---------------------------------------------------------- 192 | ; Include any files containing HotKeys/HotStrings last, as any AddCommand functions defined after 193 | ; a HotKey/HotString won't be loaded at startup, and hence, won't show up in the Command Picker list. 194 | ;---------------------------------------------------------- 195 | #Include %A_ScriptDir%\DefaultCommands\DefaultHotkeys.ahk 196 | #Include %A_ScriptDir%\UserCommands\MyHotkeys.ahk 197 | 198 | ;========================================================== 199 | ; Hotkey to launch the Command Picker window. 200 | ;========================================================== 201 | CapsLock:: 202 | SetCapslockState, Off ; Turn CapsLock off after it was pressed 203 | CPLaunchCommandPicker() 204 | return 205 | 206 | ;========================================================== 207 | ; Launch the Command Picker window. 208 | ;========================================================== 209 | CPLaunchCommandPicker() 210 | { 211 | ; Let this function know about the necessary global variables. 212 | global _cpWindowName, _cpActiveWindowID 213 | 214 | ; If the Command Picker Window is not already open and in focus. 215 | if !WinActive(_cpWindowName) 216 | { 217 | _cpActiveWindowID := WinExist("A") ; Save the ID of the Window that was active when the picker was launched. 218 | } 219 | CPPutCommandPickerWindowInFocus() 220 | } 221 | 222 | ;========================================================== 223 | ; Hotkey to reload the AHK Command Picker when it is executing a command. 224 | ; This can be used if one of the scripts is out of control and you need to kill it quickly. 225 | ;========================================================== 226 | Esc Up:: 227 | global _cpEscapeKeyShouldReloadScriptWhenACommandIsRunning, _cpCommandIsRunning, _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete 228 | 229 | ; Rethrow the Escape keypress that we intercepted. 230 | SendInput, {Esc} 231 | 232 | ; If the Escape key is allowed to reload the script when a Command is running, AND a Command is running, AND the user hasn't temporarily disabled the Escape key refresh functionaliry, then reload the script. 233 | if (_cpEscapeKeyShouldReloadScriptWhenACommandIsRunning && _cpCommandIsRunning && !_cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete) 234 | Run, %A_ScriptFullPath% 235 | return 236 | 237 | ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 238 | ; Only process the following hotkeys in this Command Picker window. 239 | ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 240 | #IfWinActive, ahk_group _cpWindowGroup 241 | 242 | ;========================================================== 243 | ; Intercept the Up and Down actions to move through the commands in the listbox. 244 | ;========================================================== 245 | Up::CPForwardKeyPressToListBox("Up") return 246 | Down::CPForwardKeyPressToListBox("Down") return 247 | PgUp::CPForwardKeyPressToListBox("PgUp") return 248 | PgDn::CPForwardKeyPressToListBox("PgDn") return 249 | ^Home::CPForwardKeyPressToListBox("Home") return ; Ctrl+Home to jump to top of list. 250 | ^End::CPForwardKeyPressToListBox("End") return ; Ctrl+End to jump to bottom of list. 251 | 252 | CPForwardKeyPressToListBox(key = "Down") 253 | { 254 | global _cpWindowName 255 | 256 | ; If the Command Picker window is active and the Edit box (i.e. search box) has focus. 257 | ControlGetFocus, control, %_cpWindowName% 258 | if (%control% = Edit1) 259 | { 260 | ; Move the selected command to the previous/next command in the list. 261 | ControlSend, ListBox1, {%key%}, %_cpWindowName% 262 | } 263 | } 264 | 265 | ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 266 | ; Return hotkeys back to being processed in all windows. 267 | ;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 268 | #IfWinActive 269 | 270 | ;========================================================== 271 | ; Create the Command Picker window if necessary and put it in focus. 272 | ;========================================================== 273 | CPPutCommandPickerWindowInFocus() 274 | { 275 | ; Let this function know that all variables except the passed in parameters are global variables. 276 | global _cpWindowName, _cpWindowGroup, _cpSearchedString 277 | 278 | ; If the window is already open. 279 | windowID := WinExist(_cpWindowName) 280 | if (windowID > 0) 281 | { 282 | ; Put the window in focus, and give focus to the text box. 283 | WinActivate, %_cpWindowName% 284 | ControlFocus, Edit1, %_cpWindowName% 285 | } 286 | ; Else the window is not already open. 287 | else 288 | { 289 | ; Create the window 290 | CPCreateCommandPickerWindow() 291 | 292 | ; Make sure this window is in focus before sending commands. 293 | WinWaitActive, %_cpWindowName%,, 5 294 | 295 | ; If the window wasn't opened for some reason (or it opened but was closed very quickly). 296 | windowID := WinExist(_cpWindowName) 297 | if (windowID = 0) 298 | { 299 | ; Exit. 300 | return false 301 | } 302 | } 303 | 304 | ; Add this window to Command Picker window group. 305 | GroupAdd, _cpWindowGroup, ahk_id %windowID% 306 | } 307 | 308 | ;========================================================== 309 | ; Create and show the Command Picker Window. 310 | ;========================================================== 311 | CPCreateCommandPickerWindow() 312 | { 313 | ; Let this function know about the necessary global variables. 314 | global _cpWindowName, _cpCommandList, _cpCommandArray, _cpCommandDelimiter, _cpCommandSelected, _cpSearchedString, _cpNumberOfCommandsToShow, _cpWindowWidthInPixels, _cpFontSize, _cpShowAHKScriptInSystemTray, _cpShowSelectedCommandWindow, _cpCommandParameterSeparator, _cpParameterNameValueSeparator, _cpCommandMatchMethod, _cpCommandParameterListSeparator, _cpParameterDelimiter, _cpCommandDescriptionSeparator 315 | 316 | ; Define any static variables needed for the GUI. 317 | static commandListBoxContents := "" ; Will hold the commands currently being shown in the command list. 318 | static lastKeyPressWasSearchingParameters := false ; Tells if the last search performed was on the command's preset parameters or not. 319 | 320 | ; Create the GUI. 321 | Gui 1:Default 322 | Gui, +AlwaysOnTop +Owner +OwnDialogs ToolWindow ; +Owner avoids a taskbar button ; +OwnDialogs makes any windows launched by this one modal ; ToolWindow makes border smaller and hides the min/maximize buttons. 323 | Gui, font, S%_cpFontSize% ; S=Size 324 | 325 | ; Calculate how to size and position the controls to look nice. 326 | editBoxWidth := _cpWindowWidthInPixels - (11.5 * _cpFontSize) ; Try and size the Edit box according to the font size so that it leaves enough room for the "Run Command" button. 327 | buttonHeightOffset := 0.25 * _cpFontSize ; The Button is taller than the Edit box, so calculate how much to move it up according to the font size to try and vertically center it with the Edit box. 328 | buttonLeftMargin := _cpFontSize ; Calculate how much to space the Button from the Edit box based on the font size. 329 | 330 | ; Add the controls to the GUI. 331 | Gui Add, Edit, w%editBoxWidth% v_cpSearchedString gSearchedStringChanged 332 | Gui Add, Button, gCommandSubmittedByButton Default x+%buttonLeftMargin% yp-%buttonHeightOffset%, Run Command ; default makes this the default action when Enter is pressed. 333 | Gui Add, ListBox, Sort v_cpCommandSelected gCommandSubmittedByListBoxClick w%_cpWindowWidthInPixels% r%_cpNumberOfCommandsToShow% hscroll vscroll xm 334 | 335 | ; Fill the ListBox with the commands. 336 | gosub, FillListBoxWithAllCommands 337 | 338 | ; Create and attach the Menu Bar 339 | Menu, FileMenu, Add, &Close Window, MenuHandler 340 | Menu, FileMane, Add ; Separator 341 | Menu, FileMenu, Add, &Exit (stop script from running), MenuHandler 342 | Menu, SettingsMenu, Add, Show &Settings, MenuHandler 343 | Menu, MyMenuBar, Add, &File, :FileMenu 344 | Menu, MyMenuBar, Add, &Settings, :SettingsMenu 345 | Gui, Menu, MyMenuBar 346 | 347 | ; Show the GUI, set focus to the input box, and wait for input. 348 | Gui, Show, AutoSize Center, %_cpWindowName% - Choose a command to run 349 | GuiControl, Focus, _cpSearchedString 350 | 351 | ; Display a tooltip that we are waiting for a command to be entered. 352 | CPShowTooltip("Enter a command") 353 | 354 | SetTimer, SleepScript, Off ; Restart the Sleep timer. 355 | SetTimer, SleepScript, 100 ; Sleep the AHK Command Picker several times a second so we can process hotkeys sent while commands are running. 356 | 357 | return ; End of auto-execute section. The script is idle until the user does something. 358 | 359 | SleepScript: 360 | ; Just sleep the script for one millisecond, so that if we are in a long-running loop in a user's command, the script will stop to 361 | ; check if any other hotkeys or hotstrings have been sent. 362 | ; This is extremely important as it allows us to kill this script with a hotkey if the user's command is out of control. 363 | Sleep, 1 364 | return 365 | 366 | MenuHandler: 367 | ; File menu commands. 368 | if (A_ThisMenu = "FileMenu") 369 | { 370 | if (A_ThisMenuItem = "&Close Window") 371 | goto, GuiClose 372 | else if (A_ThisMenuItem = "&Exit (stop script from running)") 373 | ExitApp 374 | } 375 | ; Settings menu commands. 376 | else if (A_ThisMenu = "SettingsMenu") 377 | { 378 | ; Close this window and show the Settings window. When the Settings window is closed this window will be reloaded to show any changes. 379 | Gui, 1:Destroy ; Close the GUI, but leave the script running. 380 | CPShowSettingsWindow() ; Show the Settings window so the user can change settings if they want. 381 | } 382 | return 383 | 384 | SearchedStringChanged: 385 | Gui 1:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 386 | 387 | ; A problem seemed to be introduced with Windows 8 where if the user types very fast the "text changed" event on the Edit box 388 | ; does not fire after the last key has been pressed, so we end up filtering the listbox on a different SearchedString than is in the Edit box. 389 | ; The solution is to re-run the search again if the SearchedString is different than what we just searched with, so we keep looping until we are sure we searched on the proper SearchedString. 390 | haveEnteredLoopOnce := false 391 | while ((haveEnteredLoopOnce = false) || isCurrentlyRunningSearchForCommand = false && textThatWillBeSearchedWith != _cpSearchedString) 392 | { 393 | haveEnteredLoopOnce := true 394 | 395 | ; Save the text that we are going to search on, so that we know if it has changed by the time the search finishes and if we need to search again or not. 396 | textThatWillBeSearchedWith := _cpSearchedString 397 | 398 | ; Filter the listbox to show items based on the new SearchedString. 399 | gosub, SearchForCommand 400 | 401 | ; Sleep for a short period of time to allow the GUI to update, and then do the search again if necessary. 402 | Sleep, 25 403 | Gui 1:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 404 | } 405 | return 406 | 407 | SearchForCommand: 408 | isCurrentlyRunningSearchForCommand := true 409 | 410 | searchingWithParameters := false ; Mark that we are searching for the command, ignoring any parameters that may be supplied. 411 | 412 | ; Get the user's search string, stripping off any parameters that may have been provided. 413 | searchText := _cpSearchedString 414 | StringGetPos, firstCommandParameterSeparatorPosition, _cpSearchedString, %_cpCommandParameterListSeparator% 415 | if (firstCommandParameterSeparatorPosition >= 0) 416 | { 417 | searchText := SubStr(_cpSearchedString, 1, firstCommandParameterSeparatorPosition) 418 | } 419 | 420 | ; Search for the command to select based on the user's input. 421 | gosub, PerformSearch 422 | 423 | ; If parameters are being provided, narrow the listbox contents down to just the currently selected command and any preset parameters that it might have before performing the search. 424 | if (firstCommandParameterSeparatorPosition >= 0) 425 | { 426 | gosub, PerformSearchForPresetParameters 427 | 428 | ; Record that this search was done on the preset parameters. 429 | lastKeyPressWasSearchingParameters := true 430 | } 431 | else 432 | { 433 | ; Record that this search was NOT done on the preset parameters. 434 | lastKeyPressWasSearchingParameters := false 435 | } 436 | 437 | isCurrentlyRunningSearchForCommand := false 438 | return 439 | 440 | PerformSearch: 441 | indexThatShouldBeSelected := 0 ; Assume that there are no matches for the given user string. 442 | currentSelectionsText := "" ; The text of the selected command that will be run. 443 | 444 | ; Do the search using whatever search method is specified in the settings. 445 | if (_cpCommandMatchMethod = "Incremental") 446 | gosub, IncrementalSearch 447 | else 448 | gosub, CamelCaseSearch 449 | 450 | ; Select the command in the list, using the index if we have it (faster than using string). 451 | ; If currentSelectedText is empty then indexThatShouldBeSelected is likely 0, but we still use the index so that NO item gets selected. 452 | if (indexThatShouldBeSelected > 0 || currentSelectionsText = "") 453 | GuiControl Choose, _cpCommandSelected, %indexThatShouldBeSelected% 454 | else 455 | GuiControl Choose, _cpCommandSelected, %currentSelectionsText% 456 | 457 | ; Ensure _cpCommandSelected gets updated with the new value, otherwise it sometimes has 458 | ; the wrong value when typing quickly, due to a race condition I suppose. 459 | Gui 1:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 460 | 461 | ; Display the currently selected command in the tooltip, and hide it after a bit of time. 462 | CPShowTooltip(_cpSearchedString . " (" . currentSelectionsText . ")") 463 | return 464 | 465 | PerformSearchForPresetParameters: 466 | ; If the user is trying to enter parameters for a command that doesn't exist, just exit. 467 | if (_cpCommandSelected = "") 468 | return 469 | 470 | ; Get the name of the currently selected command. 471 | commandName := CPGetCommandName(_cpCommandSelected) 472 | 473 | ; Create a delimited list of the command's preset parameters. 474 | commandParameters := "" 475 | commandParametersArray := _cpCommandArray[commandName].Parameters 476 | Loop, Parse, commandParametersArray, %_cpParameterDelimiter% 477 | { 478 | parameterNameAndValue := A_LoopField 479 | 480 | ; If this preset parameter has both a name and a value, strip them out to get the name. 481 | StringGetPos, firstDelimiterPosition, parameterNameAndValue, %_cpParameterNameValueSeparator% 482 | if (firstDelimiterPosition >= 0) 483 | { 484 | parameterName := SubStr(parameterNameAndValue, 1, firstDelimiterPosition) 485 | parameterValue := SubStr(parameterNameAndValue, firstDelimiterPosition + 2) ; +2 because SubStr starts at position 1 (not 0), and we want to start at the character AFTER the delimiter. 486 | } 487 | else 488 | parameterName := parameterNameAndValue 489 | 490 | ; Append this parameter name to the list of parameters 491 | parameterName := Trim(parameterName) 492 | commandParameters .= _cpCommandDelimiter . commandName . _cpCommandParameterSeparator . " " . parameterName 493 | } 494 | 495 | ; If we have some parameters, copy them into the ListBox contents (always have the parameterless command as the first item in the listbox). 496 | commandListBoxContents := _cpCommandArray[commandName].ToString() 497 | if (commandParameters != "") 498 | commandListBoxContents .= commandParameters 499 | 500 | ; Sort the list of commands alphabetically. 501 | Sort, commandListBoxContents, D%_cpCommandDelimiter% 502 | 503 | ; Refresh the list of words in the ListBox. 504 | GuiControl, -Redraw, _cpCommandSelected ; To improve performance, don't redraw the list box until all items have been added. 505 | GuiControl, , _cpCommandSelected, %_cpCommandDelimiter%%commandListBoxContents% ; Put a delimiter before the contents to clear the current listbox contents. 506 | GuiControl, +Redraw, _cpCommandSelected 507 | 508 | ; Setup the searchText to use to search for. 509 | ; Since the user may provide multiple parameters, we only want to search on the last parameter provided. 510 | StringGetPos, lastCommandParameterSeparatorPosition, _cpSearchedString, %_cpCommandParameterListSeparator%, R ; Search from the Right side of the string. 511 | searchText := SubStr(_cpSearchedString, 1, firstCommandParameterSeparatorPosition) ; Get the command text. 512 | searchText .= SubStr(_cpSearchedString, lastCommandParameterSeparatorPosition + 1) ; Append the last parameter provided. 513 | 514 | ; Since we want to search through the parameters, but the user may not have provided the full command name, modify the searchText so it looks like they did provide the full command name so the search focuses on the parameters. 515 | searchTextContentsAfterParameterSeparator := SubStr(searchText, firstCommandParameterSeparatorPosition + 2) ; +2 because SubStr first char position is 1 (not 0), and we don't want to include the CommandParameterSeparator. 516 | searchText := commandName . _cpCommandParameterSeparator . searchTextContentsAfterParameterSeparator 517 | 518 | ; Search for the item to select based on the user's input, now that we have populated the listbox with the preset parameters (if any). 519 | searchingWithParameters := true 520 | gosub, PerformSearch 521 | 522 | ; If no matches were found, choose the parameterless command (should always be the first item in the listbox). 523 | if (currentSelectionsText = "") 524 | GuiControl Choose, _cpCommandSelected, 1 525 | return 526 | 527 | IncrementalSearch: ; The user changed the text in the Edit box. 528 | ; If we are not searching through a command's preset parameters. 529 | if (searchingWithParameters = false) 530 | { 531 | ; If the last keypress was searching parameters (and now this one isn't), refresh the Listbox contents to show all commands, since they would have been changed to only show the preset parameters. 532 | if (lastKeyPressWasSearchingParameters) 533 | { 534 | gosub, FillListBoxWithAllCommands 535 | } 536 | } 537 | 538 | ; Incremental search will need to exactly match the listbox contents, and items are never removed in incremental mode so we can always just search the listbox contents. 539 | itemsToSearchThrough := commandListBoxContents 540 | 541 | searchedStringLength := StrLen(searchText) ; Get the length of the string the user entered. 542 | 543 | ; Loop through each item in the ListBox to see if the typed text matches any of the items. 544 | Loop Parse, itemsToSearchThrough, %_cpCommandDelimiter% 545 | { 546 | StringLeft part, A_LoopField, searchedStringLength ; We only want to compare the number of characters that the user has typed so far. 547 | If (part = searchText) 548 | { 549 | indexThatShouldBeSelected := A_Index 550 | currentSelectionsText := A_LoopField 551 | break 552 | } 553 | } 554 | return 555 | 556 | CamelCaseSearch: 557 | matchingCommands := "" ; Will hold all of the commands that match the search string, so we know which ones to keep in the ListBox. 558 | lowestWordIndexMatchedAgainst = 9999 ; Used to keep track of which command matched against the string in the least number of words (as we want to select that command by default). 559 | 560 | ; If we are not searching through a command's preset parameters. 561 | if (searchingWithParameters = false) 562 | { 563 | ; Because items are removed from the listbox in TypeAhead mode, we actually need to search the list of commands, so that when the user backspaces, items that were removed come back into the listbox. 564 | itemsToSearchThrough := _cpCommandList 565 | } 566 | ; Else we are searching through a command's preset parameters. 567 | else 568 | { 569 | ; Because we always repopulate the listbox contents with all preset parameters, we want to search through the listbox contents. 570 | itemsToSearchThrough := commandListBoxContents 571 | 572 | StringReplace, searchText, searchText, %_cpCommandParameterListSeparator% ; Strip the command-parameter list separator out so that it doesn't mess up the search. 573 | } 574 | 575 | ; Loop through each command to see if the typed text matches any of the commands. 576 | Loop Parse, itemsToSearchThrough, %_cpCommandDelimiter% 577 | { 578 | ; Skip empty entries (somehow an empty one gets added after backspacing out the entire search string). 579 | if (A_LoopField = "") 580 | continue 581 | 582 | ; Get the commandLine to process. 583 | commandLine := A_LoopField 584 | 585 | ; If we are not searching through preset parameters. 586 | if (searchingWithParameters = false) 587 | { 588 | ; Let's only search the command name, so that the camel-case search doesn't include the command description when matching. 589 | commandLine := CPGetCommandName(commandLine) 590 | } 591 | 592 | ; Strip any spaces out of the command name, as they just get in the way of the camel case matching. 593 | StringReplace, commandLine, commandLine, %A_Space%, , All 594 | 595 | ; Also strip out the command-parameter separator so that it doesn't interfere. 596 | StringReplace, commandLine, commandLine, %_cpCommandParameterSeparator% 597 | 598 | ; Break each camel-case word out of the Command Line by separating them with a space. 599 | ; Regex Breakdown: This will match against each word in Camel and Pascal case strings, while properly handling acrynoms. 600 | ; (^[a-z]+) Match against any lower-case letters at the start of the command. 601 | ; ([0-9]+) Match against one or more consecutive numbers (anywhere in the string, including at the start). 602 | ; ([A-Z]{1}[a-z]+) Match against Title case words (one upper case followed by lower case letters). 603 | ; ([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))) Match against multiple consecutive upper-case letters, leaving the last upper case letter out the match if it is followed by lower case letters, and including it if it's followed by the end of the string or a number. 604 | words := RegExReplace(commandLine, "((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))", "$1 ") 605 | words := Trim(words) 606 | 607 | ; Split the string into an array at each space. 608 | StringSplit, wordArray, words, %A_Space% 609 | 610 | ; Throw the array of words into an object so that we can pass it into the function below (array can't be passed directly by itself). 611 | camelCaseWordsInCommandLine := {} ; Create a new object to hold the array of words. 612 | camelCaseWordsInCommandLine.Length := wordArray0 ; Record how many words are in the array. 613 | camelCaseWordsInCommandLine.Words := Object() ; Will hold the array of all words in the Command. 614 | Loop, %wordArray0% 615 | { 616 | camelCaseWordsInCommandLine.Words[%A_Index%] := wordArray%A_Index% 617 | } 618 | 619 | ; Get if this command matches against the searched string, and how good the match is. 620 | lastWordIndexMatchedAgainst := CPCommandIsPossibleCamelCaseMatch(searchText, camelCaseWordsInCommandLine) 621 | 622 | ; If we are searching through parameters, make sure that the parameterless command is always added to the listbox, but make sure it is given a high value so that it is not chosen over another potential match. 623 | if (searchingWithParameters && InStr(commandLine, _cpCommandDescriptionSeparator)) 624 | lastWordIndexMatchedAgainst := 9999 625 | 626 | ; If this Command matches the search string, add it to our list of Commands to be displayed. 627 | if (lastWordIndexMatchedAgainst > 0) 628 | { 629 | ; Add this command to the list of matching commands. 630 | matchingCommands .= _cpCommandDelimiter . A_LoopField 631 | 632 | ; We want to select the command whose match is closest to the start of the string. 633 | if (lastWordIndexMatchedAgainst < lowestWordIndexMatchedAgainst) 634 | { 635 | ; Record the LowestWordIndex to beat and the command that should be selected so far. 636 | lowestWordIndexMatchedAgainst := lastWordIndexMatchedAgainst 637 | currentSelectionsText := A_LoopField 638 | } 639 | ; If these two commands match against the same word index, pick the one that is lowest alphabetically. 640 | else if(lastWordIndexMatchedAgainst = lowestWordIndexMatchedAgainst) 641 | { 642 | if (A_LoopField < currentSelectionsText) 643 | { 644 | currentSelectionsText := A_LoopField 645 | } 646 | } 647 | } 648 | } 649 | 650 | ; If we have some matches, copy the matches into the ListBox contents (leaving the leading delimiter as a signal to overwrite the existing contents), otherwise leave a delimiter character to clear the list. 651 | if (matchingCommands != "") 652 | commandListBoxContents := matchingCommands 653 | else 654 | commandListBoxContents := _cpCommandDelimiter 655 | 656 | ; Refresh the list of words in the ListBox. 657 | GuiControl, -Redraw, _cpCommandSelected ; To improve performance, don't redraw the list box until all items have been added. 658 | GuiControl, , _cpCommandSelected, %commandListBoxContents% 659 | GuiControl, +Redraw, _cpCommandSelected 660 | return 661 | 662 | GuiSize: ; The user resized the window. 663 | 664 | return 665 | 666 | 667 | CommandSubmittedByButton: ; The user submitted a selection using the Enter key or the Run Command button. 668 | gosub, CommandSubmitted 669 | return 670 | 671 | CommandSubmittedByListBoxClick: ; The user submitted a selection by clicking in the ListBox. 672 | ; Only allow double clicks to run a command. 673 | if (A_GuiEvent != "DoubleClick") 674 | return 675 | 676 | gosub, CommandSubmitted 677 | return 678 | 679 | CommandSubmitted: 680 | Gui 1:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 681 | commandWasSubmitted := true ; Record that the user actually wants to run the selected command (e.g. not just exit). 682 | 683 | ; If the user did not specify a valid command, don't process anything. 684 | if (_cpCommandSelected = "") 685 | { 686 | commandWasSubmitted := false 687 | return 688 | } 689 | 690 | GuiClose: ; The window was closed (by clicking X or through task manager). 691 | GuiEscape: ; The Escape key was pressed. 692 | Gui 1:Show, Hide ; Hide the GUI. 693 | CPHideTooltip() ; Hide the tooltip that we were showing. 694 | 695 | ; If the user just wants to close the window (i.e. they didn't submit a command), destroy it and exit. 696 | if (commandWasSubmitted != true) 697 | { 698 | Gui, 1:Destroy ; Close the GUI, but leave the script running. 699 | return 700 | } 701 | ; Else the user submitted a command. 702 | 703 | ; Strip the Description off the command to get just the Command Name. 704 | commandName := CPGetCommandName(_cpCommandSelected) 705 | 706 | ; Get each provided parameter and store it in an array. 707 | StringSplit, parametersArray, _cpSearchedString, %_cpCommandParameterListSeparator% 708 | 709 | ; If parameters were provided, get their proper values (since some may be preset parameters). 710 | parameters := "" 711 | if (parametersArray0 > 1) 712 | { 713 | ; Loop over each parameter that was typed by the user. 714 | Loop, %parametersArray0% 715 | { 716 | ; Skip the first loop since it just contains the command name that the user typed. 717 | if (A_Index = 1) 718 | continue 719 | 720 | ; If more than one parameter was provided (command name + 1 parameter = 2), select the value in the ListBox for this parameter. 721 | if (parametersArray0 > 2) 722 | { 723 | ; Form a new string to search the listbox with, which is just the Command Name and this one parameter. 724 | _cpSearchedString := commandName . _cpCommandParameterListSeparator . parametersArray%A_Index% 725 | 726 | ; Select the proper parameter value in the ListBox 727 | gosub, PerformSearchForPresetParameters 728 | Gui 1:Submit, NoHide ; Get the GUI's selected ListBox value. 729 | } 730 | 731 | ; If a preset parameter was chosen (i.e. the command-parameter separator is found in the ListBox's selected command, and the command-description separator is not). 732 | StringGetPos, positionOfCommandParameterSeparator, _cpCommandSelected, %_cpCommandParameterSeparator% 733 | StringGetPos, positionOfCommandDescriptionSeparator, _cpCommandSelected, %_cpCommandDescriptionSeparator% 734 | if (positionOfCommandParameterSeparator >= 0 && positionOfCommandDescriptionSeparator < 0) 735 | { 736 | ; Get the preset parameter value 737 | parameterValue := CPGetPresetParameterValues(commandName, SubStr(_cpCommandSelected, positionOfCommandParameterSeparator + 2)) ; +2 becaues SubStr starts at 1 (not 0), and we want to start at the character AFTER the delimiter. 738 | } 739 | ; Else get the custom parameter if one was specified. 740 | else 741 | { 742 | parameterValue := parametersArray%A_Index% 743 | } 744 | parameterValue := Trim(parameterValue) 745 | 746 | ; If a parameter was provided, add it to the list of parameters. 747 | if (parameterValue != "") 748 | { 749 | ; If this is not the first parameter, prepend it with the separator character. 750 | if (parameters = "") 751 | parameters := parameterValue 752 | else 753 | parameters .= _cpCommandParameterListSeparator . parameterValue 754 | } 755 | } 756 | } 757 | 758 | ; Destroy the GUI before running the selected command. 759 | Gui, 1:Destroy ; Close the GUI, but leave the script running. 760 | 761 | ; Run the command with the given parameters. 762 | CPRunCommand(commandName, parameters) 763 | return 764 | 765 | FillListBoxWithAllCommands: 766 | ; Copy the _cpCommandList into commandListBoxContents. 767 | commandListBoxContents := _cpCommandList 768 | 769 | ; Sort the list of commands alphabetically. 770 | Sort, commandListBoxContents, D%_cpCommandDelimiter% 771 | 772 | ; Refresh the list of words in the ListBox. 773 | GuiControl, -Redraw, _cpCommandSelected ; To improve performance, don't redraw the list box until all items have been added. 774 | GuiControl, , _cpCommandSelected, %_cpCommandDelimiter%%commandListBoxContents% ; Insert a delimiter onto the start of the list to clear the current listbox contents. 775 | GuiControl, +Redraw, _cpCommandSelected 776 | return 777 | } 778 | 779 | ; Shows the given Tooltip for the specified amount of time. 780 | CPShowTooltip(tooltipText = "", durationInMilliseconds = 3000) 781 | { 782 | ToolTip, %tooltipText% 783 | SetTimer, HideToolTip, Off ; Restart the tooltip timer. 784 | SetTimer, HideTooltip, %durationInMilliseconds% 785 | return 786 | 787 | HideTooltip: 788 | CPHideTooltip() 789 | return 790 | } 791 | 792 | ; Clears out and hides the Tooltip. 793 | CPHideTooltip() 794 | { 795 | SetTimer HideTooltip, Off 796 | Tooltip 797 | } 798 | 799 | ; Returns whether the given searchText matches against the words in the Array or not. 800 | CPCommandIsPossibleCamelCaseMatch(searchText, camelCaseWordsInCommandLine) 801 | { 802 | ; Strip any spaces out of the user's string, as they just get in the way of the camel case matching. 803 | StringReplace, searchText, searchText, %A_Space%, , All 804 | 805 | ; Get the number of characters that will need to be checked. 806 | lengthOfUsersString := StrLen(searchText) 807 | 808 | ; Call recursive function to roll through each letter the user typed, checking to see if it's part of one of the command's words. 809 | return CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchText, lengthOfUsersString, 1, camelCaseWordsInCommandLine.Words, camelCaseWordsInCommandLine.Length, 1, 1) 810 | } 811 | 812 | ; Recursive function to see if the searchString characters sequentially match characters in the word array, where as long as the first character in the word 813 | ; was matched again, the searchString could then match against the next sequential characters in that word, or match against the start of the next word in the array. 814 | ; searchString = the user string that was entered. 815 | ; searchStringLength = the number of characters in the searchString. 816 | ; searchCharacterIndex = the current character of the searchString that we are trying to find a match for. 817 | ; wordArray = the camel case words in the Command Name to match the searchString against. 818 | ; numberOfWordsInArray = the number of words in the wordArray. 819 | ; wordIndex = the current word in the wordArray that we are looking to match against. 820 | ; wordCharacterIndex = the current character of the wordIndex word that we are looking for a match against. 821 | CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchString, searchStringLength, searchCharacterIndex, wordArray, numberOfWordsInArray, wordIndex, wordCharacterIndex) 822 | { 823 | ; If all of the characters in the search string were matched, return true that this command is a possible match. 824 | if (searchCharacterIndex > searchStringLength) 825 | { 826 | ; Return the index of the word that was last matched against. 827 | return %wordIndex% 828 | } 829 | 830 | ; If we were asked to look against a word that doesn't exist, or past the last character in the word, just return false since we can't go any further on this path. 831 | if (wordIndex > numberOfWordsInArray || wordCharacterIndex > StrLen(wordArray%wordIndex%)) 832 | { 833 | return 0 834 | } 835 | 836 | ; Get the character at the specified character index 837 | character := SubStr(searchString, searchCharacterIndex, 1) 838 | 839 | ; If the character matches in this word, then keep going down this path. 840 | if (character = SubStr(wordArray%wordIndex%, wordCharacterIndex, 1)) 841 | { 842 | ; See if the next character matches the next character in the current word, or the start of the next word. 843 | match1 := CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchString, searchStringLength, searchCharacterIndex + 1, wordArray, numberOfWordsInArray, wordIndex, wordCharacterIndex + 1) 844 | match2 := CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchString, searchStringLength, searchCharacterIndex + 1, wordArray, numberOfWordsInArray, wordIndex + 1, 1) 845 | 846 | ; If one or both of the paths returned a match. 847 | if (match1 > 0 || match2 > 0) 848 | { 849 | ; Return the match with the lowest word index above zero (i.e. the one that matched closest to the start of the (array) string). 850 | if (match1 > 0 && match2 > 0) 851 | return match1 < match2 ? match1 : match2 ; Returns the Min out of match1 and match2. 852 | else 853 | return match1 < match2 ? match2 : match1 ; Returns the Max out of match1 and match2, since one of them is zero. 854 | } 855 | ; Else neither path found a match, so see if this character matches the start of the next word. 856 | else 857 | return CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchString, searchStringLength, searchCharacterIndex, wordArray, numberOfWordsInArray, wordIndex + 1, 1) 858 | } 859 | ; Otherwise the character doesn't match the current word. 860 | else 861 | { 862 | ; See if this character matches the start of the next word. 863 | return CPLetterMatchesPartOfCurrentWordOrBeginningOfNextWord(searchString, searchStringLength, searchCharacterIndex, wordArray, numberOfWordsInArray, wordIndex + 1, 1) 864 | } 865 | } 866 | 867 | ;========================================================== 868 | ; Run the given command. 869 | ;========================================================== 870 | CPRunCommand(commandName, parameters) 871 | { 872 | global _cpCommandArray, _cpShowSelectedCommandWindow, _cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand, _cpCommandIsRunning, _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete 873 | static _cpListOfFunctionsCurrentlyRunning, _cpListOfFunctionsCurrentlyRunningDelimiter := "|" 874 | 875 | ; If the Command to run doesn't exist, display error and exit. 876 | if (!_cpCommandArray[commandName]) 877 | { 878 | MsgBox, Command "%commandName%" does not exist. 879 | return 880 | } 881 | 882 | ; Get the Command's Function to call. 883 | commandFunction := _cpCommandArray[commandName].FunctionName 884 | 885 | ; If the Function to call doesn't exist, display an error and exit. 886 | if (!IsFunc(commandFunction)) 887 | { 888 | MsgBox, Function "%commandFunction%" does not exist. 889 | return 890 | } 891 | 892 | ; If we are already running the given function then exit with an error message, as running the same function concurrently may crash the AHK script. 893 | Loop Parse, _cpListOfFunctionsCurrentlyRunning, %_cpListOfFunctionsCurrentlyRunningDelimiter% 894 | { 895 | ; Skip empty entries (somehow an empty one gets added after backspacing out the entire search string). 896 | if (A_LoopField = commandFunction) 897 | { 898 | CPDisplayTextOnScreen("COMMAND NOT CALLED!", "'" . commandName . "' is currently running so it will not be called again.") 899 | return 900 | } 901 | } 902 | 903 | ; Record that we are running this function. 904 | _cpListOfFunctionsCurrentlyRunning .= commandFunction . _cpListOfFunctionsCurrentlyRunningDelimiter 905 | _cpCommandIsRunning := true 906 | 907 | ; If no parameters were given, but this command provides a default parameter, use the default parameter value. 908 | if (parameters = "" && _cpCommandArray[commandName].DefaultParameterValue != "") 909 | parameters := _cpCommandArray[commandName].DefaultParameterValue 910 | 911 | ; Call the Command's function, only passing in parameters if they were supplied (so that default parameter values will be used by functions). 912 | if (parameters = "") 913 | textReturnedFromCommand := %commandFunction%() 914 | else 915 | textReturnedFromCommand := %commandFunction%(parameters) 916 | 917 | ; Now that we are done running the function, remove it from our list of functions currently running. 918 | functionNameAndDelimeter := commandFunction . _cpListOfFunctionsCurrentlyRunningDelimiter 919 | StringReplace, _cpListOfFunctionsCurrentlyRunning, _cpListOfFunctionsCurrentlyRunning, %functionNameAndDelimeter% 920 | 921 | ; If we are no longer running any Commands at the moment. 922 | if (_cpListOfFunctionsCurrentlyRunning = "") 923 | { 924 | _cpCommandIsRunning := false ; Record that all functions have finished running. 925 | _cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := false ; Reset the Disable Escape Key user variable (if it was enabled) now that all commands have finished running. 926 | } 927 | 928 | ;~ ; Example of how to loop through the parameters 929 | ;~ For index, value in parameters 930 | ;~ MsgBox % "Item " index " is '" value "'" 931 | 932 | ; If the setting to show which command was ran is enabled, and the command did not explicitly return telling us to not show the text, display the command text. 933 | if ((_cpShowSelectedCommandWindow || (_cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand && parameters != "")) && textReturnedFromCommand != false) 934 | { 935 | ; Get the command's text to show. 936 | command := _cpCommandArray[commandName].ToString() 937 | 938 | ; Display the Command's text on the screen, as well as any text returned from the command (i.e. the textReturnedFromCommand). 939 | CPDisplayTextOnScreen(command, textReturnedFromCommand) 940 | } 941 | } 942 | 943 | ;========================================================== 944 | ; Parse the given command to pull the Command Name from it. 945 | ;========================================================== 946 | CPGetCommandName(command) 947 | { 948 | ; Let this function know about the necessary global variables. 949 | global _cpCommandDescriptionSeparator, _cpCommandParameterSeparator 950 | 951 | ; We're not sure if we are being given the Command+Description or Command+Parameter, so try and split against both. 952 | 953 | ; Replace each Command-Description separator string with an accent symbol so that it is easy to split against (since we can only split against characters, not strings). 954 | StringReplace, command, command, %_cpCommandDescriptionSeparator%, ``, All 955 | 956 | ; Replace each Command-Parameter separator string with an accent symbol so that it is easy to split against (since we can only split against characters, not strings). 957 | StringReplace, command, command, %_cpCommandParameterSeparator%, ``, All 958 | 959 | ; Split the string at the accent symbol, and strip spaces and tabs off each element. 960 | StringSplit, commandArray, command,``, %A_Space%%A_Tab% 961 | 962 | ; return the first element of the array, as that should be the command name. 963 | return %commandArray1% 964 | } 965 | 966 | ;========================================================== 967 | ; Retrieves a preset parameter's value, given its Name and the command it is on. 968 | ;========================================================== 969 | CPGetPresetParameterValues(commandName, presetParameterNames) 970 | { 971 | ; Let this function know about the necessary global variables. 972 | global _cpCommandArray, _cpParameterDelimiter, _cpParameterNameValueSeparator 973 | 974 | ; String to hold the parameter values to be returned. 975 | parameterValues := "" 976 | 977 | ; Loop through each of the given parameter Names. 978 | Loop, Parse, presetParameterNames, %_cpParameterDelimiter% 979 | { 980 | ; Trim any whitespace off the preset parameter name so that we can match against it properly. 981 | presetParameterName := Trim(A_LoopField) 982 | 983 | ; Variable to tell if we found a value for this presetParameterName or not. 984 | matchFound := false 985 | 986 | ; Get the command's preset parameters and loop through each of them. 987 | presetParameters := _cpCommandArray[commandName].Parameters 988 | Loop, Parse, presetParameters, %_cpParameterDelimiter% 989 | { 990 | parameterNameAndValue := A_LoopField 991 | 992 | ; If this preset parameter has both a name and a value, strip them out to get the value. 993 | StringGetPos, firstDelimiterPosition, parameterNameAndValue, %_cpParameterNameValueSeparator% 994 | if (firstDelimiterPosition >= 0) 995 | { 996 | parameterName := Trim(SubStr(parameterNameAndValue, 1, firstDelimiterPosition)) 997 | parameterValue := SubStr(parameterNameAndValue, firstDelimiterPosition + 2) ; +2 because SubStr starts at position 1 (not 0), and we want to start at the character AFTER the delimiter. 998 | } 999 | else 1000 | { 1001 | parameterName := Trim(parameterNameAndValue) 1002 | parameterValue := parameterNameAndValue 1003 | } 1004 | 1005 | ; If this is the parameter that we are looking for, append it to the parameter list to be returned and move on to the next parameter to process. 1006 | if (presetParameterName = parameterName) 1007 | { 1008 | parameterValues := CPAppendParameterValueToEndOfList(parameterValues, parameterValue) 1009 | matchFound := true 1010 | break 1011 | } 1012 | } 1013 | 1014 | ; If we couldn't find the parameter name in the list of preset parameters, assume this is a custom parameter and append it to the end of the list. 1015 | if (matchFound = false) 1016 | parameterValues := CPAppendParameterValueToEndOfList(parameterValues, presetParameterName) 1017 | } 1018 | 1019 | ; Return the list of parameter values that corresond to the list of parameter names we were given. 1020 | return parameterValues 1021 | } 1022 | 1023 | ; Returns the given parameterList with the given parameterValue appended to the end of it, separated by the parameter delimiter if necessary. 1024 | CPAppendParameterValueToEndOfList(parameterList, parameterValue) 1025 | { 1026 | global _cpParameterDelimiter 1027 | 1028 | ; If the list is empty right now, just set it to this value, otherwise append this value to the end of the list, separating it from the previous paramter value with the delimiter. 1029 | if (parameterList = "") 1030 | parameterList := parameterValue 1031 | else 1032 | parameterList .= _cpParameterDelimiter . parameterValue 1033 | 1034 | return parameterList 1035 | } 1036 | 1037 | ;========================================================== 1038 | ; Displays the Settings window to allow user to change settings. 1039 | ;========================================================== 1040 | CPShowSettingsWindow() 1041 | { 1042 | ; Let this function know about the necessary global variables. 1043 | global _cpWindowName, _cpNumberOfCommandsToShow, _cpWindowWidthInPixels, _cpFontSize, _cpShowAHKScriptInSystemTray, _cpCommandMatchMethod, _cpShowSelectedCommandWindow, _cpNumberOfSecondsToShowSelectedCommandWindowFor, _cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand, _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand, _cpEscapeKeyShouldReloadScriptWhenACommandIsRunning 1044 | 1045 | ; Define any static variables needed for the GUI. 1046 | static CommandMatchMethodDescription 1047 | 1048 | Gui 2:Default ; Specify that these controls are for window #2. 1049 | 1050 | ; Create the GUI. 1051 | Gui, +AlwaysOnTop +Owner ToolWindow ; +Owner avoids a taskbar button ; +OwnDialogs makes any windows launched by this one modal ; ToolWindow makes border smaller and hides the min/maximize buttons. 1052 | 1053 | ; Add the controls to the GUI. 1054 | Gui, Add, Checkbox, v_cpShowAHKScriptInSystemTray Checked%_cpShowAHKScriptInSystemTray%, Show AHK script in the system tray 1055 | 1056 | Gui, Add, GroupBox, x10 w500 r8, Command Picker Window Options: 1057 | Gui, Add, Text, yp+25 x20, Number of commands to show: 1058 | Gui, Add, Edit, x+5 1059 | Gui, Add, UpDown, v_cpNumberOfCommandsToShow Range1-50, %_cpNumberOfCommandsToShow% 1060 | 1061 | Gui, Add, Text, yp+25 x20, Font size: 1062 | Gui, Add, Edit, x+5 1063 | Gui, Add, UpDown, v_cpFontSize Range5-25, %_cpFontSize% 1064 | 1065 | Gui, Add, Text, yp+25 x20, Window width (in pixels): 1066 | Gui, Add, Edit, x+5 1067 | Gui, Add, UpDown, v_cpWindowWidthInPixels Range100-2000, %_cpWindowWidthInPixels% 1068 | 1069 | Gui, Add, Text, yp+25 x20, Command search method: 1070 | Gui, Add, DropDownList, x+5 v_cpCommandMatchMethod gCommandMatchMethodChanged Sort, Type Ahead|Incremental 1071 | Gui, Add, Text, yp+25 x40 vCommandMatchMethodDescription w450 h45, 1072 | 1073 | ;gui, add, text, xm w500 0x10 ;Horizontal Line 1074 | 1075 | Gui, Add, GroupBox, x10 w500 r3, Selected Command Window Options: 1076 | Gui, Add, Checkbox, yp+25 x20 v_cpShowSelectedCommandWindow Checked%_cpShowSelectedCommandWindow% gShowSelectedCommandWindowToggled, Show selected command window (after picker closes) for 1077 | Gui, Add, Edit, x+0 w50 v_cpNumberOfSecondsToShowSelectedCommandWindowFor, %_cpNumberOfSecondsToShowSelectedCommandWindowFor% 1078 | ;~ Gui, Add, UpDown, v_cpNumberOfSecondsToShowSelectedCommandWindowFor Range1-10, %_cpNumberOfSecondsToShowSelectedCommandWindowFor% 1079 | Gui, Add, Text, x+5, seconds. 1080 | Gui, Add, Checkbox, yp+25 x20 v_cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand Checked%_cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand% gShowSelectedCommandWindowWhenInfoIsReturnedFromCommandToggled, Show selected command window when command returns info for 1081 | Gui, Add, Edit, x+0 w50 v_cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand, %_cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand% 1082 | Gui, Add, Text, x+5, seconds. 1083 | 1084 | Gui, Add, GroupBox, x10 w500 r4, Escape Key Options: 1085 | Gui, Add, Checkbox, yp+25 x20 v_cpEscapeKeyShouldReloadScriptWhenACommandIsRunning Checked%_cpEscapeKeyShouldReloadScriptWhenACommandIsRunning%, Allow the Escape key to kill all currently running commands. 1086 | Gui, Add, Text, yp+20 x40, The Escape key can be used to kill Commands that are currently running by reloading the script.`nIf enabled, but you need to use "Send {Escape}" in your Command, temporarily disable this by`nusing "_cpDisableEscapeKeyScriptReloadUntilAllCommandsComplete := true" in your Command. 1087 | 1088 | Gui, Add, Button, gSettingsCancelButton xm w100, Cancel 1089 | Gui, Add, Button, gSettingsSaveButton x+300 w100, Save 1090 | 1091 | GuiControl, Choose, _cpCommandMatchMethod, %_cpCommandMatchMethod% 1092 | gosub, CommandMatchMethodChanged ; Display the description of the currently selected Command Match Method. 1093 | 1094 | ; Show the GUI, set focus to the input box, and wait for input. 1095 | Gui, Show, AutoSize Center, %_cpWindowName% - Settings 1096 | 1097 | return ; End of auto-execute section. The script is idle until the user does something. 1098 | 1099 | CommandMatchMethodChanged: 1100 | Gui 2:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 1101 | 1102 | ; Update the shown description based on which search method is selected. 1103 | if (_cpCommandMatchMethod = "Type Ahead") 1104 | GuiControl, , Static5, - Matches against Camel/Pascal Casing of any part of the command name.`n- Filters the list as you type to only show possible matches.`nE.g. 'WebBro', 'WB', 'B', and 'Brow' would all match against a 'WebBrowser' command. 1105 | else 1106 | GuiControl, , Static5, - Only matches against the command name exactly.`n- Will not filter the list as you type.`nE.g. 'WebBro' would match against a 'WebBrowser' command. 1107 | return 1108 | 1109 | ShowSelectedCommandWindowToggled: 1110 | Gui 2:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 1111 | 1112 | ; If ShowSelectedCommandWindow is checked, also check off the ShowSelectedCommandWindowWhenInfoIsReturnedFromCommand checkbox. 1113 | if (_cpShowSelectedCommandWindow) 1114 | GuiControl, , Button5, 1 1115 | return 1116 | 1117 | ShowSelectedCommandWindowWhenInfoIsReturnedFromCommandToggled: 1118 | Gui 2:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 1119 | 1120 | ; If ShowSelectedCommandWindowWhenInfoIsReturnedFromCommand is unchecked, also uncheck the ShowSelectedCommandWindow checkbox. 1121 | if (_cpShowSelectedCommandWindowWhenInfoIsReturnedFromCommand = false) 1122 | GuiControl, , Button4, 0 1123 | return 1124 | 1125 | SettingsSaveButton: ; Settings Save button was clicked. 1126 | Gui 2:Submit, NoHide ; Get the values from the GUI controls without closing the GUI. 1127 | CPSaveSettings() ; Save the settings before loading them again. 1128 | 1129 | SettingsCancelButton: ; Settings Cancel button was clicked. 1130 | 2GuiClose: ; The window was closed (by clicking X or through task manager). 1131 | 2GuiEscape: ; The Escape key was pressed. 1132 | CPLoadSettings() ; If user pressed Cancel the old settings will be loaded. If they pressed Save the saved settings will be loaded. 1133 | Gui, 2:Destroy ; Close the GUI, but leave the script running. 1134 | CPLaunchCommandPicker() ; Re-launch the Command Picker. 1135 | return 1136 | } 1137 | 1138 | ;========================================================== 1139 | ; Displays the given text on the screen for a given duration. 1140 | ;========================================================== 1141 | CPDisplayTextOnScreen(title, text = "") 1142 | { 1143 | ; Import any required global variables. 1144 | global _cpNumberOfSecondsToShowSelectedCommandWindowFor, _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand 1145 | 1146 | titleFontSize := 24 1147 | textFontSize := 16 1148 | 1149 | ; Shrink the margin so that the text goes up close to the edge of the window border. 1150 | windowMargin := titleFontSize * 0.1 1151 | 1152 | Gui 3:Default ; Specify that these controls are for window #3. 1153 | 1154 | ; Create the transparent window to display the text 1155 | backgroundColor = DDDDDD ; Can be any RGB color (it will be made transparent below). 1156 | Gui +LastFound +AlwaysOnTop -Caption +ToolWindow +Border ; +ToolWindow avoids a taskbar button and an alt-tab menu item. 1157 | Gui, Margin, %windowMargin%, %windowMargin% 1158 | Gui, Color, %backgroundColor% 1159 | Gui, Font, s%titleFontSize% bold 1160 | Gui, Add, Text,, %title% 1161 | Gui, Font, s%textFontSize% norm 1162 | if (text != "") 1163 | Gui, Add, Text,, %text% 1164 | WinSet, TransColor, FFFFFF 180 ; Make all pixels of this color transparent (shouldn't be any with color FFFFFF) and make all other pixels semi-transparent. 1165 | Gui, Show, AutoSize Center NoActivate ; NoActivate avoids deactivating the currently active window. 1166 | 1167 | ; Set the window to close after the given duration. 1168 | if (text = "") 1169 | durationToShowWindowForInMilliseconds := _cpNumberOfSecondsToShowSelectedCommandWindowFor * 1000 1170 | else 1171 | durationToShowWindowForInMilliseconds := _cpNumberOfSecondsToShowSelectedCommandWindowForWhenInfoIsReturnedFromCommand * 1000 1172 | SetTimer, CloseWindow, %durationToShowWindowForInMilliseconds% 1173 | ;SetTimer, FadeOutText, 200 ; Update every 200ms. 1174 | return 1175 | 1176 | FadeOutText: 1177 | 1178 | return 1179 | 1180 | CloseWindow: 1181 | SetTimer, CloseWindow, Off ; Make sure the timer doesn't fire again. 1182 | Gui, 3:Destroy ; Close the GUI, but leave the script running. 1183 | return 1184 | } 1185 | 1186 | 1187 | ;========================================================== 1188 | ; Adds the given command to our global list of commands. 1189 | ;========================================================== 1190 | AddCommand(functionName, descriptionOfWhatFunctionDoes = "", parameterList = "", defaultParameterValue = "") 1191 | { 1192 | AddNamedCommand(functionName, functionName, descriptionOfWhatFunctionDoes, parameterList, defaultParameterValue) 1193 | } 1194 | 1195 | ; commandName = The name of the command to appear in the Command Picker. 1196 | ; functionName = The function to call when this command is selected to run. 1197 | ; descriptionOfWhatFunctionDoes = A user-friendly message that will appear in the Command Picker telling what the command does. 1198 | ; parameterList = A pre-set list of parameters to choose from in the Command Picker when this command is selected. 1199 | ; Parameter values should be separated by a comma, and if you would like your parameter to show a different name in the GUI, separate the name from the value with a pipe character (|) (e.g. "Name1|Value1, Value2, Name3|Value3"). 1200 | ; defaultParameterValue = The parameter value that will be passed to the function when no other parameter is given. 1201 | AddNamedCommand(commandName, functionName, descriptionOfWhatFunctionDoes = "", parameterList = "", defaultParameterValue = "") 1202 | { 1203 | global _cpCommandList, _cpCommandArray, _cpCommandDelimiter, _cpCommandDescriptionSeparator, _cpCommandParameterSeparator, _cpCommandParameterListSeparator 1204 | 1205 | ; Trim all of the given values. 1206 | commandName := Trim(commandName) 1207 | functionName := Trim(functionName) 1208 | descriptionOfWhatFunctionDoes := Trim(descriptionOfWhatFunctionDoes) 1209 | parameterList := Trim(parameterList) 1210 | defaultParameterValue := Trim(defaultParameterValue) 1211 | 1212 | ; The Command Names should be unique, so make sure it is not already in the list. 1213 | if (_cpCommandArray[commandName]) 1214 | { 1215 | MsgBox, The command '%commandName%' has already been added to the list of commands. Command names should be unique. 1216 | return 1217 | } 1218 | 1219 | ; Make sure the Command Name does not contain any special strings that we parse on. 1220 | if (InStr(commandName, _cpCommandDescriptionSeparator)) 1221 | { 1222 | MsgBox, The command '%commandName%' is not allowed to contain the special string '%_cpCommandDescriptionSeparator%'. You must remove this string from the command's name before it will be allowed. 1223 | return 1224 | } 1225 | if (InStr(commandName, _cpCommandParameterSeparator)) 1226 | { 1227 | MsgBox, The command '%commandName%' is not allowed to contain the special string '%_cpCommandParameterSeparator%'. You must remove this string from the command's name before it will be allowed. 1228 | return 1229 | } 1230 | if (InStr(commandName, _cpCommandParameterListSeparator)) 1231 | { 1232 | MsgBox, The command '%commandName%' is not allowed to contain the special string '%_cpCommandParameterListSeparator%'. You must remove this string from the command's name before it will be allowed. 1233 | return 1234 | } 1235 | 1236 | ; Remove any whitespace around the parameter. 1237 | ; Ideally this regex would be the following, but it breaks at run-time since ',' is a special character: 1238 | ; RegExReplace(parameters, "(\s*%_cpParameterDelimiter%\s*)", "%_cpParameterDelimiter%") 1239 | parameterList := RegExReplace(parameterList, "(\s*,\s*)", ",") 1240 | 1241 | ; Create the command object and fill its properties. 1242 | command := {} 1243 | command.CommandName := commandName 1244 | command.FunctionName := functionName 1245 | command.Description := descriptionOfWhatFunctionDoes 1246 | command.Parameters := parameterList 1247 | command.DefaultParameterValue := defaultParameterValue 1248 | command.ToString := Func("CPCommand_ToString") 1249 | 1250 | ; Add the command into the Command Array. 1251 | _cpCommandArray[commandName] := command 1252 | 1253 | ; Add the command into our delimited list of commands string. 1254 | commandString := commandName . " " . _cpCommandDescriptionSeparator . " " . descriptionOfWhatFunctionDoes 1255 | if (_cpCommandList = "") 1256 | _cpCommandList := commandString 1257 | else 1258 | _cpCommandList .= _cpCommandDelimiter . commandString 1259 | } 1260 | 1261 | ; Defines the ToString() function for our Command objects. 1262 | CPCommand_ToString(this) 1263 | { global _cpCommandDescriptionSeparator 1264 | return this.CommandName . " " . _cpCommandDescriptionSeparator . " " . this.Description 1265 | } 1266 | 1267 | ; Calls the AddNamedCommand() function for each command in the commandList. 1268 | ; Each command in the list will call the given functionName, supplying the command's specific value as a parameter to the function. 1269 | ; commandList = The commands to show up in the picker that will call the function, separated with _cpParameterDelimiter (a comma by default). 1270 | ; Separate the command name that appears in the picker and the value to pass to the function with _cpCommandNameValueSeparator (a pipe character | by default). 1271 | ; If no pipe character is provided, the given value will be both shown in the picker and passed to the function. 1272 | ; prefix = The prefix to add to the beginning of all the command names in the commandList. 1273 | ; postfix = The postfix to add to the end of all the command names in the commandList. 1274 | AddCommandsWithPrePostfix(functionName, descriptionOfWhatFunctionDoes = "", commandList = "", prefix = "", postfix = "") 1275 | { 1276 | global _cpCommandNameValueSeparator, _cpParameterDelimiter 1277 | 1278 | ; Trim the given values that won't be passed explicitly into AddNamedCommand to be trimmed. 1279 | commandList := Trim(commandList) 1280 | prefix := Trim(prefix) 1281 | postfix := Trim(postfix) 1282 | 1283 | ; Loop through each command in the commandList 1284 | Loop, Parse, commandList, %_cpParameterDelimiter% 1285 | { 1286 | commandNameAndValue := A_LoopField 1287 | 1288 | ; If this command has both a name and a value, strip them out. 1289 | StringGetPos, firstDelimiterPosition, commandNameAndValue, %_cpCommandNameValueSeparator% 1290 | if (firstDelimiterPosition >= 0) 1291 | { 1292 | commandName := SubStr(commandNameAndValue, 1, firstDelimiterPosition) 1293 | commandValue := SubStr(commandNameAndValue, firstDelimiterPosition + 2) ; +2 because SubStr starts at position 1 (not 0), and we want to start at the character AFTER the delimiter. 1294 | } 1295 | ; Otherwise use the value for both. 1296 | else 1297 | { 1298 | commandName := commandNameAndValue 1299 | commandValue := commandNameAndValue 1300 | } 1301 | 1302 | ; Add this command to the list of cmmands. 1303 | commandName := prefix . Trim(commandName) . postfix 1304 | commandValue := Trim(commandValue) 1305 | AddNamedCommand(commandName, functionName, descriptionOfWhatFunctionDoes, commandValue, commandValue) 1306 | } 1307 | } 1308 | 1309 | AddCommands(functionName, descriptionOfWhatFunctionDoes = "", commandList = "") 1310 | { 1311 | AddCommandsWithPrePostfix(functionName, descriptionOfWhatFunctionDoes, commandList) 1312 | } 1313 | 1314 | ;~ ; Example of how to add a named command. 1315 | ;~ AddNamedCommand("FF", "FireFox", "Opens Firefox", "xnaparticles.com, dpsf.com, digg.com") 1316 | ;~ FireFox(website = "") 1317 | ;~ { 1318 | ;~ ; Code to open website in firefox goes here. 1319 | ;~ } 1320 | 1321 | ;~ ; Example of how to use AddCommands() 1322 | ;~ AddCommands("OpenDirectory", "Opens the specified directory in Windows Explorer", "exploreC|C:\, exploreDPSF|C:\DPSF, C:\Windows") 1323 | ;~ OpenDirectory(path = "") 1324 | ;~ { 1325 | ;~ Run, explore %path% 1326 | ;~ } 1327 | 1328 | ;========================================================== 1329 | ; Shows or Hides the Tray Icon for this AHK Script. 1330 | ;========================================================== 1331 | CPShowAHKScriptInSystemTray(show) 1332 | { 1333 | ; If we should show the Tray Icon. 1334 | if (show) 1335 | menu, tray, Icon 1336 | ; Else hide the Tray Icon. 1337 | else 1338 | menu, tray, NoIcon 1339 | } 1340 | 1341 | ;========================================================== 1342 | ; Adds the given parameter to parameter string 1343 | ;========================================================== 1344 | AddParameterToString(ByRef parametersString, parameterToAdd) 1345 | { global _cpParameterDelimiter 1346 | if (parametersString = "") 1347 | parametersString := parameterToAdd 1348 | else 1349 | parametersString .= _cpParameterDelimiter . parameterToAdd 1350 | } 1351 | --------------------------------------------------------------------------------