How to Turn AutoHotkey Hotstring AutoCorrect Pop-up Menus into a Function, (Part 5, Beginning Hotstrings)

Save Redundant AutoHotkey Code by Creating a Function for Pop-up Hotstring Menus

*          *          *

Beginning AutoHotkey Hotstrings 200pxAs a convenience for people who want to get this entire series (plus more) in one place, I have now published the e-book Beginning AutoHotkey Hotstrings which is available on the ComputorEdge E-Books site (in three formats—EPUB for mobile devices, MOBI for Kindle, and PDF for printing and computer) and through Amazon. Regardless of whether you’re interested in the book or not, it’s worth your time to peruse some of the blogs linked at “Beginning AutoHotkey Hotstring Techniques” found under the “AutoHotkey Topics and Series” tab in the top menu bar. They just might inspire your next AutoHotkey script.

*          *          *

In the last blog I introduced a technique for creating AutoHotkey selection menus whenever there are multiple possible choices for common misspellings or typos. That snippet of code can be reused by merely changing the activation Hotstring and including a Menu, Add command line for each possible correction. The primary problem with this approach is the number of possible selections adds many additional lines of code to the script for each new AutoCorrect word. By building a function the redundant code is written only one time and used over and over again. At most, each new Hotstring menu only requires three lines of code. Let’s start with the agin example from last time:

::agin::
 Menu, MyMenu, add, again, MenuAction
 Menu, MyMenu, add, a gin, MenuAction
 Menu, MyMenu, add, aging, MenuAction
 Menu, MyMenu, Show
Return

MenuAction:
 SendInput %A_ThisMenuItem%
 Menu, MyMenu, DeleteAll
Return

The plan is to place all the Menu commands inside a function which builds a new AutoCorrect menu each time it is called. One calling function will set up a menu for all of the AutoCorrect options:

TextMenu(Words)

I’ve named the function TextMenu. Any parameters needed by the function appear within the parentheses. In this case the parameter Words contains the list of possible replacement options for the AutoCorrect menu. We can directly add the words to the function by enclosing the list in double quotes. For example, using agin as the Hotstring:

::agin::
  TextMenu("again,a gin,aging")
Return

If the TextMenu() function is properly constructed, this is all that will be needed to set up an individual Hotstring menu. Although almost any separating character may be used, here commas are placed between the option words in the list. The commas make it easy to separate the list into individual words using another AutoHotkey trick inside the function. The plan is to parse the list of Words to create a menu item for each entry with the following function:

TextMenu(TextOptions)
{
  StringSplit, MenuItems, TextOptions , `,
  Loop %MenuItems0%
  {
    Item := MenuItems%A_Index%
    Menu, MyMenu, add, %Item%, MenuAction
  }
  Menu, MyMenu, Show
}

This function may look a little more complex than merely repeating the code from the last blog, but once understood, this snippet will add more power to your AutoCorrect script. A new menu building command called TextMenu() is added to your bag of tricks. There are two important AutoHotkey commands used to build this menu function.

ComputorEdge AutoHotkey E-BooksThe first is the StringSplit command which separates (parses) the list of Words into individual pieces. In the above function, StringSplit automatically breaks up the TextOptions word list into each word and stores it in a different numbered variable. Note that the function assigned the variable TextOptions (inside the parentheses) to the list of words passed to the function. The variable TextOptions only temporarily exists within the function and does not affect any other part of the script.

The line of code (StringSplit, MenuItems, TextOptions , `,) saves the parsed words to a set of MenuItems variables (i.e.MenuItems1, MenuItems2, and MenuItems3). There is an additional variable created (MenuItems0) which contains the number of words in the list. Any character may be used to delimit and separate the words (designated by the parameter at the the end of the StringSplit command line), but, since the comma is commonly used in lists, here it acts as the delimiter. However, since the comma is a special character it needs escaping with the backtick character (`,).

The StringSplit output is used to build the menu with the Loop command—the second new command:

  Loop %MenuItems0%
  {
    Item := MenuItems%A_Index%
    Menu, MyMenu, add, %Item%, MenuAction
  }

The Loop command repeats the code between the two curly brackets a designated number of times (%MenuItems0%) or until it encounters the Break command. Since the number of menu items is known (MenuItems0) there is no need to use the Break command. (Placing MenuItems0 between percent signs % inserts the value of MenuItems0 into the Loop command.)

The Loop command includes the built-in variable A_Index which keeps track of the current iteration of the loop. A_Index can be used to recall the variable for each word in the menu (Item := MenuItems%A_Index%). When first executed the loop assigns the variable name MenuItems1 to Item. On the second iteration MenuItems2 is stored to Item—and so on.

In the same Loop, by placing Item between percent signs (%Item%) the variable evaluates to the corresponding variable name (e.g. MenuItems1, MenuItems2,…) for use in the menu (Menu, MyMenu, add, %Item%, MenuAction). The menu is created. Now, all that’s needed is to Show the Menu:

 Menu, MyMenu, Show

As discussed last time, the Label subroutine MenuAction: must exist.

MenuAction:
  SendInput %A_ThisMenuItem%
  Menu, MyMenu, DeleteAll
Return

First, I notice that I was remiss last time in not adding the activating end character to the MenuAction: subroutine (A_EndChar—first introduced in Part 3).

MenuAction:
  SendInput %A_ThisMenuItem%%A_EndChar%
  Menu, MyMenu, DeleteAll
Return

By adding the built-in variable A_EndChar which stores the last activating key pressed, the replacement will restore that ending character.

One more change. Move the Menu, MyMenu, DeleteAll from the Label subroutine MenuAction: to the end of the function TextMenu(). This will ensure that the menu items are deleted after the menu is activated. If the menu deletion line remains in MenuAction:, then, when no selection is made (Escape or mouse click off the menu), the old menu items could appear in a new Hotstring menu. The final function and Label subroutine appear as follows:

TextMenu(TextOptions)
{
  StringSplit, MenuItems, TextOptions , `,
  Loop %MenuItems0%
  {
    Item := MenuItems%A_Index%
    Menu, MyMenu, add, %Item%, MenuAction
  }
  Menu, MyMenu, Show
  Menu, MyMenu, DeleteAll      ;Moved from MenuAction:
}
MenuAction:
  SendInput %A_ThisMenuItem%%A_EndChar%
Return

I just noticed that the exclamation point (!) does not automatically get added to the end of the replacement string when used as the activating character. The exclamation point is the special character for the ALT key in HotKey combinations. Normally, to get a plain vanilla exclamation point, it must be escaped with the backtick (`!)—which is unworkable in this situation. The solution could be to remove the exclamation point’s magical powers by using the SendRaw command. However, when using SendInput, merely adding the term {Raw} before the terminating variable has the same effect.

MenuAction:
  SendInput %A_ThisMenuItem%{Raw}%A_EndChar%
Return

Now, if you want to add a new Hotstring menu with multiple options to your AutoCorrect file, all you need to do is call the new function TextMenu():

::duh::
  TextMenu("what,huh,you're joking")
Return

Next time, we’ll look at other practical ways to use this Hotstring menu function, such as, a menu for inserting currency symbols.

*         *          *

Find Jack’s AutoHotkey e-Books at ComputorEdgeBooks.com.

9 thoughts on “How to Turn AutoHotkey Hotstring AutoCorrect Pop-up Menus into a Function, (Part 5, Beginning Hotstrings)

  1. Hi Jack,

    I really like your blog. This is very useful information. Thanks a lot for sharing. I’ve only made a small adjustment to the “Pop-up Hotstring Menu” that I wanted to share. To make it easier to select the menu entries via keyboard I’m just adding “&” to the Menu entries. That way the entries can be selected by typing the first letter of the respective entry. (If multiple entries have the same first letter typing the letter repeatedly will jump from entry to entry.):

    TextMenu(TextOptions)
    {
    StringSplit, MenuItems, TextOptions , `,
    Loop %MenuItems0%
    {
    Item := MenuItems%A_Index%
    Menu, MyMenu, add, &%Item%, MenuAction
    }
    Menu, MyMenu, Show
    Menu, MyMenu, DeleteAll
    }
    MenuAction:
    StringReplace, ThisMenuItem, A_ThisMenuItem, &
    SendInput %ThisMenuItem%{Raw}%A_EndChar%
    Return

    Dirk

    Like

  2. Hi Jack , thank you for the tutorial about functions. When i try to use your example above , it gave me an error : “Too many parameters passed to function.”.

    I’ve tried everything but still the error props out.

    By the way , i’ve copied your code verbatim as such to ensure no mistake on my part.

    Appreciate much for your reply.

    Warmest regards

    Namza

    Like

    • Hi, Namza,

      I see that I never showed an entire working script, leaving you to piece it together. This one works for me:

      ::agin::
      TextMenu("again,a gin,aging")
      Return

      TextMenu(TextOptions)
      {
      StringSplit, MenuItems, TextOptions , `,
      Loop %MenuItems0%
      {
      Item := MenuItems%A_Index%
      Menu, MyMenu, add, %Item%, MenuAction
      }
      Menu, MyMenu, Show
      Menu, MyMenu, DeleteAll ;Moved from MenuAction:
      }

      MenuAction:
      SendInput %A_ThisMenuItem%{Raw}%A_EndChar%
      Return

      ::duh::
      TextMenu("what,huh,you're joking")

      Return

      My guess is that you forgot the quotation marks in the calling function (TextMenu(again,a gin,aging)). That would generate the error you see. Without the quotes, AutoHotkey interprets the commas as separating parameters in the function.

      Like

      • Hi, Jack, first of all, thank you so much for this tutorial, and I have 3 questions:

        1) First, I have a list of words that are not in this format:

        ::duh::(“what,huh,you’re joking”)

        My list is like:

        duh=what
        duh=huh
        duh=you’re joking

        Is there any way to convert my list to this format, so that I can use your script?

        2) If a world has only one correspondence, could the script to select it automatically? What I mean by that (and sorry, english is not my first language, so something might got lost in translation) is, let’s say you have the following command:

        ::maibe::
        TextMenu(“maybe”)
        Return

        Could the script in this cases automatically select the word “maybe” instead of showing the menu? I’m saying that because I’m planning to use this in a dictionary script as well, and in many cases there’s only 1 translation to that word.

        3) The last question is…when the menu pops up, is there any configuration so that… if I don’t type anything the script automatically select the first word? Like, if I just keep on typing the script automatically select the first word of the list?

        Thank you so much.

        Like

  3. 1. You can write a script which rewrites the file in the new format. You use two loops. The first increments through the first file looking for matching terms (i.e. duh). The second loop builds the string needed for the final output which you save to another file.

    When I get a chance, I’ll write a blog with a sample script.

    2. If you know the text replacement you want, use a simple Hotstring:

    ::maibe::maybe

    No need for the menu or alternatives. This gives you an instant replacement.

    3. The problem with not responding to the menu is that by default it does nothing. You can probably find a method for doing what you want, but it may be too clever by half. If you expect that you may want to use the first option in most cases, then make it a simple Hotstring as shown in item 2. You can always use an alternative method such as the SynonymLookup.ahk script to replace a word.

    Like

  4. Quick question: Is there any way to improve the script in order to, once the menu appears, to select an option using a given key combination? For instance: If I typed “alt+1” he would automatically select the option “again”, if I typed “alt+2” it would select the option “a gin” and so on so forth until alt+0?

    Like

    • Yes, you can add a hot character to each menu item by adding an ampersand and number to the each. Then pressing the number will select the item:

      Menu, MyMenu, add, &%A_Index% %Item%, MenuAction

      You need to remove the ampersand and number from the menu item before replacing the word:

      MenuAction:
      SendInput, % Substr(A_ThisMenuItem,4) . A_EndChar
      Return

      Here’s the new script:

      ::agin::
      TextMenu(“again,a gin,aging”)
      Return

      TextMenu(TextOptions)
      {
      StringSplit, MenuItems, TextOptions , `,
      Loop %MenuItems0%
      {
      Item := MenuItems%A_Index%
      Menu, MyMenu, add, &%A_Index% %Item%, MenuAction
      }
      Menu, MyMenu, Show
      Menu, MyMenu, DeleteAll ;Moved from MenuAction:
      }

      MenuAction:
      SendInput, % Substr(A_ThisMenuItem,4) . A_EndChar
      Return

      ::duh::
      TextMenu(“what,huh,you’re joking”)
      Return

      Like

Leave a Reply to Anonymous Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s