Digging Deeper Into AutoHotkey (Part II)

Amazon.com: Jack's New Beginner's Guide to AutoHotkey: Absolutely ...




Table of Contens:



Chapter Nineteen: Make Your Own Start Pop-up for Windows

“Add a new Start window to any version of Windows including Windows 8!”
Missing the Start Menu in Windows 8 or want to do more with any version of Windows? Now you can create your own tailored Start window for any of your Windows computers with AutoHotkey.
As I scour the Web looking for new AutoHotkey ideas that will help Windows users, I've notice that many of the scripts are quite long and complex. There is nothing inherently wrong with that, but I look for the simplest ways to add utility to Windows computers. Keeping it simple is also the key to adding the most functionality to your computer while making it easy to learn. Sometimes it's a matter of finding the right AutoHotkey command. Other times it requires rethinking the entire problem and coming up with a new solution.
Many of the techniques offered here can be combined with other simple techniques to create entirely new AutoHotkey features. For example, this time we will create a pop-up window which launches programs—just like the Start Menu or Taskbar. Yet, that same pop-up could be used to select special e-mail addresses for adding to documents or Web forms. Or, as is the case of the example used in this chapter, grouping together similar programs for easy launching. This limits the crowding on the Start Menu or Taskbar while giving quick access to the applications. (In the case of Windows 8, it replaces the old Start Menu.) In fact, it could be used as a selection list for virtually any set of Windows functions. It is merely a matter of understanding the relatively simple code and substituting the commands which make it do your bidding. Often, as you learn new AutoHotkey techniques, you'll find you can improve on your previous scripts.

Create Your Own Start Menu

It's easy for the Windows Start Menu or Taskbar to become overloaded with program launch shortcuts. (Or as is the case with Windows 8, sometimes the Start Menu is missing completely.) Wouldn't it be easier if you could open special launch windows for specific types of work? You can do that with AutoHotkey (see Figure 1). Three commonly used Web browsers (Google Chrome, Mozilla Firefox, and Internet Explorer) are grouped in one pop-up window for launching your choice.Figure 1. Google Chrome, Mozilla Firefox, and Internet Explorer are grouped together in a pop-up window for launching with a click of the mouse. Dropbox has been thrown in for good measure.
When the hotkeys CTRL++L (L for launch) are pressed simultaneously, this AutoHotkey routine uses the GUI command (Graphic User Interface) which creates the pop-up. The icons and text are added to the pop-up for each program. A label is created which accesses the subroutine launching the application when the associated icon or text is clicked with the left mouse button. The launch pop-up then closes.

Finding the Program Path and Using the Program Icon

To write this script there are a couple of pieces of information needed for each application. The first is the location of the application executable file. This can be found by right-clicking on the program or program shortcut icon and selecting properties from the list. The path to the program file will be listed in the Target field (see Figure 2). This path will need to be copied for inclusion in each launching subroutine.Figure 2. The Properties window for a program or program shortcut will display the path to the executable file for the application. The icons included with a program can be found by clicking the "Change Icon..." button.
If you click the "Change Icon..." button, a window will open displaying the icons included within that program (see Figure 3). The path to the icons will be the same or similar to the program path, although it may contain wild cards such as %ProgramFiles%. If this path does not work in the AutoHotkey routine for displaying the icon, the full path from the Properties window will.Figure 3. The icons associated with an application are found by clicking the "Change Icon..." button in the Properties window. When used in an AutoHotkey routine the icons are designated based upon their location in this window as shown (Icon1, Icon2, Icon3, etc).
When deciding which icon to use, the variable Icon? is used. (The "?" represents the number of the icon which corresponds to its location in the program file.)

The LaunchWindow.ahk Routine

The AutoHotkey code in Figure 4 sets up the hotkeys, creates the pop-up window and includes the subroutines for launching each application. It may look like a long routine but it consists primarily of very similar redundant code. The sections at the top (A, B, C, and D) set up the icon and text buttons in the pop-up. The corresponding code sections at the bottom (also A, B, C, and D) are the associated subroutines which launch the respective programs. If you eliminated all sections except the A's, the AutoHotkey script would run just as well except Google Chrome would be the only listing. By the same token, you could add many more launch applications by merely adding more two-part sections (E => E, F => F, G => G, etc).Figure 4. The AutoHotkey code for the LaunchWindow.ahk script. Each item in the list (Figure 1) is represented by pairs of code (A => A, B => B, C => C, and D => D).
As you can see there are only four lines of code which are not associated (not outlined in red) with specific applications.

The Simplest Launch Script

To start we will only add and launch one application, Google Chrome. Once this routine is understood it's easy to add the others. To include the entire routine for running Google Chrome from a pop-up, copy-and-paste the following code into a new AHK file or add it to a current file:
^#L::
Gui, Font, s20 cBlue, Arial
Gui, Add, Picture, w30 h-1 section gLaunchGoogle Icon1
     , C:\Users\%A_UserName%\AppData\Local\Google\Chrome\Application\chrome.exe
Gui, Add, Text, ys gLaunchGoogle, Google Chrome
Gui, Show, , Launch Program
Return

LaunchGoogle:
Run C:\Users\%A_UserName%\AppData\Local\Google\Chrome\Application\chrome.exe
WinClose
return

The first line of code (^#L::) sets up the hotkey combination CTRL++L. The double colon indicates the end of the hotkey combination and the beginning of the routine code.
The second line of code (Gui, Font, s20 cBlue, Arial) uses the GUI command to begin the building of the pop-up window. In this case the Font parameter sets the size (s20), the color (cBlue), and the font family (Arial) to be used in the pop-up. These values are used for text throughout the entire pop-up unless a new Gui, Font command is issued. If you want to adjust the pop-up text appearance, this is where you do it.
The third line of code (Gui, Add, Picture) uses the GUI, ADD command to place a graphic in the pop-up with the PICTURE parameter. The "W30" limits the width of the graphic to 30 pixels while the "H-1" forces the image to use the height necessary to maintain the proper aspect ratio. (Using the same width/height setting for all program icons stops the icons from being distorted while ensuring that all the icons are the same size.) The option "section" creates a section on the pop-up for placing the icon and text. Without "section" there can be alignment problems.
The label "gLaunchGoogle" names the subroutine which will be called if the icon is clicked. "Icon1" tells the AutoHotkey to use the first icon found in the program file which appears on the next line of code.
Tip: All these options (w30 h-1 section gLaunchGoogle Icon1) can be in any order as long as there is no comma allowed or required between them. However, for consistency you may want to use the same order within your AutoHotkey routines.
Breaking Lines of Code: Note that the fourth line (the path to the program file which contains the icons—copied from the Properties window) is preceded by a comma. This is merely the required comma which is part of the GUI command. Originally, this line was a continuous part of the previously line. However, for the purposes of readability (and displaying in this chapter) it was convenient to break long lines of code. When a line is broken before a comma that then becomes the first character on the new line (as shown), then it is considered to be part of the preceding line.
The fifth line of code (Gui, Add, Text, ys gLaunchGoogle, Google Chrome) uses the same ADD parameter to include TEXT in the pop-up. The "ys" option tells AutoHotkey to start a new column in the current section. Otherwise, the text would appear on the next line. The same "gLaunchGoogle" label makes the text an active link for running the subroutine when clicked. "Google Chrome" is merely the displayed text (now clickable) and is subject to the Font parameters.
The sixth line of code (Gui, Show, , Launch Program) uses the Show parameter to activate the pop-up on the screen. The title of the window is "Launch Program." Note the extra comma after Show. This comma is required even though (or because) there are no options added. Options may be used to set the size of the pop-up and its location on the screen.
The "return" in the seventh line of code marks the end of the initial portion of the script. The pop-up will now wait until either the icon or text is clicked, or the window is closed.
If the icon or text is clicked, then the "gLaunchGoogle:" subroutine shown in the eighth line of code is activated. The subroutine uses the Run command to launch Google Chrome. Notice that the path uses %A_UserName% rather than the actual user name found in the program's Properties window. This is to make the script more universal for use on other Windows computers with other users. However, programs do not always have the same path (or same icons available) in all versions of Windows. AutoHotkey scripts may need tailoring for specific computers and operating system versions (32-bit versus 64-bit). Checking the program properties window will provide the correct path for a specific computer.
After launching the application, the ninth line of code (WinClose) closes the pop-up with the "return" on the next line marking the end of the routine.

Adding More Application Launch Items

Once the first routine is up and running, it's easy to add more applications. It's a matter of copying and pasting any of the Gui, Add, Picture/Text sets for each, as well as, the associated launch subroutine. Then modify the code for the new launch item. As you add more, you will insert the paths for both the program file and the icons at the appropriate location. A new subroutine and subroutine label will be needed for each new launch link. Otherwise, the routine remains the same. When setting up the pop-up a new "section" option at the first Add will maintain the proper alignment.
The entire code for the pop-up displayed in Figure 1 is as follows:
^#L::

Gui, Font, s20 cBlue, Arial

Gui, Add, Picture, w30 h-1 section gLaunchGoogle Icon1
     , C:\Users\%A_UserName%\AppData\Local\Google\Chrome\Application\chrome.exe
Gui, Add, Text, ys gLaunchGoogle, Google Chrome

Gui, Add, Picture, w30 h-1 section xs gLaunchFirefox Icon3
     , %ProgramFiles%\Mozilla Firefox\firefox.exe
Gui, Add, Text, ys gLaunchFirefox, Firefox

Gui, Add, Picture, w30 h-1 section xs gLaunchExplorer Icon3
, C:\Program Files\Internet Explorer\iexplore.exe
Gui, Add, Text, ys gLaunchExplorer, Internet Explorer

Gui, Add, Picture, w30 h-1 section xs gLaunchDropBox Icon6
      , C:\Users\%A_UserName%\AppData\Roaming\Dropbox\bin\Dropbox.exe
Gui, Add, Text, ys gLaunchDropBox, DropBox

Gui, Show, , Launch Program
Return

LaunchGoogle:
Run C:\Users\%A_UserName%\AppData\Local\Google\Chrome\Application\chrome.exe
WinClose
return

LaunchFirefox:
Run "C:\Program Files\Mozilla Firefox\firefox.exe" www.facebook.com
WinClose
return

LaunchExplorer:
Run "C:\Program Files\Internet Explorer\iexplore.exe"
WinClose
return

LaunchDropBox:
Run C:\Users\%A_UserName%\Dropbox
WinClose
return

If you copy-and-paste this code into an AutoHotkey script and run it on your computer, it may or may not look the same. You might need to modify the script to reflect where you have installed the programs (if installed) and the find the proper icon by checking the program Properties window.

Using Buttons in the GUI

One difference between this pop-up launch window and the Windows Start Menu or Taskbar is there is no icon or background change when you hover over a button with the cursor. If this is something you want then you can replace Text with the Button control (see Figure 5). The Button control will change color when the mouse hovers over the button. However, the Font control doesn't affect the color of the text in the button which will remain black.Figure 5. When the cursor hovers over an AutoHotkey Button control, the background changes color.
To modify the pop-up to use buttons rather than text copy-and-paste the following code:
Gui, Add, Picture, w30 h-1 section gLaunchGoogle Icon1
     , C:\Users\%A_UserName%\AppData\Local\Google\Chrome\Application\chrome.exe
Gui, Add, Button, ys gLaunchGoogle, Google

Gui, Add, Picture, w30 h-1 section xs gLaunchFirefox Icon3
     , %ProgramFiles%\Mozilla Firefox\firefox.exe
Gui, Add, Button, ys gLaunchFirefox, Firefox

Gui, Add, Picture, w30 h-1 section xs gLaunchExplorer Icon3
     , C:\Program Files\Internet Explorer\iexplore.exe
Gui, Add, Button, ys gLaunchExplorer, Internet Explorer

You could also just replace the word "Text" with "Button" in the appropriate lines. Note that it was necessary to adjust the Font size to make the buttons align properly.
There is no limit on the number of ways these routines can be used. If you add a Web URL to the Run Web browser command you can create a special launch window to open your favorite Web sites. Different hotkey combinations can be set up to open new launch pop-ups for specific groups' applications. You are only limited by your imagination.

Chapter Twenty: A Free QuickLinks Windows App from Jack

“All Your favorite programs, Web sites and documents are only a click away.”
Jack offers a simple, easy to use, free app which can replace both the Windows Taskbar and Start Menu links (even in Windows 8). Guaranteed safe. Jack should know. He wrote it.
QuickLinks is an AutoHotkey app I wrote to demonstrate the power of the AutoHotkey Menu command. This chapter was originally written for people with no knowledge of AutoHotkey and explains how QuickLinks works. Although there is very little about AutoHotkey scripting in this section, I felt it was worthwhile to include it for its descriptive nature. You may want to skim this chapter first or go directly to Chapter Twenty-one for the inner workings of the QuickLinks AutoHotkey app.
*                    *                    *

Ever since the introduction of Windows Vista there have been complaints about the loss of the old Windows XP file tree in the Start Menu. When opening the default Start Menu in Windows Vista and Windows 7, "All Programs" opens folders in place without displaying the cascading folder/file tree structure. In Windows Vista you can return to the Classic XP style Start Menu through Properties, but that capability was lost in Windows 7. Furthermore, there is no Start Menu at all on the Taskbar in Windows 8.
I like the Favorite Links section of the navigation pane of Windows File Explorer which was first introduced with Windows Vista. (Note: The name Windows Explorer was changed to File Explorer in Windows 8. To save (or cause) confusion I use the term Windows File Explorer which opens with +E in all versions of Windows.) I slip my key editorial folders in and out of Favorite Links every week. However, it is fairly limited in space and only available when Windows File Explorer is open. What if there was an easy to access dropdown menu similar to the Windows' right-click menu which is always available, easy to use, organized by category, and tailored to open those folders, programs, and Web sites used most. ComputorEdge now has a free AutoHotkey app which does just that (see Figure 1). This app might replace some of the inadequacies in the Windows Start Menu and the confusion created by loading the Taskbar with numerous (uncategorized) quick launch buttons.Figure 1. QuickLinks is a Windows dropdown menu which can be tailored with categories of programs, Web pages, folders, and documents. Activated with WIN+Z or ALT+Comma (for left-handed people).
I'm sure that there are many such apps freely available for Windows computers, but this one was built for its simplicity, plus ease of set up and use. There is no installation program and it runs in all versions of Windows—including Windows 8 Desktop. Simply download it, extract it from the ZIP file, and double-click on the filename to load. More importantly since I wrote it, I know that there is nothing in it that will hurt your computer system.
First, here's how QuickLinks.exe works. (The free download information is found at the end of this column.)

How the QuickLinks App Works

When QuickLinks.exe is run for the first time on a Windows computer (double-click the extracted QuickLinks.exe file name), it looks for a folder named QuickLinks in the individual user folder. If the QuickLinks folder is not found, the app automatically creates it. An icon composed of a green square with an H inside (for AutoHotkey, shown at left) will appear in the System Tray on the Taskbar. If you hover over the icon, you will see the name of the app. If you right-click on the System Tray icon, a menu with the options Suspend Hotkeys, Pause Script, and Exit will pop up. These options can be used to control the running of the AutoHotkey app.
To activate QuickLinks, hold down WIN+Z (+Z) or ALT+Comma (for left-handed mouse users). The QuickLinks menu will appear at the location of the mouse cursor (see Figure 2 top). Initially, there are no categories (folders) shown in the QuickLinks folder. They must be added by the user. Click on Edit QuickLinks and the QuickLinks folder will open in Windows File Explorer (see Figure 2 bottom).Figure 2. The first QuickLinks menu (top) has no categories. Click Edit QuickLinks to open the QuickLinks folder (bottom).
Note for Windows 8 Users: The keyboard combination +Z is used in the Windows 8 Modern Start interface to open the Toolbar within apps and the Start screen. This hotkey for the Modern interface will be disabled if you run QuickLinks.exe. However, a right-click of the mouse has the exact same effect as +Z in the Modern interface and has the added advantage of selecting tiles at the same time. +Z and ALT+Comma (left hand mouse) were selected for their proximity and ease of use with a mouse—easy two-finger combination. If you like QuickLinks, but want to change the hotkeys, then you can recompile the source code (Chapter Twenty-one) with different keyboard combinations. When activated, the QuickLinks menu always (and only) appears on the Desktop, even if activated when the Windows 8 Modern Start Screen is open. (There is another version of QuickLinks, QuickLinksTray.exe, which adds the menus to the right-click menu of the System Tray icon, Chapter Twenty-two.

Adding Categories to QuickLinks

In its current form, QuickLinks has two levels of functionality. The first is a collection of categories used to group the links based upon the user's preferences. Each category is a folder stored in the QuickLinks folder. To define a new category, merely create and label a folder with the desired category name within the QuickLinks folder. (While the QuickLinks folder is the active window in Windows File Explorer, right-click and select New => Folder to create a new folder—or press ALT, F, W, F in order. In Windows 7 and Windows 8, you can press the keyboard shortcut CTRL+SHIFT+N simultaneously to create a new folder.)
Note: While anything (folders, shortcuts, documents, etc.) can be placed in the QuickLinks folder, only folders, each of which represent a category, will be read by the QuickLinks app. The links themselves must be located within the individual category folders.
The second level of functionality in QuickLinks consists of the shortcuts located within the category folders. These shortcuts can be for anything acceptable to Windows File Explorer, including programs, Web site URLs, documents, folders, etc. Folders linked within a category folder will merely open the folder. For simplicity, this version of QuickLinks does not support multiple layers of categories (folders with folders of links—and so on).
After adding a new category folder to the QuickLinks folder, select Reload QuickLinks from the menu (+Z). This updates the menu with any changes. The next time the QuickLinks menu is activated (+Z), the new category will appear, as shown in Figure 3 (top). Since there are currently no shortcuts in the new category, the menu item is an active link for opening the category folder. One click and that folder opens in Windows File Explorer (see Figure 3; bottom).Figure 3. After reloading the QuickLinks menu, the new folder (Web Browser) appears (top). Click on an empty category and the folder opens in Windows File Explorer (bottom).

Adding Shortcuts to QuickLinks Categories

While it is possible to add any type of Windows file to a category folder, it's recommended that only shortcuts be used. QuickLinks is designed merely as quick access to anything the user desires, not a repository for programs, files, folders and other data of a more permanent nature. If programs, documents, and such are placed in a category folder, they will open in the same manner as a link. However, once it is no longer relevant for QuickLinks uses, the user will need to be careful to move it to a more appropriate place. Otherwise it will continue to appear in the QuickLinks menu until it is moved or deleted. If you delete it, the program or file is sent to the Recycle Bin. Yet, when an obsolete shortcut is deleted, the original folder, program, or file is not affected.
There are a number of ways to add shortcuts to the category folders. Some of the easiest are by dragging an icon from one location to the category folder opened in Windows File Explorer. The hotkey for creating a shortcut for a folder or file is to hold down CTRL+SHIFT while dragging the icon to the new location. However, this can be a bit tricky if you forget to use the key combination or use the wrong combination (CTRL for move). I prefer to use a systematic approach creating shortcuts which doesn't run the risk of disappearing a program file or folder into some unknown folder because the mouse or a key slipped. Plus, the following technique works equally as well for Web site shortcuts.
The first step is to find the program, file, or folder, right-click on the file name and select Create Shortcut. Once there is a shortcut available in Windows File Explorer it can be either copied and pasted or dragged directly to the category folder. If the shortcut is needed at its current location, copy it rather than dragging. If this is the case, then you're done and reloading QuickLinks will update the +Z menu.
If you are looking at replicating a shortcut on the Start Menu, the Taskbar, or the Desktop, then right-click and select Properties. The Properties window will open, as shown in Figure 4. (If the shortcut is on the Taskbar in Windows 7 or Windows 8, hold down the SHIFT key, right-click and select Properties.) Select the Target path (highlight by clicking at the beginning of the path, holding the left mouse button, and dragging to the end) and copy it (CTRL+C or right-click => Copy). This is the program or file path used when creating a new shortcut in the QuickLinks category folder.Figure 4. The Shortcut tab in the Properties window of any shortcut shows the Target path needed to create a new shortcut for a program or file. Copy this path before returning to the QuickLinks category folder in Windows File Explorer.
Note: When adding Web pages to QuickLinks open the target Web page in any browser and copy (CTRL+C) the selected (highlighted) URL located in the browser's navigation field. The following techniques for creating shortcuts work equally as well for Web page shortcuts when substituting the URL for the target path.
To create the new shortcut, return to the QuickLinks category folder in Windows File Explorer, right-click in the main file area and select New => Shortcut from the menu (see Figure 5).Figure 5. Right-click in the main file area of the QuickLinks category folder in Windows File Explorer, then select New => Shortcut from the menu. The Create Shortcut dialog will open.
An alternative method for opening the same Create Shortcut dialog is to press the keys ALT, F, W, S successively.
Paste the copied file path (URL for Web pages) into the "Type the location of the item:" field as shown in Figure 6, then click Next.Figure 6. Paste (CTRL+V) or type the target file path or Web page URL in the location field.
In the next window, type the name you want assigned to the new shortcut (see Figure 7). This is the name which will appear in the QuickLinks menu. Clicking Finish creates the shortcut.Figure 7. Type the name you want assigned to the new shortcut, then click Finish.
The QuickLinks menu does not update until the script is reloaded. Activate the menu (+Z), then select Reload QuickLinks, as shown in Figure 8 (left). On the next +Z, hovering over the recently changed category folder displays the new addition(s) (see Figure 8; right).Figure 8. Reloading the QuickLinks menu (left) causes the empty category folder link to change to a menu (right).
Continue this process of adding category folders and shortcuts within each folder, until you're satisfied with your QuickLinks pop-up menu. The top level QuickLinks folder can always be opened with Edit QuickLinks option. Figure 9 shows my personal QuickLinks folder in its current state. Each category folder has at least one shortcut within it.Figure 9. The QuickLinks folder open is Windows File Explorer showing the category folders.
When the QuickLinks menu is activated on my computer the links display as shown in Figure 10 (two examples shown). The QuickLinks Help option (circled in red in the bottom example) takes the user to this Web column—since it is the most extensive (and only) how-to on the topic.Figure 10. Two examples of Jack's personal QuickLinks menu.
You'll note that the link names in the category show the extensions (".url" for Web links, ".lnk" for regular shortcuts). While I could have written code to avoid displaying these extensions, for the sake of simplicity and the function of the AutoHotkey Menu tool, I left it as is.

Downloading and Installing QuickLinks

You can download it free directly from the ComputorEdge AutoHotkey Dropbox. You will need to save the ZIP file to your computer. (You will probably get a warning about the file coming from an unknown source. Don't worry, it's safe.) The program (EXE) file is compressed in a ZIP file and needs to be extracted. Open the ZIP folder (double click on QuickLinks.zip) to view the compressed EXE file. To extract the QuickLinks.exe file from the ZIP folder either click "Extract all files" at the top or right-click and select "Extract All..." from the menu (see Figure 11). Save to a new or chosen folder.Figure 11. To extract the QuickLinks.exe file from the ZIP folder either click "Extract all files" at the top or right-click and select "Extract All..." from the menu.
Once QuickLinks.exe is extracted, in the new location double-click or right-click => Open to run the QuickLinks.exe file. The first time you run the program, since your computer doesn't know me, you will most likely get another warning from your security software. It's safe to run (as long as you get the app directly from my link and not some unknown source).
If you would like the QuickLinks app to run every time you use your computer, Run "shell:startup" to open the Startup folder and place the QuickLinks.exe file or a shortcut inside.
If you do give it a try and run into problems, please leave a comment or send an e-mail to ComputorEdge Editor. In any case, let me know what you think. QuickLinks is a sample app of one of the many things that AutoHotkey can do. To see QuickLinks.exe's inner workings, go to Chapter Twenty-one. It is a fairly short, easily modified script. If you find any bugs (always possible in new apps), please let me know right away.

Chapter Twenty-one: How to Make a Pop-up Menu for Programs, Web Sites and Files

“Reading files from folders to build a menu structure with AutoHotkey.”
Jack discusses how his new app QuickLinks was written with a short AutoHotkey script.
I was digging around the Web looking for another interesting AutoHotkey application when I came across the menu structure shown in Figure 1. I was intrigued because I had looked at all of the GUI (Graphic User Interface) objects, yet nothing resembled this. The pop-up has the look of a standard right-click menu, but is obviously tailored for a specific AutoHotkey task. The person posting the image raved about how it was helping his company. Unfortunately, as is so often true on the Web, the script writer didn't even give a hint as to how it was done.Figure 1. Sample AutoHotkey menu structure.
Armed with only the image and the knowledge that it could be done, I started reviewing (again) the AutoHotkey commands. It didn't take very long to find the two commands crucial to this type of menu feature. (The "commands in large font are the most commonly used.") I soon narrowed down the application's tools to the MENU command and the LOOP command—or more specifically the LOOP (files & folders) command. I had previously noticed the MENU command and intended to do further investigation, but had let it slip to the back burner. Seeing an app which navigated through files had piqued my interest. While a pop-up menu with links might be convenient for any Windows user, this type of app would most help Windows 8 users who no longer have a Start Menu. I went to work on the QuickLinks app introduced as a free download in the previous chapter. The MENU and LOOP commands are the backbone of this simple app (see Figure 2).Figure 2. AutoHotkey QuickLinks.ahk menu.
While the QuickLinks.ahk script may look short and simple, it is not a beginning level exercise. As well as using the MENU command, the code employs a LOOP within a LOOP, a few conditional IFs, and a couple of programming tricks not well known to the novice. The entire script code for QuickLinks.ahk is shown in Figure 3. Key portions of the script are highlighted with red text.Figure 3. AutoHotkey QuickLinks.ahk script code.
There are three primary steps for building the QuickLinks menu. The first (1) is the creation of the QuickLinks folder (if it doesn't already exist). Second (2), the script loops through the QuickLinks folder using only the category folders within it to create menu items for the top level. Third (3), for each folder found at the top level the script loops through the folder items creating links and adding each to a submenu. If nothing is found in the subfolder, the category menu item action defaults to opening that folder.
Next in the script, the standard links are added at the bottom of the category menu followed by the labels (routines) containing the action executed when an item is selected from the pop-up menu. Lastly, the keyboard combinations are assigned; one for right-handed people (+Z, mouse in right hand) and one for left-handed people (ALT+Comma, mouse in left hand). For those who wish to copy-and-paste the code, the complete script (without standard boilerplate from a new AutoHotkey, AHK , file) can be found near the end of this chapter.

How the QuickLinks Script Works

Since QuickLinks uses a standard folder (QuickLinks) found in the user folder, the script employs the IfNotExist command to look for it. If the folder does not exist, it is created with the FileCreateDir command.
IfNotExist, C:\Users\%A_UserName%\QuickLinks\
    FileCreateDir, C:\Users\%A_UserName%\QuickLinks\

The script immediately starts the process of scanning the QuickLinks folder with the LOOP (files and folders) command. If there is nothing in the folder, then it skips to the end where the standard menu items are added. At this time, when activate with one of the hotkey combinations (+Z or ALT+Comma), the QuickLinks menu will appear as shown in the image at the left.
LOOP is a powerful command for reading Windows file and folder names in Windows File Explorer. It automatically increments through every item in a folder providing data on each. The basic format of the command is simply Loop followed by the path of the target files. Standard wildcards such as "*" and "?" can be used in the folder/file path. The first LOOP command is as follows:
Loop, C:\Users\%A_UserName%\QuickLinks\*.*, 2 , 0
{
   Menu, QuickLinks, Add, %A_LoopFileName%, MenuHandler
}

Note that the variable A_UserName is used in the loop path. This makes the app more universal and tailored for each user. The "*.*" is the wildcard for checking every folder/file. The parameter 2 found after the path and comma tells AutoHotkey to only retrieve folders. The default for this parameter is 0 which will retrieve only files. To retrieve both files and folders the value must be set to 1. The last parameter shown (set to 0) tells AutoHotkey not to look beyond this current folder. If set to 1, the LOOP command will continue recursively looking into all subfolders.
The LOOP command uses the standard curly brackets ({}) to enclose the code executed each time the loop is incremented to the next file or folder. In this case the primary function of the loop is to create a menu item for each category folder. The first step in creating a menu is to ADD the menu items. In this case, a menu item named with the folder name (the value of A_LoopFileName, the variable which stores the name of the current folder or file) is added to a menu named QuickLinks. If this menu item, as it is now, is left on the final menu, the label (subroutine) MenuHandler, found later in the script, would execute when clicked on the activated menu. In this script, each category menu item is changed later depending upon whether there are files or folders within this folder.
I see now that I could have saved a couple of lines of code which occur later in the script by immediately assigning the FolderHandler label rather than MenuHandler label—which is only needed in the submenus. It's not unusual to start down one path, then later realize there is a simpler way to reach your goal. A person doesn't always see all the implications of his approach until he actually gets there. Rather than rework it now, I'll use it as a learning point. More on that later.
Left alone, this loop would create a menu of the folders located in the QuickLinks folder. The next step is to add a second loop within the first loop (inside the curly brackets of the first loop after the MENU statement) so that each time a folder is found it immediately looks for files within that folder:
   MainMenu := A_LoopFileName

   Loop, %A_LoopFileFullPath%\*.*, 1 , 0
   {
     if A_LoopFileAttrib contains H,R,S  ;Skip any file that is 
       continue                                ; H, R, or S.

        Menu, %MainMenu%, Add, %A_LoopFileName%, MenuHandler

   }

The variable MainMenu is created to save the name of the category item. Once within the second loop A_LoopFileName will take on the value of each item located in that folder as it increments through them. MainMenu is used as the name for the submenu when the items are added.
This second LOOP command uses the parameter 1 to retrieve all folders and files. Unlike the listing in Windows File Explorer, the AutoHotkey LOOP command, when used with files, retrieves all files regardless of their attributes—including hidden and system files. To ensure that none of these types of files are included in the new menu, a conditional IF (if A_LoopFileAttrib contains H,R,S) is included to skip those files by executing the CONTINUE command which skips the loop to the next file/folder. The H (Hidden), R (Read Only), and S (System) parameters must be separated by commas with no spaces between them.
The MENU command is almost identical to the command in the first loop except that MainMenu (the category folder name) is used as the menu name rather than QuickLinks. Once exiting the loop, all of the relevant files have been added to the menu MainMenu, but they need to be added to QuickLinks as a submenu.
      Menu, QuickLinks, Add, %MainMenu%, :%MainMenu%

There are limited numbers of options for types of items when adding to an AutoHotkey menu. The first is a label (to run a subroutine) as shown in the first couple of examples. The second is to add a menu as a submenu to an already existing menu item. This is done by place a colon in front of the name of the higher level menu item name (:%MainMenu%). A menu item can either execute a label or contain a submenu—not both. The line of code above replaces the original menu item (which includes a label) and changes it to the prompt for a submenu. An error in this script can occurs when no files are found inside the category folder while attempting to add a submenu to the top level menu.
There are a number of different ways to deal with this situation. One is to add an item to the submenu before entering the second loop—possibly an item which would open the category folder. Another option is to add a toggle which would only trigger if an item is found in the category folder. I choose the second option only because I thought of it first. If I were doing it now, I would probably go with the first option and add a link which opens the category folder for editing to every submenu. (Maybe I'll do this in a future version.)
Adding a toggle or switch is a common programming trick when the program needs to know if a loop or conditional is entered. To implement my toggle solution I created the variable CountLoop and set the value to 0:
   CountLoop := 0

   Loop, ...
      {
       ...
       ...
       ...
      CountLoop := 1
      }

   If (CountLoop = 1)
    {
      Menu, QuickLinks, Add, %MainMenu%, :%MainMenu%
    }
   Else
    {
     Menu, QuickLinks, Add, %MainMenu%, FolderHandler
    }

If there is a file inside the category folder, the loop will run and set the value of CountLoop to 1. On exiting the loop, IF CountLoop equals 1, the submenu is added to QuickLinks. If not (Else), the category menu is set to the label FolderHandler which will cause the folder to open when clicked.
This is where I could have completely eliminated the ELSE portion of the conditional by setting the label on the first MENU statement to FolderHandler. Then that MENU statement would only need to be changed if files are found in the category folder, thus turning it into a submenu. Of course, I could completely eliminate the need for the CountLoop toggle and conditional by using the second option I mentioned above and creating the item for editing the category folder in every submenu prior to entering the second loop.
The third possibility for the MENU ADD command is to add a separator bar by leaving the label/submenu space blank:
Menu, QuickLinks, Add ;Add a separator bar

The last three lines of code before the RETURN and subroutines add the menu items which appear on every QuickLinks menu:
 Menu, QuickLinks, Add, Edit QuickLinks, QuickLinksHandler
 Menu, QuickLinks, Add, Reload QuickLinks, ReloadHandler
 Menu, QuickLinks, Add, QuickLinks Help, HelpHandler

The labels (each followed by a colon) contain the actions (subroutines) initiated when the menu items are selected. Note that they all use the RUN command with the exception of the one RELOAD command.
MenuHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenu%\%A_ThisMenuItem%
return

QuickLinksHandler:
run, C:\Users\%A_UserName%\QuickLinks\
Return

FolderHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenuItem%
Return

ReloadHandler:
Reload
Return

HelpHandler:
Run, http://webserver.computoredge.com/online.mvc?zone=SD
            &issue=3103&article=vista&src=qcklnks
Return

The MENU command only provides three variables for returning data when a menu item is selected: A_ThisMenu (name of the menu selected), A_ThisMenuItem (name of the menu item selected), and A_ThisMenuItemPos (numeric position of the menu item in the menu). The first two are used in the subroutines to build the paths needed to launch the shortcuts. Since these values are based upon the actual folder and file names, the commands for opening the shortcuts can be built on the fly using the same label.
The purpose of the RELOAD subroutine is to allow the update of the menu after changes are made in the folder.
The last two lines of the script set up the hotkey combinations for showing the menu:
#z::Menu, QuickLinks, Show  ;right-hand mouse
!,::Menu, QuickLinks, Show  ;left-hand mouse

The menu will not display until the MENU SHOW command is executed. The hotkeys were selected based upon their proximity to the right and left hand, depending upon which hand the mouse is operated with.
The entire script is found here for copying and pasting:
IfNotExist, C:\Users\%A_UserName%\QuickLinks\
    FileCreateDir, C:\Users\%A_UserName%\QuickLinks\

Loop, C:\Users\%A_UserName%\QuickLinks\*.*, 2 , 0  
{
   Menu, QuickLinks, Add, %A_LoopFileName%, MenuHandler

   MainMenu := A_LoopFileName
   CountLoop := 0
   Loop, %A_LoopFileFullPath%\*.*, 1 , 0
   {
     if A_LoopFileAttrib contains H,R,S  ;Skip any file that is 
       continue                           ; H, R, or S (System).

        Menu, %MainMenu%, Add, %A_LoopFileName%, MenuHandler
        CountLoop := 1
   }
   If (CountLoop = 1)
    {
      Menu, QuickLinks, Add, %MainMenu%, :%MainMenu%
    }
   Else
    {
     Menu, QuickLinks, Add, %MainMenu%, FolderHandler
    }
}

 Menu, QuickLinks, Add ;Add a separator bar

 Menu, QuickLinks, Add, Edit QuickLinks, QuickLinksHandler
 Menu, QuickLinks, Add, Reload QuickLinks, ReloadHandler
 Menu, QuickLinks, Add, QuickLinks Help, HelpHandler

Return

MenuHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenu%\%A_ThisMenuItem%
return

QuickLinksHandler:
run, C:\Users\%A_UserName%\QuickLinks\
Return

FolderHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenuItem%
Return

ReloadHandler:
Reload
Return

HelpHandler:
Run, http://webserver.computoredge.com/online.mvc?zone=SD
            &issue=3103&article=vista&src=qcklnks
Return

#z::Menu, QuickLinks, Show
!,::Menu, QuickLinks, Show

QuickLinks Notes

One question I asked myself was, "Why not subcategories?" The current QuickLinks app only works at two levels: the categories level and the links within the categories.
To increase the capabilities of the QuickLinks app means adding more complication to the script. The MENU command only operates at two levels: menu and menu items. When a selection is made only the name of the menu, the name of the menu item, and menu position are available. If the menu is to drill down further than those two levels, then unless special provisions are made for tracking path data, the intermediate level path data will be lost. The additional levels either need to be hard coded into the script with specific path data or global variables (arrays) must be created with unique (recall-able) names.
This tracking of path data can be done a number of different ways, but to maintain flexibility for the user some type of array structure based upon the menu position variable may be the best bet. That would require that the index (concatenated string of successive levels) would need to appear in some form in the menu item name. This is the only way that I can see to easily tie the menu items to the array variable. An assignable variable as part of the MENU ADD command would have resolved this issue, but that does not appear to exist in AutoHotkey.
As another alternative, storing more complete path information at each successive level is conceivable, but this would require displaying that path information in the menu itself. At each level the menu drills down, the displayed menu name gets longer.
Another complication for adding more layers is the handling of the increased number of LOOP commands within loops within loops. The best option might be to put the internal loops into a function which calls itself as it drills down through the folder tree structure. If this is done with a function, the array variables would need to be declared global as they are created at each level. Otherwise the data would be lost in local variables.
Wow, I just read over the last few paragraphs. "What a nerd!" If you made it this far then more power to you. I think I may have gone beyond the scope of what I planned to accomplish here. However, if you have any thoughts or questions, I'd sure like to hear them.
The LOOP (files and folders) and MENU commands are two more power tools to add to your bag of AutoHotkey tricks. When combined with other tools many more apps are possible.

Chapter Twenty-two: Tweaking the QuickLinks AutoHotkey Script

“A reader's question prompts a look at improving AutoHotkey scripts by adding QuickLinks to the System Tray icon right-click menu.”
What if your keyboard doesn't have a Windows key? Here are some simple modifications to the AutoHotkey scripts. Plus compiling AHK scripts into EXE files that run on any Windows computer.
Ron Cerrato from San Diego writes in regards to the QuickLinks app compiled from the AutoHotkey script:
My PC has no Windows key. I found your QuickLinks program useful in Windows 8 since it doesn't require a Windows key, however even there, I'd much prefer a right click option to show up in the desktop right-click list.
I hadn't considered the fact that many people don't have a Windows  key on their keyboard. My purpose in writing these apps is to use them as tools for learning how to write and use AutoHotkey scripts. Therefore, rather than merely making changes to the apps, I will show you how to make changes to the scripts which achieve the results you want.

Adding Right-click Capability to the QuickLinks App

While I don't see a way to add QuickLinks to the right-click menu on the Windows Desktop, it can be added to either the AutoHotkey icon in the System Tray on the right-side of the Taskbar or as a separate right-click in conjunction with one other key—in this case the CTRL key. In the latter case, it is merely a matter of adding an additional hotkey combination at the bottom of the QuickLinks.ahk script:
^RButton::Menu, QuickLinks, Show

With line of code, CTRL+Right Mouse Button will pop up the QuickLinks menu.
To add the right-click capability to the icon in the System Tray, there is a MENU command for doing just that called Tray. It is simply a matter of replacing all occurrences of QuickLinks in the script with Tray. Then, when the QuickLinks icon in the Systems Tray is right-clicked the QuickLinks menu will pop up (see Figure 1). The other key combinations will continue to work as before.Figure 1. The QuickLinks app is attached to the AutoHotkey System Tray icon with the Tray parameter. Notice that the icon and tool tip message have been changed.
While the new QuickLinks script is very much like the original offered in the Chapter Twenty-one, there are few changes to note. First, two lines were added: one to change the icon, the second to change the tool tip message when hovering over System Tray icon.
 Menu, Tray, Icon, %A_WinDir%\system32\SHELL32.dll,43
 Menu, Tray, Tip, Right-click for`nQuickLinks

Notice that Tray is used in place of the menu name. Tray always refers to the System Tray icon for the current AutoHotkey script. The Icon parameter is used to change the icon displayed. In this case it is icon number 43 found in the Windows System file SHELL32.dll. Later, when the file is compiled with the separate utility (discussed below), the icon is replaced with a replica of the Microsoft logo and this line of code is removed.
The second line of code noted merely changes the tool tip message to "Right-click for QuickLinks", as shown in Figure 1.
The other change of interest is the addition of the following lines:
 Menu, Tray, NoStandard
 Menu, Tray, Standard

Normally the standard AutoHotkey Tray menu items (Suspend Hotkeys, Pause Script, Exit) would appear at the top in the right-click menu. After the new menu items are added to Tray, the standard items can be relocated to the bottom by removing them with the NoStandard option in the Menu, Tray command. When the standard items are added back with the Standard option, they appear at the bottom of the list. (The complete QuickLinksTray.ahk script is found at the end of this chapter.)

Using the "Convert .ahk to .exe" Utility

While compiling is as simple as right-clicking on the AHK script file name and selecting Compile Script, if you use the "Convert .ahk to .exe" utility which comes with the AutoHotkey download, you get a little more flexibility and you can add a tailored icon for the System Tray (see Figure 2).Figure 2. The "Convert .ahk to .exe" AutoHotkey compiling utility allows the selection of a custom icon for the System Tray.
If the Tray icon is also access to QuickLinks, I felt that it should look a little different. I could have used the Windows 7 Start logo, but I settled for making an ICO file with the Microsoft key logo () used in ComputorEdge articles and columns. (The program IrfanView is excellent for converting images to ICO files.)
Once compiled, when the QuickLinks.exe program is loaded, the icon will appear in the System Tray and operate with a right-click as shown above in Figure 2.

QuickLinksTray.ahk AutoHotkey Script

For a new version of the QuickLinks app which adds CTRL+Right-click and right-click on the System Tray, copy-and-paste the following code into a new or current AutoHotkey script:
IfNotExist, C:\Users\%A_UserName%\QuickLinks\
    FileCreateDir, C:\Users\%A_UserName%\QuickLinks\

Loop, C:\Users\%A_UserName%\QuickLinks\*.*, 2 , 0  
{
   Menu, Tray, Add, %A_LoopFileName%, MenuHandler

   MainMenu := A_LoopFileName
   CountLoop := 0
   Loop, %A_LoopFileFullPath%\*.*, 1 , 0
   {
     if A_LoopFileAttrib contains H,R,S  ;Skip any file that is 
       continue                               ; H, R, or S (System).

        Menu, %MainMenu%, Add, %A_LoopFileName%, MenuHandler
        CountLoop := 1
   }
   If (CountLoop = 1)
    {
      Menu, Tray, Add, %MainMenu%, :%MainMenu%
    }
   Else
    {
     Menu, Tray, Add, %MainMenu%, FolderHandler
    }
}

 Menu, Tray, Add ;Add a separator bar

 Menu, Tray, Add, Edit QuickLinks, QuickLinksHandler
 Menu, Tray, Add, Reload QuickLinks, ReloadHandler
 Menu, Tray, Add, QuickLinks Help, HelpHandler
 Menu, Tray, NoStandard
 Menu, Tray, Standard
 Menu, Tray, Icon, %A_WinDir%\system32\SHELL32.dll,43
 Menu, Tray, Tip, Right-click for`nQuickLinks

Return

MenuHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenu%\%A_ThisMenuItem%
return

QuickLinksHandler:
run, C:\Users\%A_UserName%\QuickLinks\
Return

FolderHandler:
run, C:\Users\%A_UserName%\QuickLinks\%A_ThisMenuItem%
Return

ReloadHandler:
Reload
Return

HelpHandler:
Run, http://webserver.computoredge.com/online.mvc?issue=3103&article=vista&src=qcklnks
Return

#z::Menu, Tray, Show
!,::Menu, Tray, Show
^RButton::Menu, Tray, Show

Part VI: AutoCorrect for All Windows Apps and AutoUpdating AutoHotkey on Multiple Computers

“Add the automatic correction of commonly misspelled words to all of your Windows computers.”
AutoCorrect is one of the most valuable AutoHotkey apps. It works across all Windows programs and Web browsers. If you add Dropbox, then you can keep it updated on all of your Windows computers.
While the AutoCorrect.ahk AutoHotkey script file is extensive, it's simple code. With over 5,000 commonly misspelled words it fixes errors while they are being typed. This is a powerful AutoHotkey script and is easy even for the beginning script writer. If you use AutoCorrect for any amount of time, you will become an AutoHotkey devotee.
Plus, if you have multiple Windows computers, then there is an easy, free way to keep them up-to-date. Sign up for a Dropbox account and share your compiled EXE files on all of them. You only need AutoHotkey installed on one of them.
Chapter Twenty-three: Add AutoCorrect to Your Windows PC
For people who fall victim to typos and common misspellings.
Microsoft Word offers AutoCorrect, but now you can add it to all of your other Windows software and Web browsing. You can even add your own personal pet peeves to the list.
Chapter Twenty-four: Adding More to AutoCorrect
Anyone can add needed keys and other special characters to AutoCorrect.
An AutoHotkey tip for novice script writers to add that needed special character to their techniques for improving AutoCorrect.
Chapter Twenty-five: Updating Programs on Multiple Computers
A trick for replacing program files on computers with Dropbox.
You are writing and compiling your AutoHotkey scripts on one computer, but using the compiled EXE programs on a number of other Windows computers. Here's how to use Dropbox for a no-hassle way to keep all the connected computers updated.

Part VI Index to Chapters

::, hotkey substitution, Chapter EightChapter Twenty-three
:*:, hotkey substitution * option, Chapter Twenty-three
Automating AutoHotkey app launch at startup, Chapter Twenty-threeChapter Twenty-five
AutoCorrect app, Chapter Twenty-threeChapter Twenty-four
Character Map, adding special symbols in Windows with, Chapter Twenty-four
Common misspellings in English, Chapter Twenty-three
Dropbox, for updating computers, Chapter Twenty-five
Exiting an AutoHotkey app with the System Tray icon, Chapter Twenty-three
SendInputChapter Twenty-four
Sharing AutoHotkey files with Dropbox, Chapter Twenty-five
Startup folder, automate AutoHotkey script launch, Chapter Twenty-threeChapter Twenty-five
Stop a running AutoHotkey app, Chapter Twenty-three
Substution (::), AutoHotkey, Chapter Twenty-three

Chapter Twenty-three: Add AutoCorrect to Your Windows PC

“For people who fall victim to typos and common misspellings.”
Microsoft Word offers AutoCorrect, but now you can add it to all of your other Windows software and Web browsing. You can even add your own personal pet peeves to the list.
Have you ever wished that you could type resume when you need it without searching for the "e" in Windows Character Map? Are you like me, a little dyslectic, and occasionally enter "thier" rather than "their" when typing on your keyboard? Or, "hte" when you mean "the"? Microsoft Word has AutoCorrect which will fix these errors, but what if you're using Notepad, blogging on the Web, or sending an e-mail? Do you want a quicker way to enter the trademark symbol (™), copyright (©), or registered trademark (®)? Now you can use a program which will automatically correct common typos and misspellings on the fly and works in any program or any Web page—not just Microsoft Word.
This program uses Wikipedia's "Lists of common misspellings," ''Typo," the Microsoft Office autocorrect list, the OpenOffice autocorrect list, and various other lists. The original program file is rather old, but last time I checked common typos and misspellings haven't changed much in the last century.
This AutoCorrect program is not a replacement for a spell checker, but an AutoHotkey extension for your Windows computer which immediately makes changes to common errors—not all misspellings. Nor does it scan a previously written documents looking for misspellings. It works only while you're typing and corrects the errors immediately without prompting. The list of words is limited to common, not easily confused, mistakes, but there are still over 5,000 entries in the list. Generally, a spell checker merely highlights a word not found in its dictionary, then you make the decision.
This AutoCorrect program is an AutoHotkey script, but since many people may not be interested in installing or learning AutoHotkey (not you—since you're reading this book), I've compiled the basic file into an executable, AutoCorrect.exe, which will run on any Windows computer—even if AutoHotkey has never been installed. I've posted it as a free download on DropBox in the same folder as past AutoHotkey downloads. If you download the AutoCorrect.exe file and double-click it, you will get the benefit of this program without further installation. If you would like the program to automatically load whenever you log into Windows, open the Startup folder (Run => shell:startup), and add either a copy of the program file or create a shortcut pointing to the program. (I recommend a shortcut as discussed in Chapter Twenty-five.)
Note: When the AutoCorrect.exe file is loaded a green icon with an "H" in it will appear in the System Tray on the right side of the Windows Taskbar. (See the figure to the left). To stop the program, right-click on the icon and select Exit.
The only downside to using the compiled "run-anywhere" program, AutoCorrect.exe, is that you won't be able to modify it for your special needs. If you want to tailor your list of AutoCorrect words, then you must install AutoHotkey and make the modifications yourself to the AHK source file which is in plain text. (The source code I used for AutoCorrect, AutoCorrect.ahk, can be found in the same DropBox download folder. For more information on DropBox, see "Forget E-mail! Dropbox for Moving and Sharing Files!") Even if you don't plan to use the AutoCorrect.ahk file with AutoHotkey, you can view the words included in this version of the program by opening AutoCorrect.ahk with Notepad.
There are many people who could have written this program, but my hat goes off to those who took the time to compile the lists, make corrections, and add new functionality. The original source (unmodified by me) can be found at the AutoHotkey site. (You can review this page on the Web rather than downloading my AutoCorrect.ahk file. It is almost identical except for the changes I note below.) This may be one of the most useful scripts from AutoHotkey and the reason why I'm now offering it to ComputorEdge readers.

How AutoCorrect Works

The operation of AutoCorrect is built on one of the most basic features of AutoHotkey: substitution. Within the script is a long list of common errors. Whenever one of those non-words is typed, it is replaced on the spot with the correct word. Of course errors which are also other real words aren't included. That could become correction chaos. You still need to know whether you want "your" or "you're" when typing, although "you;re" will be changed to "you're."
If you type "btu" it will be replaced with "but", but only if it follows a comma or semicolon and a space. Otherwise you would never be able to use the British Thermal Unit (BTU). The word "embarass" becomes "embarrass"—even when part of "embarrassment." Here are some random samples from the "a" list:
::amoung::among
::amung::among
::amoungst::amongst
::ammount::amount
::ammused::amused
::analagous::analogous
::analogeous::analogous
::analitic::analytic

The word after the first double colon is automatically replaced with the word following the second double colon ("ammused" => "amused") when it is followed by a space or a punctuation mark. It's that simple. After you load the script (double-click AutoCorrect.exe), open Notepad and give it a try.

Fancy Foreign Words in English

If you want to add effete snobbery to your e-mails, then included is a list of foreign words found in English which use those ridiculous accents and umlauts. "He went to the smorgasbord soiree with his protege because he wanted some crepes or a souffle" or, "Senor will fish for piranas manana." You might want to refer to a moral from one of "?sop's" fables. Is that pretentious enough for you? Here are a few examples from the foreign word file:
::outre::outre
::papier-mache::papier-mache
::passe::passe
::piece de resistance::piece de resistance
::pied-a-terre::pied-a-terre
::plisse::plisse
::pina colada::Pina Colada
::pina coladas::Pina Coladas
::pinata::pinata
::pinatas::pinatas
::pinon::pinon

Pet Peeves

Here is one that I added to my list just for all those sports announcers who don't know that many fans have more "athleticism" than any of the players.
::athleticism::athletic ability

athleticism 1. an active interest in sports. 2. an obsessive participation in physical activity.
Now whenever someone wants to use "athleticism" it is automatically changed to" athletic ability." Too bad this doesn't work when the announcers are talking on air. I wanted to change it to, "It's 'athletic ability' you airhead!", but I thought it would be too rude.
I've added:
::i could care less::I couldn't care less
since most people actually couldn't care less, even though they say they could.
I've also added a line to change "on accident" to "on purpose" since the former is obviously a Freudian slip used by people trying to hide the truth. Otherwise, they would say "by accident" just like innocent people do. It turns out that "on accident" is commonly used by people under 35 while "by accident" is used by more mature individuals. Some put the blame on Barney and Friends, but that doesn't work since Barney and Friends started airing in 1992. By then my second son, the first person I heard say "on accident," was ten years old and he wouldn't have been caught dead watching that show.
I considered putting in ("gone missing" => "disappeared"), but I've watched too many British mysteries to make that change.

Correcting the British

If you want to force the British expatriates in your office to be more American, then you will need:
::colour::color
::capitalise::capitalize
::organisation::organization

For a more complete list of British spelling variations see Karen's Linguistics Issues and Wikipedia.
There is no limit on making additions to the list, but you can't do it to the compiled AutoCorrect.exe file I posted. You need to start with either the AutoCorrect.ahk file or cut-and-paste the original linked code into a new AutoHotkey AHK file.

What Else is Included?

In addition to the over 5,000 common misspellings and typos, the list includes common word endings ("sice" => "sive") and beginnings ("asociat" => "associat"). There is also a fairly long list of non-operating options that would need to be added to the active list before they could be used. You must decide which result (if any) would work for you. Here is a small portion of that inactive list.
::calaber::caliber, calibre
::calander::calendar, calender, colander
::cancelled::canceled  ; commonwealth vs US
::cancelling::canceling  ; commonwealth vs US
::canon::canon
::cant::cannot, can not, can't
::carcas::carcass, Caracas
::carmel::caramel, carmel-by-the-sea
::Cataline::Catiline, Catalina
::censur::censor, censure
::ceratin::certain, keratin
::cervial::cervical, servile, serval
::chasr::chaser, chase
::clera::clear, sclera
::comander::commander, commandeer

To make any of these changes, install AutoHotkey and update the original AutoCorrect.ahk. Then you can either run the AHK file with the installed software or you can recompile it into the AutoCorrect.exe program which will run on any Windows computer.
This AutoCorrect program will also automatically capitalize the days of the week and months (except March and May which have alternate meanings).

Adding Your Own Corrections

If there are common typing errors for particular words which plague you, and if you're running the AHK file with the installed AutoHotkey program, you can select (highlight) the error and use the WIN+H (+H) hotkey combination to add it to the AutoCorrect.ahk file. For example, suppose mistyping "enoug" when you mean "enough" is a common typo for you. Rather than always going back during your proofing to edit "enoug", you can add it to the AutoCorrect.ahk file by selecting it and pressing WIN+H (+H). An editing window for adding new corrections will open (see the figure to the left). Simply edit the word on the right to the correct form, then click OK. The correction will be added to the bottom of the AutoCorrect.ahk file. (Remember, this will not work with the AutoCorrect.exe compiled program since you can't add to the executable file. You need to first add changes to the AHK file, then recompile it into a program file.)
The following is a list of corrections I added before compiling the version posted on Dropbox:
::(c)::©
::(r)::®
::(tm)::™
::creme::creme
::expose*::expose
::resume*::resume
::ne::ne
::athleticism::athletic ability
::i could care less::I couldn't care less
::on accident::on purpose
::enoug::enough

I first added the copyright and trademarks symbols (©, ®, and ™) . They are automatically converted when you enclose the corresponding letters in parentheses. I added "creme" because I would never use it without the accent. Plus, here are a few fractions:
:*:1/4*::?
:*:1/2*::?
:*:3/4*::?

Note that in order to distinguish when I want to use "expose" or "resume" rather than "expose" or "resume" respectively, I just add an asterisk to the end of the word which will convert it to the accented form. I did the same for the fractions, although they will convert instantly when the asterisk is added due to the asterisk option between the first two colons (:*:).
If for some reason AutoCorrect is changing something you don't want changed, you can usually correct it by temporarily leaving out the space or punctuation at the end of the word (the correction won't activate), then go back to add the space later.

It Works Everywhere

This AutoCorrect program should work anywhere that your keyboard works (word processors, e-mail, Web page edit boxes, Notepad, and more). If you want to tailor the AutoCorrect program to your work, then a little editing of the AutoCorrect.ahk file will go a long way (i.e. removing the "on accident" => "on purpose" replacement).

Chapter Twenty-four: Adding More to AutoCorrect

“Anyone can add needed keys and other special characters to AutoCorrect.”
An AutoHotkey tip for novice script writers to add that needed special character to their techniques for improving AutoCorrect.

Adding that One Needed Special Character

As discussed in Chapter One, if you have one or two special characters such as the copyright symbol (©), the degree sign (°), or the British Pound (?), that you use extensively in your work, you may want to add it to your keyboard as a hotkey combination. You could use Character Map (Run (+R) => charmap) to find and copy the character to the Windows Clipboard, but that quickly becomes tedious if you need to do it all the time (see Figure 1). A simple keyboard combination is much easier.Figure 1. Character Map can be used to copy special characters to the Clipboard for pasting into an AutoHotkey script.
I discussed this technique in more detail in a Chapter One, but I've come to realize that it's possible to go hotkey crazy and add too many combinations to your keyboard. You should reserve the assignment of specific hotkeys to only those special characters that you use most. Otherwise, adding special characters to a more comprehensive app such as AutoCorrect (Chapter Twenty-three) makes more sense.
Suppose you're an advertising copy writer and frequently use the copyright symbol (©). You could use the ALT combination "0169" to enter the symbol. (Hold down the ALT key, enter 0169 on the number keypad, then release the key. The © will appear.) The problem with that technique is remembering the four-digit number combination, as well as pressing five keys. An alternative is to add a hotkey combination for the copyright symbol—in this case ALT+C. It's a simple one-line script.
!C::SendInput {©}

The exclamation point (!) and letter C represents the ALT key plus the C key pressed simultaneously. The double colon (::) marks the end of the combination and the beginning of the action. SendInput inserts the symbol shown between the curly brackets into your document.
Once the script is activated, the copyright symbol will be inserted every time you press ALT+C. You could add a number of these hotkey combinations to your keyboard, but if you have too many, it will be impossible to remember them all without looking them up somewhere. That's too involved. If you have numerous special characters to add, then you should put them in an AutoCorrect type script with easy-to-remember keys.

Adding Special Characters with AutoCorrect

I've added the AutoCorrect app to all my computers. It impresses me when I see the typo "thier" change to "their" right before my eyes—regardless of which program or Web page I'm using. If you need to add a number of special characters to your keyboard, then it may be better to add them to the AutoCorrect.ahk file in a manner that makes them easy to remember.
For example, I added copyright, trademark, and registered trademark symbols by using the common text substitutes i.e. (c), (tm), and (r) which automatically convert to ©, ™, and ®. The lines added to the AutoCorrection script are as follows:
::(c)::©
::(r)::®
::(tm)::™

For symbols that were not so obvious, I merely added an asterisk (*) at the end of the spelled word. For example the Yen symbol (?) is displayed by typing "yen*" followed by a space or punctuation. In the AutoCorrection file they appear as follows:
::cents*::?
::pounds*::?
::yen*::?
::plusminus::±

Note that the plus or minus sign (±) is added by typing "plusminus" followed by a space or punctuation. These techniques are much easier to remember than hotkey combinations or ALT codes.
There is no limit on the number of additions that can be made to your AutoCorrect.ahk file to tailor your substitutions. Plus, there are more options available for the double colon (::) substitution command (Chapter Three) which affect how they work.

Chapter Twenty-five: Updating Programs on Multiple Computers

“A trick for replacing program files on computers with Dropbox.”
You are writing and compiling your AutoHotkey scripts on one computer, but using the compiled EXE programs on a number of other Windows computers. Here's how to use Dropbox for a no-hassle way to keep all the connected computers updated.
The main theme of this part of the book is using AutoHotkey for auto-correcting spelling and typos on the fly. If this is a service that fits into your daily computing, then you'll want the AutoCorrect.ahk program to load every time you log in or start up the computer. Plus, if you have multiple computers, rather than installing the complete AutoHotkey software on each, it's much easier to compile the AHK scripts into EXE programs, Chapter Twenty-two, on one computer, then share that executable with all your other computers.
Here's how to use the Dropbox file sharing system to automatically share and update your program files on all your computers, launching the new version on the next logon or startup.
If you don't already have one, the first step is to open a free Dropbox account. Dropbox is a program which sets up a folder on your computer, then automatically shares files you put in it with your other computers and devices on the same Dropbox account (Windows computers, Macs, iPad, Kindle Fire, etc.). Dropbox is an Internet service which also stores the shared files in the Cloud. (For more information on free Dropbox accounts see "Forget E-mail! Dropbox for Moving and Sharing Files!")
Once you have Dropbox on all of your Windows computers, put your compiled AutoHotkey file (i.e. AutoCorrect.exe) into a special folder. I used one called Dropbox => AutoHotkey, as shown in Figure 2 (top).

Adding AutoCorrect.exe to Startup

To automatically load programs and utilities when you log on, place a program shortcut in your Startup folder. Any program (or shortcut pointing to a program) located in the Startup folder will load as part of the log in process. The easiest way to open the Start folder is to Run (+R) => shell:startup. This opens the Startup folder (see Figure 1). If you want a program to open for all users, place the shortcut into the Common Startup folder—Run (+R) => shell:common startup.Figure 1. Selecting Run from the Start menu and entering shell:startup will open the Startup folder.
The next step is to make a shortcut for the AutoHotkey file in the Startup folder. It is important to create a shortcut rather than placing the actual program in the Startup folder. The reason for this is that once a program is launched the original file is locked and can't be copied over while it is running from the Startup folder. However, if the shortcut in the Startup folder is running the program directly from Dropbox, the original file will continue to update through Dropbox. This means that you will be able to update your Dropbox AutoHotkey programs even while they are running.
The easiest way to create the shortcut is to open both the Dropbox folder and the Startup folder, then while holding down CTRL+SHIFT, click and hold the left mouse button on the target program file and drag it to the Startup folder (see Figure 2).Figure 2. To create a shortcut pointing to a file, open both folders in separate instances of Windows File Explorer and drag the file to the new folder while holding down CTRL+SHIFT.
Once each computer has Dropbox installed and the appropriate shortcuts have been created in the respective Startup folders, you will be able to update the AutoHotkey scripts and programs on one computer, then copy them via Dropbox to all the other computers in one move. When logging on to the updated computers, the new version of the AutoHotkey file will load.

Part VII: Building the Reminder App Step by Step

“Starting out as a quick same-day reminder, this app slowly gets more robust.”
The Reminder app is a quick way to set up a text pop-up cue with an audio reading component. Follow the evolution of this AutoHotkey script.
The Reminder app, a simple script which allows a Windows user to set a quick reminder note which pops up at the appointed time, is the most involved script in the book. The following chapters build the app a piece at a time. It is done in a very real step-by-step process which explains each new line of code and any nuances in the AutoHotkey code (including the errors I made). While the app is functional at each stage, it is not actually completed in this e-book. That is left to the reader—although at some time in the future there will be ComputorEdge columns and another e-book which will add more desirable features.
Chapter Twenty-six: A Cool Little Appointment Reminder for Windows
Don't forget those scheduled meetings you can't afford to miss!
AutoHotkey's flexibility is demonstrated with a script which sets up a personal reminder in Windows that breaks through the haze.
Chapter Twenty-seven: Lonely? Make Your Computer Talk to You!
Add many cool features to your Windows computer with the free nircmd utility.
NirCmd is a free command line utility which can open and close your DVD trays, hide the clock on the Taskbar, make your computer read to you, and much more.
Chapter Twenty-eight: A Talking Reminder for Windows
Add the NirCmd Speak command to the AutoHotkey Reminder app.
Now you can make your AutoHotkey Reminder app read your appointment out loud. Plus, there's a better way to calculate time/date differences in AutoHotkey (EnvAdd).
Chapter Twenty-nine: Extending the Reminder AutoHotkey Script Beyond 24 Hours
Add a calendar and default the GUI to now, plus limit the reminder to the future dates and time.
Why not a reminder program which schedules for more than the next 24 hours? Plus, default the GUI to the current date and limit your reminders to the future.
Chapter Thirty: Solving More AutoHotkey Script Issues
Correcting a time calculation error, check for voice support, disabling the Escape key, and RETURN key problem.
There is another error in the time calculation error, plus what happens if there is no voice support. We need to disable the Escape key, and an accidental pressing of the RETURN key dismisses the reminder.
Chapter Thirty-one: Adding Reminder Setup to System Tray Saving the Reminder to a File
Adding setup to System Tray and how to make the Reminder app last for days with an INI file.
Corrections and enhancement for the Reminder app which teach more valuable AutoHotkey techniques, including adding Reminder setup to the System Tray icon and saving your reminder to disk.
Chapter Thirty-two: Check for Voice, Turn It On and Off, and Change the Cursor
Is NirCmd installed?; turning voice on and off; and change the cursor to a pointing press finger.
In this wrap up of the current state of the Reminder script, features are added to make the app a little more useable.

Index to Part VII: The Reminder App

%, expression evaluation with single %, Chapter Twenty-eight
# Commands, Chapter Thirty
#IfWinActive commandChapter Thirty
A_Now (current time), Chapter Twenty-sixChapter Twenty-nine
Adding audio with NirCmd, Chapter Twenty-sevenChapter Twenty-eight
Adding speech, Chapter Twenty-eight
Audio, adding with NirCmd, Chapter Twenty-sevenChapter Twenty-eight
Calculating time, EnvSub, Chapter Twenty-eight
Change button names, Chapter Thirty
Check for program installation, NirCmd, Chapter Thirty
ControlSetTextChapter Thirty
Date/time specific GUI commandChapter Twenty-sixChapter Thirty-one
Date/time, limited range, Chapter Twenty-nineChapter Thirty-one
Disable hotkey combinations temporarily, Chapter Thirty
Disable key in active window, Chapter Thirty
Disabling keyboard keys, Chapter EightChapter Thirty
DllCall, Chapter Thirty-two
EnvAdd and EnvSub, difference in time calculation, Chapter Twenty-eight
EnvSub commandChapter Twenty-sixChapter Twenty-eightChapter Thirty
Menu, add to System Tray, Chapter Thirty-one
Menu, disable System Tray item, Chapter Thirty-one
MsgBox, Chapter Twenty-sixChapter Twenty-nineChapter Thirty
MsgBox Options, Chapter Thirty

Chapter Twenty-six: A Cool Little Appointment Reminder for Windows

“Don't forget those scheduled meetings you can't afford to miss!”
AutoHotkey's flexibility is demonstrated with a script which sets up a personal reminder in Windows that breaks through the haze.
There are times when you make appointments that you absolutely don't want to miss. That is when it is handy to have a quick reminder—both easy to set up and use—that will pop up in the middle of your computer screen screaming at you to get moving. This type of alarm is especially useful if you are the type of person who immerses yourself in your work (or play) oblivious to the rest of the world. I set about the task of finding an AutoHotkey reminder script that would do just that.
While I'm sure that there is one out there somewhere, I didn't find a reminder script that was both simple to use and easy to code. I decided to write one myself keeping complication to a minimum. I liked the idea of writing an AutoHotkey script which schedules a reminder at a later time in the day because it would shows both the flexibility and power of the scripting language. This reminder app is not the usual text substitution that most people think of when working with AutoHotkey.

The Reminder App

The reminder script is basic in its function. Press the hotkey combination CTRL++R (R for reminder) and a setup window pops up. It uses both the GUI (Graphic User Interface) command for picking a time and adding a message, then sets a timer for a pop-up to appear at a later time in the same day (see Figure 1).Figure 1. Select a time later the same day and enter a message to set the reminder.
After clicking the Submit button a confirmation window pops up showing the new reminder (see Figure 2). If you accidentally select a time earlier than now, then a different window pops up explaining that you need to pick a later time. In its first form, the reminder can only be set up to midnight, but I explain later in this chapter (and continue in later chapters) what's necessary to make it a 24-hour reminder. Initially, since it is sitting in memory unsaved, if you shutdown the computer, the reminder will be lost. I'm sure that it can be saved in a file to add persistence to the reminder system, but that's a more advanced topic suitable for a later chapter (Chapter Thirty-one).Figure 2. The pop-up tells you when the reminder will occur.
This first AutoHotkey reminder script is a simple scheduler for one quick and easy prompt. If you try to schedule a second reminder, it will overwrite the first. (Adding multiple reminders is another more advanced topic for sometime in the future. Check out my AutoHotkey column at ComputorEdge.com) When the scheduled time arrives, another window pops up displaying the reminder (see Figure 3). It both chimes (if the audio is turned on) and the window stays always-on-top until it is closed. (In Chapter Twenty-eight a talking voice is added to read the reminder.)Figure 3. The reminder pops up at the scheduled time and stays always-on-top until it is closed.

Writing the Reminder Script

Even if you have never used most of them, it is worthwhile to be familiar with what AutoHotkey commands are available. It's like knowing what's in your toolbox. The first step for me was figuring out how I could set an alarm. I looked for a command which would set a clock time and at that time it would launch a routine. That requires a function which continuously checks the clock until the appointed time. I didn't find a scheduling command in AutoHotkey that checked the clock.
However, I did find the SetTimer command which is similar to a kitchen timer for soft-boiled eggs. With the SetTimer command a routine can be scheduled to run a routine a specific number of microseconds later. To use this command for the reminder script I needed to know the current time, the scheduled time, and the difference between the two in microseconds.
Fortunately, AutoHotkey includes a date/time specific GUI command:
Gui, Add, DateTime, [time variable, format]

This GUI, DateTime command allows the entering of a scheduled time by either direct editing or using the up and down arrows on the right side of the interface as shown in Figure 1. Without this date/time gadget, the problem would be much more difficult. It's important to note that even though only the time can be entered in the pop-up shown in Figure 1, both the date and time are saved to the variable.
The problem with the date/time variable is that it uses the Year/Month/Day/Hour/Minute/Second format as described in the column AutoHotkey column and shown in Figure 4—also included in A Beginner's Guide to AutoHotkey. But to set the timer the time delay must be in microseconds. Subtracting the current time from the scheduled time in the standard date/time format will yield nonsense since the seconds, minutes, and hours are all in odd increments (24 hours, 60 minutes, and 60 seconds). First, both times need converting to seconds before finding the time interval (the difference between current time and scheduled time).Figure 4. The computer sees the date/time as digits from the year down to the second.
Since I'm only dealing with the current day, I can calculate the number of seconds in both the current time and scheduled time by adding the seconds (the last two digits in the time variable) to the minutes multiplied by 60 seconds (the second to last two digits in the time variable times 60 seconds) plus the hours multiplied by 3600 seconds (the third to last two digits in the time variable times 60 minutes times 60 seconds). The difference between the scheduled time and current will give me the delay needed in seconds. Since, the SetTimer command uses microseconds, multiply the seconds by 1000. (As it turns out, there is a much easier way to calculate times in AutoHotkey. Using the time options in the EnvSub command, discussed in Chapter Twenty-eight, quickly replaces the math I use in this chapter.)
Once this logic is worked out, it's a matter of putting the script together.

The Reminder.ahk Script

As usual I put in the code for creating the pop-up first (Figure 1):
^#R::

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w150 1, hh:mm tt
Gui, Add, Edit, vMyNote w250, Remind Me!
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

The first line creates the hotkey combination CTRL++R (^#R) for activating the reminder.ahk script.
The second line (Gui, Font, s12, Arial) formats the text in the pop-up as described in earlier chapters.
The third line of code (Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup) adds the short list of instructions for the pop-up. Note that the "`n" is the special character for creating a new line.
The fourth line of code (Gui, Add, DateTime, vMyTime w150 1, hh:mm tt) adds the DateTime function to the GUI. The "vMyTime" creates the variable MyTime which stores the scheduled time when the Submit button is clicked. The "w150" sets the width of the time editing field.
By default the DateTime GUI has a dropdown calendar associated with it, but since we don't need or want a calendar with this app (at least not yet), the number "1" appearing in the options replaces the dropdown calendar with the up-down control on the right. The format "hh:mm tt" provides input fields for hours, minutes, and AM or PM. Note that there is no gLabel included to launch a subroutine. The reason for this is the DateTime GUI would immediately trigger the label every time it detected a change which is far too often for our purposes here. Instead a Submit button and routine are used.
The fifth line of code (Gui, Add, Edit, vMyNote w250, Remind Me!) adds the edit field for the reminder text. The text is stored in the variable MyNote when the Submit button is clicked. The "w250" sets the width of the field. "Remind Me!" is the default value appearing in the field.
The sixth line of code (Gui, Add, Button, Default, Submit) adds the Submit button to the GUI which initiates the action when clicked.
The last line of code (Gui, Show, , Reminder) before the "Return" marks the end of the GUI portion of the script displaying the pop-up with the title "Reminder."

Adding the Action

The remaining code may look a little intimidating to the novice script writer, but, once broken down, it is relatively simple. There are three subroutines (labels): ButtonSubmit: the default routine for clicking the Submit button; GuiClose: which deletes the original pop-up so you can rerun it without causing an error; and ShowReminder: which is the label for activating the reminder at the preset time. The entire routine for setting up the reminder triggered by clicking the Submit button is as follows:
ButtonSubmit:
Gui, Submit, NoHide

If (MyTime > A_Now)
  {
    FormatTime, HrTime , %MyTime%, HH
    FormatTime, MinTime , %MyTime%, m
    FormatTime, SecTime , %MyTime%, s
    NewTime := ((HrTime*3600 + MinTime*60 + SecTime) 
                        - (A_Hour*3600 + A_Min*60 + A_Sec))*1000
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%
    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, %NewTime%
  }
Else
  {
    MsgBox, The time must be later in the same day!
    GuiControl,,MyTime, %A_Now%
  }
Return

GuiClose:
Gui, Destroy
Return

ShowReminder:
FormatTime, RightNow
MsgBox,4160,Your Reminder!, %MyNote% %RemMessage%`n%RightNow%
SetTimer, ShowReminder, Off
Return

In the ButtonSubmit: routine, the first line of code (Gui, Submit, NoHide) stores the set time to the variable MyTime. The NoHide parameter keeps the pop-up visible and available for change until it is closed by clicking the X in the upper right-hand corner of the GUI.
Next, based upon the set time, the IF conditional is used to determine if the scheduled time is a later time or earlier time by comparing the current time (A_Now) to the time stored in MyTime. If the user accidentally inputs an earlier time, then there is no point in continuing with setting the appointment. Thus the ELSE portion of the conditional displays a message (MsgBox, The time must be later in the same day!) stating the problem and replaces the time displayed in the pop-up with the current time (GuiControl,,MyTime, %A_Now%). Since the pop-up doesn't display seconds, it is often necessary to increment at least one minute forward to set an appointment.
If you decide that you want to make this routine a true 24-hour reminder, then you must change the ELSE portion of the conditional to the same code as the TRUE portion and add 24 hours worth of seconds to the calculation (60 seconds per minute times 60 minutes per hour times 24 hours per day) prior to the microsecond conversion.
The TRUE portion of the IF conditional checks for a time later than the current time (If (MyTime > A_Now)). If MyTime is greater than (>) the current time (A_Now) then the condition is TRUE. The selected time must be converted to seconds. To do this, the number of hours, minutes and seconds needs to be parsed and stored to variables using the FormatTime command:
    FormatTime, HrTime , %MyTime%, HH
    FormatTime, MinTime , %MyTime%, m
    FormatTime, SecTime , %MyTime%, s

The variables HrTimeMinTime, and SecTime store the hours, minutes, and seconds found in the set time variable MyTime respectively. Then each is multiplied by the appropriate number to convert to seconds, then added together to get the total number of seconds (HrTime*3600 + MinTime*60 + SecTime).
Using the variables for the current hour, minute, and second, the current time is converted to the total number of seconds (A_Hour*3600 + A_Min*60 + A_Sec). This value is subtracted from the set time number of seconds, then multiplied by 1000 to convert to microseconds and stored in the variable NewTime (NewTime := ((HrTime*3600 + MinTime*60 + SecTime) - (A_Hour*3600 + A_Min*60 + A_Sec))*1000).
NewTime is the value in microseconds needed for the SetTimer command (SetTimer, ShowReminder, %NewTime%). ShowReminder is the label for the routine to pop-up the Reminder window. At NewTime the subroutine ShowReminder will activate.
Prior to setting the timer, a pop-up window using the AutoHotkey MsgBox command activates which confirms the time of the reminder (MsgBox, 4160, Your Reminder, "%MyNote%" is scheduled for`n%Schedule%). The number 4106 indicates the type of window to open and makes it always-on-top as described in the "Making an AutoHotkey Help Window" section of a previous Windows Tips and Tricks column and Chapter Eleven of the beginner's e-book. The title of the window is "Your Reminder" with the input text (MyNote) and the time that it is scheduled as shown in Figure 2 above.
When the term "#Persistent" is added to a routine, it will continue to run even if an EXIT is executed. This is commonly used in routines using SetTimer to prevent it from prematurely terminating even after the pop-up is closed.
The next line (RemMessage := "Better get going!") is completely optional and adds nothing to the logic of the routine. It is merely a personal prompt.

Waiting for the Reminder

Once the timer is set (SetTimer, ShowReminder, %NewTime%) both of the open windows can be closed. At the appointed time the label ShowReminder will activate.
ShowReminder:
FormatTime, RightNow
MsgBox,4160,Your Reminder!, %MyNote% %RemMessage%`n%RightNow%
SetTimer, ShowReminder, Off
Return

The current time and date will be stored in the default format to RightNow using the FormatTime command.
Then in the same format as previously discussed the MsgBox command displays the reminder (MsgBox, 4160, Your Reminder!, %MyNote% %RemMessage%`n%RightNow%).
The SetTimer command is used to disable the timer. Otherwise, it would repeat the routine activation at the same time interval. The repeat of SetTimer can be disabled by making the interval in microseconds a negative number.
The complete AutoHotkey code for this version the Reminder.ahk program is as follows:
^#R::

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w150 1, hh:mm tt
Gui, Add, Edit, vMyNote w250, Remind Me!
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

ButtonSubmit:
Gui, Submit, NoHide

If (MyTime > A_Now)
  {
    FormatTime, HrTime , %MyTime%, HH
    FormatTime, MinTime , %MyTime%, m
    FormatTime, SecTime , %MyTime%, s
    NewTime := ((HrTime*3600 + MinTime*60 + SecTime) 
                        - (A_Hour*3600 + A_Min*60 + A_Sec))*1000
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%
    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, %NewTime%
  }
Else
  {
    MsgBox, The time must be later in the same day!
    GuiControl,,MyTime, %A_Now%
  }
Return

GuiClose:
Gui, Destroy
Return

ShowReminder:
FormatTime, RightNow
MsgBox,4160,Your Reminder!, %MyNote% %RemMessage%`n%RightNow%
SetTimer, ShowReminder, Off
Return

You can copy-and-paste this code into an AutoHotkey script, run it, then set a reminder with the hotkey combination CTRL++R.

Reminder.ahk Notes

As previously mentioned, if you would like to make the reminder script a full 24-hour script, then it is necessary to add 24 hours worth of microseconds to time which occurs before the current time. The ELSE portion of the IF conditional could be changed as follows:
    FormatTime, HrTime , %MyTime%, HH
    FormatTime, MinTime , %MyTime%, m
    FormatTime, SecTime , %MyTime%, s
    NewTime := (((HrTime*3600 + MinTime*60 + SecTime) 
                        - (A_Hour*3600 + A_Min*60 + A_Sec))*1000)+86400000
    FormatTime, Schedule, %MyTime%, h:mmtt
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`nTomorrow at %Schedule%
    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, %NewTime%

Note that 86400000 microseconds (24 hours) have been added to NewTime. Plus the FormatTime is changed to reflect only the hour and minutes with AM or PM. This is done because it's easier to just add "Tomorrow" to the MsgBox message, than calculating the date for the next day—especially if you're on the last day of the month. For now you need to take my word for it, because it is beyond the scope of this chapter. If you want future days as reminder options, then add the dropdown calendar back into the DateTime GUI. It will be easier than writing a function to do the obtuse calendar math. However, you will need to convert days (in the form of YDay—day of the year, bypassing months) into your microseconds calculations. (I'm sure that there are better options for long-term appointments.)
Note: As it turns out, once I discovered the EnvSub command with the time calculation options, discussed in Chapter Twenty-eight, life became much easier. I left the original code in this chapter for two reasons. It was my original path and shows some of the complications with time calculations.
If you want to include multiple reminders, then you will need multiple labels (ShowReminder1, ShowReminder2, etc.) and set up a way to track which are in effect so you don't overwrite them. It appears that you can't use a variable for the label at the head of the routine itself, which means adding separate routines for each reminder—even if the code is identical. Possibly a function could be used within the label, but again I haven't tried that and it's beyond the scope of this chapter. (Update: While this is true for labels, the multiple reminders could be added through the use of an INI file which would also address the weakness which follows. The techniques for adding multiple reminders are not discussed, but will be a topic for future ComputorEdge columns and/or e-books.)
Another weakness of this reminder routine is that it sits in memory. If you restart your computer, you lose your reminder. There are ways to make it more robust which include saving the basic information to a file. I see that the commands are available in AutoHotkey for getting this done, but it is an advanced topic which I will save for Chapter Thirty-one.
If you would like to test the compiled version of the 24 hour reminder (Reminder 1.1.exe), it can be found at the ComputorEdgeAutoHotkey Dropbox folder. Download Reminder.exe and double-click to load. Use CRTL++R to activate. Check the confirmation window for either today's date or the word "Tomorrow" to confirm when the reminder will actually happen—if you don't turn off your computer first.

Chapter Twenty-seven: Lonely? Make Your Computer Talk to You!

“Add many cool features to your Windows computer with the free NirCmd utility.”
NirCmd is a free command line utility which can open and close your DVD trays, hide the clock on the Taskbar, make your computer read to you, and much more.
If you want to open and close DVD drive trays without touching the computer case, hide the clock on the system tray, or make your computer read out loud to you, then there is a cool little utility which makes it easier—and it's free. Called NirCmd (nircmd.exe), it's a command line program which can control many of the functions of your Windows computer. For example, if you don't have multimedia controls on your keyboard, then you can add them to your Desktop with this little utility.
NirCmd is not new. It has been around for a number of years as a free Windows utility. It offers many simple features which normally aren't easy to access from the Windows Desktop. Since it was first written for earlier versions of Windows, some commands don't work in Windows 7 and Windows 8. However, the most useful (and interesting) features work in Windows XP, Windows Vista, Windows 7 and Windows 8, making it an almost universal Windows tool. I'm most interested in NirCmd because it can read text out loud with the Windows Narrator voice. (In the next chapter, I add this audio feature to the AutoHotkey Reminder script so that when the reminder message pops up at the appointed time the computer reads the text prompt.)
NirCmd is a command line program—meaning it is an app which can be added to a script with the appropriate parameters for a desired action. While you can put NirCmd commands into a batch file (BAT), you can also run them from the Command Prompt or with the Run (+R) window. See Figure 1 for an example of using the Run (+R) window with nircmd.exe. (The Run dialog can also be opened by selecting "Run..." from the Start Menu—if your version of Windows happens to have a Start Menu. For Windows 8 select Run from the +X menu.) The NirCmd options are listed at the Nirsoft Web site. The options include "Mute the system volume," "Turn off the monitor," "Speaks the text stored inside speak.txt into speak.wav filename," "Turn off your computer," "Set the My Computer window to right-to-left order (For Hebrew and Arabic languages)," "Empty the recycle bin in all drives," "Wait 2 seconds, and then save the current screen to shot.png," and many more.Figure 1. The nircmd.exe program is used to read out loud the contents of the Windows Clipboard.
If you're interested in testing nircmd.exe, the download can be found halfway down the Web page just after the Versions History or you can download the program from CNET. The download is a compressed ZIP folder containing three files. Open (double-click) the ZIP file to view the files (see Figure 2). Extract the files to a new folder.Figure 2. The nircmd.zip file contains three files. Click "Extract all files" to open a new folder with the uncompressed files.
The two files, nircmd.exe and nircmdc.exe, are essentially the same program with, for our purposes, little practical difference. Right-click on nircmd.exe and select "Run as administrator." The only purpose of this initial run is copying the file itself to the Windows System folder. That will put the file in the system path making it possible to run it from anywhere on the computer (see Figure 3).Figure 3. When the nircmd.exe program is run from the Windows Explorer (File Explorer in Windows 8), it copies the file to the Windows System folder. This puts the program in the path for running in scripts, from the Run command, or any directory location in the Command Prompt.
That's all there is to it. The program can now be used in scripts such as batch file or AutoHotkey, run from the Command Prompt, or just used with the Run (+R) dialog. For a test, open the Run (+R) dialog box and type in (or copy-and-paste) nircmd.exe speak text "You're so cool!" (including quote marks) and click OK (see Figure 4). Doesn't that feel good?Figure 4. With nircmd.exe located in the Windows System folder this Run command will tell you, "You're so cool!"
You can find more information about the commands and options further down the same Web page.
Do you want to hear the current day of the week and date?
nircmd.exe speak text ~$currdate.dddd,MMMMdd,yyyy$

Say "Hi!" to the current logged on user?
nircmd.exe speak text "hi,"~$sys.username$

Display a pop-up message box? (See Figure 5.)
nircmd.exe infobox "This is a message box !" "message"
Figure 5. A pop-up message box with nircmd.exe.

Do you want to see the calculator? Click "Yes" in the pop-up to open it.
nircmd.exe qbox "Do you want to run the calculator ?" "question" "calc.exe"

There are even commands for creating shortcuts and working with the Registry (careful).
Many of the commands are similar to AutoHotkey commands, although AutoHotkey is a more robust scripting language. It is relatively easy to integrate NimCmd commands into AutoHotkey scripts which in many cases may be easier than trying to do it all with AutoHotkey. For example, while it is possible to write speaking scripts with AutoHotkey, it is much simpler to add the NimCmd speak command—as is done in Chapter Twenty-eight. However, NimCmd would need to be available on each computer using the AutoHotkey app.

Chapter Twenty-eight: A Talking Reminder for Windows

“Add the NirCmd Speak command to the AutoHotkey Reminder app.”
Now you can make your AutoHotkey Reminder app read your appointment out loud. Plus, there's a better way to calculate time/date differences in AutoHotkey (EnvAdd).
In Chapter Twenty-six, I offered a short script that would schedule a reminder for sometime later in the day, then pop up the reminder at the appointed time (see Figure 1). Although the script works well, I complained at the time about not finding an easy way to calculate the difference between times and dates. Most other programming languages I've used included functions or commands to deal with the problem. Since that time I've discovered that AutoHotkey does indeed have ways to quickly and easily get the time/date calculations. It is so simple, yet it was hard to find—no special dedicated command or function. In this column, the Reminder script is rewritten using the built-in AutoHotkey time/date calculation thus eliminating quite a few lines of code.
Figure 1. The Reminder.ahk script has three pop-up windows: the setup box, the confirmation message, and the reminder which pops up at the scheduled time.
The second purpose of this chapter is to show how AutoHotkey can integrate with other scripting languages—in this case NirCmd and its Speak command. The Speak command in NirCmd is simpler and more flexible than any text reading feature I've found in AutoHotkey (so far). This makes it ideal for adding to the Reminder app. Now, not only will the Reminder app pop up and stay always-on-top until closed, the text message will be read out loud every fifteen seconds until you either close the box or you're driven crazy.
Note: To use the NirCmd commands in an AutoHotkey script, the program nircmd.exe must first be installed in the Windows System folder per the instructions given in the last chapter.

The New Reminder Script

The script for the Reminder app has been modified to include both the built-in time/date calculation and the implementation of the NirCmd Speak command with a new label (see Figure 2 in red boxes). This new version also uses the "%" expression technique for converting to the microseconds needed by the SetTimer command. (You may want to review the previous Reminder script since only the changes will be discussed here.)
Figure 2. The original Reminder.ahk script is altered to include the built-in time/date calculation format, the NirCmd Speak command, and an example of using the "%" expression within another command (SetTimer).
Note: When I originally wrote this chapter, I made an error in the implementation of the time calculation. It's been corrected here as if it never happened. If you want to see the error, you will need to refer to the original column.

Calculating New Times and Dates

I happened upon a better way to calculate new times and dates only because I was curious about the EnvAdd command.
EnvAdd, Var, Value [, TimeUnits]

I noticed the "[, TimeUnits]" option at the end of the command and found that "TimeUnits can be either Seconds, Minutes, Hours, or Days (or just the first letter of each of these)." This was the answer I was looking for in the referenced previous column. I could now calculate the time differences with EnvSub command without resorting to the mathematics in the previous script shown here:
    FormatTime, HrTime , %MyTime%, HH
    FormatTime, MinTime , %MyTime%, m
    FormatTime, SecTime , %MyTime%, s
    NewTime := ((HrTime*3600 + MinTime*60 + SecTime) 
              - (A_Hour*3600 + A_Min*60 + A_Sec))*1000

It's important to note that the commands EnvAdd and EnvSub when used with a time parameter yield vastly different types of results and store them into the original variable in different formats. This means that they are not reciprocal operations which can be used to reverse the effects of each other. Plus, if you don't save the original value of the variable in a new variable, that value will be lost. The EnvSub command stores a period of time in the original variable which is not in a date/time format. The EnvAdd command stores a new date (same date/time format) in the original variable after adding the specified seconds (s), minutes (m), hours (h), or days (d).
This TimeUnits option also works with these operators:
Var += Value [, TimeUnits]    ;for EnvAdd
Var -= Value [, TimeUnits]    ;for EnvSub

The TimeUnits option is not available with :=. I could now replace all the previous code above with one line:
EnvSub, NewTime, A_Now, s

For convenience, I moved the microsecond conversion (*1000) to where it was needed in the SetTimer command.

Forcing an Expression

There are times when rather than using a variable within a command, an expression is useful. (This does not apply to output and input variables.) By preceding the expression with a percent sign (%) and a space or tab, the following expression will be evaluated. See "Force an expression" on the Variables and Expression Web page for examples. In this script an expression is forced to calculate the number of microseconds for the SetTimer command.
    SetTimer, ShowReminder, % newtime*1000

Note that no additional percent signs are required around the variable newtime since it is assumed to be a variable.

Making the Reminder Speak

Adding speech to the AutoHotkey Reminder app is done through integrating the NirCmd Speak command into the script. This is done by simply using the AutoHotkey Run command to activate the nircmd.exe program within the new label routine TalkToMe:
TalkToMe:
run, nircmd.exe speak text "%MyNote% %RemMessage%"
Return

Note that AutoHotkey substitutes the value of MyNote and RemMessage for the text between the quotation marks. In this situation (text option), the quotation marks are required for the Speak command to work.
To start the audio, an additional SetTimer command is added to the original ShowReminder label routine.
SetTimer, TalkToMe, 15000

This sets the new label TalkToMe which includes the nircmd.exe speak statement to run starting 15 seconds after the pop-up and every 15 seconds thereafter. This audio read back of the message will continue forever unless there is a way to stop it. Therefore the following code which ends the label routine TalkToMe was added:
IfMsgBox, OK
   SetTimer, TalkToMe, Off
Return

If the OK button is clicked or the pop-up is closed, TalkToMe is turned off.
To implement the entire new Reminder script (which runs with hotkey combination CTRL++R), copy-and-paste the following into a new or current AutoHotkey script:
^#R::

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w150 1, hh:mm tt
Gui, Add, Edit, vMyNote w250, Remind Me!
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

ButtonSubmit:
Gui, Submit, NoHide
NewTime := MyTime

If (MyTime > A_Now)
  {
    EnvSub, NewTime, A_Now, s
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%

    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, % newtime*1000
  }
Else
  {
    EnvSub, NewTime, A_Now, s
    NewTime := NewTime + 86400

    FormatTime, Schedule, %MyTime%, h:mmtt
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`nTomorrow at %Schedule%
    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, % newtime*1000
    }
Return

GuiClose:
Gui, Destroy
Return

ShowReminder:
FormatTime, RightNow
#persistent
SetTimer, TalkToMe, 15000

MsgBox,4160,Your Reminder!, %MyNote% %RemMessage%`n%RightNow%
SetTimer, ShowReminder, Off
 
IfMsgBox, OK
   SetTimer, TalkToMe, Off
return

TalkToMe:
run, nircmd.exe speak text "%MyNote% %RemMessage%"
Return

After the script is loaded, CTRL++R opens the appointment setup window. This version allows you to set a reminder up to 24 hours later and reads the message every 15 seconds after the reminder activates. Adjust the SetTimer, TalkToMe time in the script to vary the interval.
This version of the script (Reminder 1.2.ahk) can be found at the ComputorEdge AutoHotkey Dropbox.

Chapter Twenty-nine: Extending the Reminder AutoHotkey Script Beyond 24 Hours

“Add a calendar and default the date/time GUI to the current time, plus limit the reminder to future dates and time.”
Why not a reminder program which schedules for more than the next 24 hours? Plus, default the GUI to the current date and limit your reminders to the future.
Ron Cerrato from San Diego writes in regards to the Reminder app and QuickLinks app both compiled from AutoHotkey scripts:
The summary says "After the script is loaded, CTRL++R opens the appointment setup window. This version allows you to set a reminder up to 24 hours later." I think it would be much more useful if you could just set an activation date/time for any future date/time. Also, my PC has no Windows key. Is there another key combination that substitutes for it? (I found your QuickLinks program useful in Windows 8 since it doesn't require a Windows key, however even there, I'd much prefer a right click option to show up in the desktop right-click list.)
Ron, you pose some interesting questions. Of course people would like to add a reminder more than 24 hours in advance, plus I hadn't considered the fact that many people don't have a Windows  key on their keyboard. My purpose in writing these apps is to use them as tools for learning how to write and use AutoHotkey app. Therefore, rather than merely making changes to the apps, I will show you how to make changes to the scripts which achieve the results you want.
[The QuickLinks portion of Ron's question is addressed in Chapter Twenty-two.]

Reminders More Than 24 Hours Later

There are a couple of reasons that I limited the reminder to the next 24 hours. The first is the fact that the reminder sits in memory without saving it to disk. That means if you log out, restart, or shutdown the computer, the reminder is lost. It will need resetting when you log in or start up again. What would remind you to set up the reminder again? This can be resolved by saving the reminder data to a text file (Comma Separated Values—CSV), however that technique is a little more advanced and I'll save that for another book. Using a data file would also make it easier to add multiple reminders.
The second reason for initially limiting the time interval was the complications I ran into calculating the time difference in the original script. I later discovered the method built into AutoHotkey for quickly making time difference calculations (EnvSub). In the most recent Reminder.ahk script (Chapter Twenty-eight) a small adjustment can be made to allow the reminder to be set to any time interval. Now extending the reminder beyond 24 hours is easy.
All that is required is a modification of the Gui, Add, DateTime command to make the date as well as the time selectable:
Gui, Add, DateTime, vMyTime w350 1 Range%A_Now%,ddddMMMMd,  yyyy hh:mm tt

The "ddddMMMMd, yyyy" has been added to display the day of the week (dddd), the month (MMMM), the day of the month (d), and the year (yyyy)—all, except the day of the week, can be manually set (see Figure 1). Since the entire date and time is always saved, there are no other changes needed in this most recent Reminder.ahk script. (See the complete Reminder.ahk script at the end of the chapter. This version includes the NirCmd speak function referenced in Chapter Twenty-eight.)
Figure 1. The Reminder app showing the date as well as the time.

Limiting the Date/Time Range

The option "Range%A_Now%" has been added to limit the reminders to future dates and times only (anytime after now—A_Now). The width has been increased to 350 pixels (w350) to accommodate the longer data entry field.
If you would prefer a dropdown selection calendar, then all that's required is the removal of the option "1" located in the middle of the line of code. The "1" option adds the up-down control to the right of the date/time fields replacing the pop up calendar selection box.
Gui, Add, DateTime, vMyTime w350 Range%A_Now%,ddddMMMMd,  yyyy hh:mm tt

Now the reminder displays a calendar when the icon is clicked on the right end of the field (see Figure 2).
Figure 2. Removing the number 1 from the options in the Reminder app adds a calendar selection box.
Now that the reminder is limited to times after the current time, it is possible to remove the If conditional which checks the time, thereby further reducing the script length and complication. However, it is still possible to select a time which is a few seconds earlier than the current time. To prevent that we keep the If conditional in and change the Else to a message saying to pick a later time:
NewTime := MyTime
If (MyTime > A_Now)
  {
    EnvSub, NewTime, A_Now, s
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%

    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, % newtime*1000
  }
Else
  {
    MsgBox, The time must be later than right now!
    GuiControl,,MyTime, %A_Now%
  }

In the Else portion of the If conditional, the MsgBox command is used to display the message and the GuiControl command is used to reset the pop-up time and date to the current time. (Refer to scripts in earlier columns for a complete explanation of this If conditional.)

Reminder.ahk AutoHotkey Script

To run or compile this new Reminder.ahk app which allows future dates, copy-and-paste the following into a new or current AutoHotkey script. If you have not installed NirCmd for the speak function, then you may want to remove the line SetTimer, TalkToMe, 15000 and the associated TalkToMe routine (label).
^#R::

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w350 Range%A_Now%,ddddMMMMd,  yyyy hh:mm tt
Gui, Add, Edit, vMyNote w350, Remind Me!
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

ButtonSubmit:
Gui, Submit, NoHide
NewTime := MyTime
If (MyTime > A_Now)
  {
    EnvSub, NewTime, A_Now, s
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%

    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, % newtime*1000
  }
Else
  {
    MsgBox, The time must be later than right now!
    GuiControl,,MyTime, %A_Now%
  }

Return

GuiClose:
Gui, Destroy
Return

ShowReminder:
FormatTime, RightNow
#persistent
SetTimer, TalkToMe, 15000

MsgBox,4160,Your Reminder!, %MyNote% %RemMessage%`n%RightNow%
SetTimer, ShowReminder, Off
 
IfMsgBox, OK
   SetTimer, TalkToMe, Off
return

TalkToMe:
run, nircmd.exe speak text "%MyNote% %RemMessage%"
Return

Chapter Thirty: Solving More AutoHotkey Reminder Script Issues

“Correcting a time calculation error, check for voice support, disabling the Escape key, and RETURN key problem.”
There is another error in the time calculation, plus what happens if there is no voice support. We need to disable the Escape key, and an accidental pressing of the RETURN key dismisses the reminder.

Cleaning up the Reminder App

Before the Reminder app can be truly useful there are a few more things that need to be done. This includes correcting the time calculation error, blocking the ESCAPE key (on Escape the talking will continue indefinitely until the program is terminated), preventing an accidental ENTER from closing the window when it first pops up, making the app continue working—even when the computer is rebooted, noting when speaking is not available, turning the talking on and off, and possibly a few more improvements.
Over the next few chapters I'll make modifications to the Reminder app to correct problems and enhance its usefulness. I'll explain the process as I go. While these additions will make the script more complicated, each change will demonstrate a technique which might be useful in your other scripts.

Correcting the Time Calculation Error

As mentioned in other chapters, when I started this AutoHotkey reminder app, I make an error in calculating the time interval in seconds. There is a special command which calculates time differences in various increments (days, hours, minutes, seconds), but I made an error in implementing it. Therefore any reminders scheduled with that script won't have an accurate time. If you found the Reminder app inaccurate, that's the reason. Not enough testing was done.
The proper commands are EnvAdd and EnvSub. These commands either add a value to themselves or calculate a difference. The time calculation component is achieved by adding another parameter to designate days, hours, minutes or seconds. When one of these time parameters is used, the value is converted from the date/time format to the new units. The time differences can be calculated with either of the following lines:
EnvSub, Var, Value [, TimeUnits]
Var -= Value [, TimeUnits] 

The line in the Reminder.ahk script has been corrected to:
EnvSub, NewTime, A_Now, s

This command acts on the variable NewTime by finding the difference between it and the current time (A_Now) in seconds (the last parameter "s") and storing it back to NewTime in seconds. It's important to note that NewTime is no longer in the Date/Time format, but has changed to a time interval in seconds. This is why it's necessary to create the new variable, NewTime, from the set time variable, MyTime, prior to the conversion with:
NewTime := MyTime

Otherwise, MyTime which is still needed in the future would be lost.

If NirCmd Is Not Installed for Speaking the Reminder

In its current form the Reminder.ahk script uses the free NirCmd utility to read the reminder message out loud using the Windows voice at the appointed time. However, if NirCmd is not installed, it won't work. To check for installation and prevent the setting of the speech SetTimer when the feature is not available, the following condition is added:
IfExist, %A_WinDir%\nircmd.exe
  {
   SetTimer, TalkToMe, 15000
  }

The app looks in the Windows folder for nircmd.exe. If it's there, the timer is set to the TalkToMe label. This prevents possible errors in the TalkToMe label when the reminder is launched and tries to Run nircmd.exe. This condition will also be used in a future iteration of the Reminder app to help people who want to add the speaking feature to their Windows computer.

Escape Key Does Not Exit App Properly

While testing the Reminder app I noted a couple of problems. The first is after the reminder itself launches in a MsgBox, hitting ESC closes the window, but the speaking feature continues repeating the message. Not only is it obviously annoying, the only way to stop it is to exit the Reminder app completely. The solution is to disable the escape key (ESC) in the reminder message window while the reminder is active.
There are two ways to disable keys for an active window. This technique works for any window—which means you can apply it to any Windows program. The beauty of it is it doesn't affect any other program. The first method is using the Hotkey command.
While the double-colon (::) is used to setup hotkey combinations when a script is first loaded, the Hotkey command is used to create, modify, disable, or enable hotkey combination while a script is running. In this case the following code is added to the ShowReminder label (routine) which displays the reminder box at the appointed time:
Hotkey, IfWinActive, Your Reminder!
Hotkey, Esc, StopEscape
.
.
.
StopEscape:                    ;this label causes the ESC to do nothing while in the reminder window
  Return

The first line of code (Hotkey, IfWinActive, Your Reminder!) tells the app to execute any following Hotkey commands only when the window titled "Your Reminder!" is active. No other active windows will be affected.
The second line establishes a new Hotkey action for the ESC key with the new label StopEscape while the reminder window is active. This new label (StopEscape:), which does nothing (Return), is placed at the end of the script ready to be called when the reminder pops up.
An additional Hotkey, IfWinActive is required in the closing subroutine for Your Reminder! to prevent the first IfWinActive from affecting the ^#R hotkey combination when the first reminder is completed, as such:
IfMsgBox, OK
   SetTimer, TalkToMe, Off
   Hotkey, IfWinActive
Return

This disables the effect of the first Hotkey, IfWinActive, Your Reminder!
The second method is similar to the first method except it is loaded when the script first loads and remains as long as the script is running. This technique uses the #IfWinActive command. The code to do similar disabling of the ESC key would be as follows:
#IfWinActive Your Reminder!  ; The "reminder" windows active
Escape::Enter                ; Change the ESC key to 
#IfWinActive

In this situation the #IfWinActive command applies to every hotkey which follows it in the script. By setting the ESC key to ENTER a proper exit is ensured if you hit the Escape key, thus turning off the voice message. I tried setting the Escape key to do nothing, but while it appears to work in the message box, it caused other strange behavior in the script. The last #IfWinActive prevents the first from affecting any other hotkey combinations in the script.
There are a number of AutoHotkey commands which are preceded with the pound sign (#). These commands are used primarily to establish settings within a script—for example #ClipboardTimeout, #Include (other AutoHotkey scripts), #NoTrayIcon. Among the most commonly used of these types of commands is #IfWinActive/#IfWinExist.

Return Key Prematurely Exits

The second problem I discovered while testing was if I accidentally hit the ENTER key just as the window popped up, I would never see the reminder or hear the audio (see Figure 1). This can happen because the OK button in the reminder window is the default button. This would only occur if I was working on something else when the reminder activated and I happened to end a paragraph with a Return. While not likely, I didn't think that it should be quite that easy to accidentally dismiss the window.
Figure 1. Original Reminder message window.
Using the previous technique of disabling the key was not an option because anyone without a mouse would seemingly be stuck unable to close the window. I needed to change the OK button from the default.
I could have switched over to a special GUI window, but that would have meant more coding—probably also more complicated. Instead I took a closer look at the MsgBox command.
The MsgBox command uses the following format:
MsgBox [, Options, Title, Text, Timeout]

The options are selected by adding up the numbers shown in the AutoHotkey MsgBox Options Chart (see Figure 2). If you want to add an option to a particular message box, merely add the corresponding number to the total of all options.
Figure 2. Configuring MsgBox with options numbers.
For example, in this case I wanted to add the innocuous Help button (it takes no action without special setup) to the message box and make it the default thereby eliminating the accidental ENTER key problem. Adding the number 16384 puts the Help button into the message box and adding 256 makes the second button (Help) the default button. Combine those with the asterisk icon (64) and always-on-top (4096), and the final options number is 20800 as shown:
MsgBox, 20800, Your Reminder!, %MyNote%`n%Schedule%

This displays the message box as shown in Figure 3 with the Help button the default. While the ENTER key will initially do nothing (since the default Help button does nothing), the cursor keys can be used to highlight the OK button activating ENTER to close the window.
Figure 3. Adding Help button and changing its name.
Note that the Help button says "Waiting" rather than "Help." I changed this because the word "Help" would mislead the user into thinking that there was help available. (Maybe later, not now.) The word "Waiting" seems harmless enough.

Changing the Name of the Help Button

Changing the name of help button is complicated by the fact that it can't be done until the message box actually exists. If the command used for changing the text in the Help button, ControlSetText, is the next line after the MsgBox command, then it is likely that it will execute before the message box exists. The Sleep command with a delay of 100 microseconds would probably help, but a more robust method is to have the command wait until the window does exist as follows:
SetTimer, ChangeButtonNames, 50 
MsgBox,20800,Your Reminder!, %MyNote%`n%Schedule%
.
Return
.
ChangeButtonNames: 
  IfWinNotExist, Your Reminder!
    return  ; Keep waiting.
  SetTimer, ChangeButtonNames, off 
  WinActivate 
  ControlSetText, Button2, Waiting, Your Reminder!
return

The label (routine) ChangeButtonNames is set to repeat every 50 microseconds (SetTimer, ChangeButtonNames, 50) just before the MsgBox command is issued. The subroutine ChangeButtonNames: checks for the existence of the message box (IfWinNotExist, Your Reminder!). If it doesn't exist, the label does nothing (Return).
Once the message box comes into existence, the timer is turned off (SetTimer, ChangeButtonNames, off) to prevent further execution, the window is activated (WinActivate), and the Help button text is changed to "Waiting" (ControlSetText, Button2, Waiting, Your Reminder!). The button number is found by activating the Window Spy utility, Chapter Ten, that comes with the AutoHotkey installation and hovering over the button within the active message box.

Disabling Primary Hotkey Combination

Another error can occur if the primary hotkey combination for the Reminder app (CTRL++R) is hit again after it has already launched the reminder setup GUI window (see Figure 4). This is caused because the script is attempting to create new variables that already exist. The easiest way to prevent this error is to disable the original hotkey combination while the window is open.Figure 4. Common error window when AutoHotkey attempts to activate a running routine.
The technique is similar to that mentioned above for the ESC key except this time it is a complete disabling. The first line of the routine (after the hotkey combination is set) is a HOTKEY command disabling the hotkey just used:
^#R
HotKey, ^#R, Off

This remains in effect until the script is exited and the combination is turned back on:
GuiClose:
Gui, Destroy
HotKey, ^#R, On
Return

The possibility of an error from running the routine with the hotkey combination more than one time simultaneously is eliminated.

A Work in Progress

Over the next couple of chapters, I will continue working on the Reminder app. In the meantime, here is the current version of the Reminder.ahk script for copy-and-paste:
^#R::

HotKey, ^#R, Off

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w350 1 Range%A_Now%,ddddMMMMd,  yyyy hh:mm tt
Gui, Add, Edit, vMyNote w350, Remind Me!
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

ButtonSubmit:
Gui, Submit, NoHide
NewTime := MyTime

If (MyTime > A_Now)
  {
    EnvSub, NewTime, A_Now, s
    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%
    #Persistent
    RemMessage := "Better get going!"
    SetTimer, ShowReminder, % NewTime*1000
  }
Else
  {
    MsgBox, The time must be later than right now!
    GuiControl,,MyTime, %A_Now%
  }


Return

GuiClose:
Gui, Destroy
HotKey, ^#R, On
Return

ShowReminder:
FormatTime, RightNow
#persistent

IfExist, %A_WinDir%\nircmd.exe
  {
   SetTimer, TalkToMe, 15000
  }

Hotkey, IfWinActive, Your Reminder!
Hotkey, Esc, StopEscape

SetTimer, ChangeButtonNames, 50 
MsgBox,20800,Your Reminder!, %MyNote% %RemMessage%`n%Schedule%
SetTimer, ShowReminder, Off
 
IfMsgBox, OK
   SetTimer, TalkToMe, Off
   Hotkey, IfWinActive
return

ChangeButtonNames: 
  IfWinNotExist, Your Reminder!
    return  ; Keep waiting.
  SetTimer, ChangeButtonNames, off 
  WinActivate 
  ControlSetText, Button2, Waiting,Your Reminder!
return

TalkToMe:
run, nircmd.exe speak text "%MyNote%"
Return

StopEscape:
  Return

Please let me know if you find any errors.

Chapter Thirty-one: Adding Reminder Setup to System Tray Saving the Reminder to a File

“Adding setup to System Tray and how to make the Reminder app last for days with an INI file.”
Corrections and enhancement for the Reminder app which teach more valuable AutoHotkey techniques, including adding Reminder setup to the System Tray icon and saving your reminder to disk.

More Work on the Reminder App

Each chapter I'm adding a little more to the Reminder app. The original Reminder script was fairly short (although longer than it needed to be). It's now becoming fairly involved. This time more important functionality is added, plus more error correction.
Ron Cerrato found an error in the last script which prevented a second reminder from being set after the first was completed. It was caused by the code added to prevent the ESC key from exiting the reminder while leaving the audio on (discussed in the last chapter):
Hotkey, IfWinActive, Your Reminder!
Hotkey, Esc, StopEscape

It turns out that when Hotkey, IfWinActive is invoked anywhere in a script, it affects all Hotkey commands that may occur after that point. Therefore, the activating hotkey combination CTRL++R was only valid if the Your Reminder! is open—which it isn't.
The solution is to add one line (Hotkey, IfWinActive) to the IfMsgBox, OK subroutine:
IfMsgBox, OK
   SetTimer, TalkToMe, Off
   Hotkey, IfWinActive    ;<= added new line to disable conditional
Return

When Hotkey, IfWinActive is used without any parameters, the original is deactivated no longer affecting subsequent hotkey combinations.

Adding Reminder Setup to the System Tray Icon

Ron also asked for activation be added to the System Tray right-click menu, as well as, the normal CTRL++R. (Ron doesn't have a  key on his keyboard.) It seemed like a good idea, so I looked into it.
Adding an item to the right-click menu is simple enough:
Menu, Tray, Add,Set Reminder,SetReminder

The item "Set Reminder" is added to the bottom of the right-click menu for the Reminder icon in the System Tray (see Figure 1). When selected the SetReminder label is run.
Figure 1. After adding the menu item Set Reminder to the System Tray, a right-click on the icon makes the setup item available.
The easiest way to implement this alternative was to turn the main routine into the label SetReminder. (I tried simulating the ^#R hotkey combination with the menu selection, but I couldn't seem to get that to work.) That means running the same label with both the hotkey combination and the menu selection. The hotkey was changed from:
^#R::
to:
Hotkey, ^#R, SetReminder, On
Return

SetReminder:

This new code creates the hotkey combination CTRL++R which executes the label SetReminder, the same label executed by the menu item. The Return is added to prevent the new label from executing immediately upon loading. The remainder of the routine is turned into a labeled routine by adding SetReminder: to the start of the main routine.
The only other code necessary is
Menu, Tray, Disable, Set Reminder
and
Menu, Tray, Enable, Set Reminder
added at the same locations where the hotkey combination ^#R is turned off and on again while the Reminder Gui is running like so:
HotKey, ^#R, Off
Menu, Tray, Disable, Set Reminder   ;<= new code to disable menu item
and
GuiClose:
Gui, Destroy
HotKey, ^#R, On
Menu, Tray, Enable, Set Reminder   ;<= new code to enable menu item
Return

Turning the main reminder setup routine into a label also increases the future flexibility of the Reminder app for future changes and options. It can be called from almost anywhere in the script.

Defaulting to Reminder Set Time

Another issue is that after you set a reminder, the time shown when you launch the setup again defaults to the current time. It would be more convenient if it showed the time selected for the most recent reminder. This is a simple change since the set time is saved in the variable MyTime. Add the Choose option to the DateTime GUI:
Gui, Add, DateTime, vMyTime w350 1 Range%A_Now% Choose%MyTime%
                          ,dddd,MMMMd,  yyyy hh:mm tt

By adding the Choose option as shown, the DateTime GUI will default to whatever date/time follows the option (no space) in the standard date/time format (YYYYMMDDHHMMSS). Note that options in AutoHotkey commands are often a list with only a space (no commas) separating each option. The order of the options does not matter nor are they case sensitive. Any capital letters are for readability only.

Adding Reminder Persistence Beyond Reboots and Shutdowns with INI Files

The problem with the current form of the Reminder app is that it sits in memory without any backup. If you lose power, you lose your reminder. What's needed is a way to save the data to disk which will automatically reload when the Reminder app comes back to life. As mentioned in the chapter about ScratchPad, INI (initialization) files can be used to save data which will return the app to its last state. In this case, an INI file called Reminder.ini located in the Reminder folder is used to save key information. If the file folder does not exist (first time the app is run), it is created with the following code:
IfNotExist, C:\Users\%A_UserName%\Reminder\
    FileCreateDir, C:\Users\%A_UserName%\Reminder\

It's not necessary to create the file since the first time the IniWrite command is issued, the Reminder.ini file will be created. This occurs after a new reminder is submitted using the label (subroutine) WriteIni:
WriteIni:
     IniWrite, %MyTime%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
 IniWrite, %MyNote%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
Return

Note that the label is saving the value of MyTime (the reminder time) and MyNote (the notes entered into the reminder setup window) to the INI keys Time and Memo respectively under the Section labeled Settings. If you opened the Reminder.ini file in the Reminder folder with Notepad, you would see something similar to the following:
[Settings]
Time=20130219154703
Memo=Remind Me!

Every time those WriteIni commands are issued, the same settings will be updated. The writing needs to occur every time a new reminder is submitted which triggers the ButtonSubmit label. The following line is added to the appropriate condition of ButtonSubmit (when the set time is greater than the current time):
    GoSub, WriteIni

The GoSub command merely calls the label WriteIni as a subroutine. The GoSub command is an easy way to run a label from anywhere in a script. This allows the label to be used multiple times without needing to repeat the code everywhere it's needed. After the label code is executed, the script returns to the same location and continues—unless it encountered a command which caused an exit. (You can see the GoSub line in context in the complete script listing at the end of the chapter.)
Now that the basic data is saved to disk, it's a matter of retrieving it when the Reminder app is loaded with the IniRead command. This is done by adding the following ReadIni label to read the Reminder.ini file:
ReadIni:
    IniRead, RemTime, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
   IniRead, RemMemo, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
Return

This is the reverse process of the IniWrite command saving the keys Time and Memo from the section Settings to the new variables RemTime and RemMemo respectively. To set up the saved reminder the following conditional are added at the beginning of the Reminder.ahk script:
IfExist, C:\Users\%A_UserName%\Reminder\Reminder.ini
  {
    GoSub, ReadIni
    If(RemTime > A_Now)
      {
        MyTime := RemTime
       MyNote := RemMemo

        GoSub, ButtonSubmit
 
      }
     Else
      {
        MyTime := A_Now
       MyNote := "Remind Me!"
      }
  }

The IfExist command is used to check for the file. This is only needed for the first run of the script, but it doesn't hurt to have it in there for all runs—just in case the INI file has been deleted.
The first step is to read the file (GoSub, ReadIni). Then, if the reminder is for a time later than now, MyTime and MyNote are set to the saved values and the reminder is reset (GoSub, ButtonSubmit). If the reminder time has already lapsed, then MyTime and MyNote are set to defaults and the old values are ignored. You could make the lapsed reminder display (just in case you forgot it completely) by adding the appropriate GoSub to the second part of the condition. However it would then display every time you loaded the app, regardless of the last time you used it. (A solution to that problem would be to zero out your saved INI data when the Your Reminder! window displays, then not execute the reminder with blank data.)
This INI file can now be used in future versions of the Reminder app as more features are added.

The Complete Reminder Script

The Reminder app script is now available in our AutoHotkey Dropbox as Reminder 5.1.ahk and is listed here for copy-and-paste:
Menu, Tray, Add,Set Reminder,SetReminder

IfExist, C:\Users\%A_UserName%\Reminder\Reminder.ini
  {
    GoSub, ReadIni
    If(RemTime > A_Now)
      {
        MyTime := RemTime
     MyNote := RemMemo

        GoSub, ButtonSubmit     
      }
     Else
      {
        MyTime := A_Now
       MyNote := "Remind Me!"
      }
  }

IfNotExist, C:\Users\%A_UserName%\Reminder\
    FileCreateDir, C:\Users\%A_UserName%\Reminder\

Hotkey, ^#R, SetReminder, On

Return

SetReminder:

HotKey, ^#R, Off
Menu, Tray, Disable, Set Reminder

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w350 1 Range%A_Now% Choose%MyTime%,dddd,MMMMd,  yyyy hh:mm tt
Gui, Add, Edit, vMyNote w350, %MyNote%
Gui, Add, Button, Default, Submit
Gui, Show,  , Reminder
Return

ButtonSubmit:
Gui, Submit, NoHide
NewTime := MyTime

If (MyTime > A_Now)
  {
    EnvSub, NewTime, A_Now, s

    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%

    #Persistent
    RemMessage := "Better get going!"
    GoSub, WriteIni
    SetTimer, ShowReminder, % NewTime*1000
  }
Else
  {
    MsgBox, The time must be later than right now!
    GuiControl,,MyTime, %A_Now%
  }

Return

GuiClose:
Gui, Destroy
HotKey, ^#R, On
Menu, Tray, Enable, Set Reminder
Return

ShowReminder:
FormatTime, RightNow
#persistent

IfExist, %A_WinDir%\nircmd.exe
  {
   SetTimer, TalkToMe, 15000
  }

Hotkey, IfWinActive, Your Reminder!
Hotkey, Esc, StopEscape

SetTimer, ChangeButtonNames, 50 
MsgBox,20800,Your Reminder!, %MyNote% `n%Rightnow%
MyNote := "New Reminder!"
SetTimer, ShowReminder, Off
 
IfMsgBox, OK
   SetTimer, TalkToMe, Off
   Hotkey, IfWinActive
return

ChangeButtonNames: 
  IfWinNotExist, Your Reminder!
    Return  ; Keep waiting.
  SetTimer, ChangeButtonNames, off 
  WinActivate 
  ControlSetText, Button2, Waiting,Your Reminder!
Return

TalkToMe:
run, nircmd.exe speak text "%MyNote%"
Return

StopEscape:
  Return

WriteIni:
      IniWrite, %MyTime%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
 IniWrite, %MyNote%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
Return

ReadIni:
       IniRead, RemTime, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
   IniRead, RemMemo, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
Return

Please let me know about any errors you find.

Chapter Thirty-two: Check for Voice, Turn It On and Off, and Change the Cursor

“Is NirCmd installed?; turning voice on and off; and change the cursor to a pointing press finger.”
In this wrap up of the current state of the Reminder script, features are added to make the app a little more useable.
I'm getting closer completing the Reminder app—that's if an app can ever be truly finished. (Adding even more features to the Reminder app will be included in ComputorEdge columns and a future e-book. For now, beyond what's included in this chapter, it's up to the reader to make improvement and additions to the Reminder app.) There are a couple more features to add, each of which has some interesting learning points. Since, if the free Windows utility nircmd.exe is installed, the app is capable of reading the reminder out loud, the user should be told whether the voice is available. Plus, if speaking is available, there should be a way to toggle it on and off. (You may not want your computer nagging you.) Lastly, some code is added to the Reminder app to change the regular cursor into a push button finger when it hovers over the voice toggle.

Checking for the Nircmd.exe Speaking Program

Since the Reminder app does have the capability to use the Nircmd speaking feature, it seems reasonable to add a message about its availability on the Windows computer. Looking at the reminder setup GUI window, there's space in the lower right-hand corner for informational text (see Figure 1).
Figure 1. There's space in the lower right-hand corner of the reminder GUI for information about the speaking feature.
If the speech capability is available, the file nircmd.exe will be located in the Windows folder. The following conditional is needed to first check for the existence of the nircmd.exe file, then add the text to the GUI:
IfExist, %A_WinDir%\nircmd.exe
  {
   Gui, Font, s10
   If(SetSpeak = 1)
     Gui, Add, Text, cGreen X+150 yp+10 vSpeak gSpeakToggle, Speaking On
   Else
     Gui, Add, Text, cRed X+150 yp+10 vSpeak gSpeakToggle, Speaking Off
  }
Else
  {
   Gui, Font, underline s10
   Gui, Add, Text, cRed x+150 yp+10 gNircmd, Speaking Not Available
  }

Of interest at this point is the ELSE portion of the conditional (nircmc.exe does not exist). The first line of code (IfExist, %A_WinDir%\nircmd.exe) looks for the file nircmc.exe in the Windows folder. If it's found, then the speaking capability is available. If not, the script jumps to the ELSE subroutine. The first line of the ELSE subroutine (Gui, Font, underline s10) set the font size to 10 points and underlines (to indicate that it's also a link) (see Figure 2).
Figure 2. When the Nircmd capability is not found, "Speaking Not Available" is displayed.
The second line of code in the ELSE section adds the text to the window with the GUI, ADD, TEXT command. In this line the color option is set to red (cRed) and the text is positioned within the GUI with (x+150 yp+10). These options are explained in the "Positioning and Sizing of Controls" section of the online AutoHotkey GUI help page. I had to play around with the numbers to get it where I wanted it. I've found that figuring out how to align text and buttons in AutoHotkey controls is not always easy and, at times, I've had to employ some formatting tricks.
The label (Nircmd:) is created with the gNircmd option. The label (subroutine) is as follows:
Nircmd:
  MsgBox, 4,Download Nircmd.exe?,You don't have Nircmd.exe utility`nfor activating speech installed
                                                          .`nWould you like to go to the Web site?
  IfMsgBox Yes
      Run http://www.nirsoft.net/utils/nircmd.html
Return

The label Nircmd pops up a message window asking if you would like to download Nircmd. If not, the window will close. If you click the "Yes" button (IfMsgBox Yes), your default browser will open and navigate to the proper site (Run http://www.nirsoft.net/utils/nircmd.html). It is then the user's option to download and install the utility which merely puts the executable nircmd.exe in the Windows folder.
Note that the second line of the MsgBox starts with the period (.) from the first line. This is merely a convenience for writing and viewing the code. When the next line is preceded by a punctuation mark, it is considered part of the prior line. This is common usage in AutoHotkey.

Turning Speaking On and Off

If the file nircmd.exe is found, then options have been added to toggle the feature on and off. You may not always want an audible alert. If the "IfExist, %A_WinDir%\nircmd.exe" statement evaluates as true, then the first part of the conditional is executed.
In the last chapter, the Reminder.ini file was added to the script to save the latest reminder. Since the INI file already exists, it's simple enough to add the line:
IniRead, SetSpeak, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak

to the ReadIni: label from last week. This makes it possible to save and recall the last setting. The variable used to save the setting for speaking is SetSpeak.
The first time through, IniRead will not find the variable SetSpeak since it has not yet been written to the INI file. Therefore when the second conditional (If(SetSpeak = 1)) is encountered SetSpeak will not equal 1 and the ELSE condition will be executed. The text color is set to red (cRed) and the text is set to "Speaking Off." This will remain the case until the first time the text is clicked and the label ToggleSpeak (added with the gSpeakToggle option) is executed. The variable vSpeak has been added as an option merely to make changing the text with the GUICONTROL command easier.
To turn on the speaking capability, click the text "Speaking Off" to launch the label SpeakToggle.
The speak toggle (SpeakToggle:) is implemented with the following code:
SpeakToggle:
 If(SetSpeak = 1)
 {
  SetSpeak := 0
  Gui,Font,Cred
  GuiControl,Font,Speak
  GuiControl,,Speak, Speaking Off
 }
 Else
 {
  SetSpeak := 1
  Gui,Font,Cgreen
  GuiControl,Font,Speak
  GuiControl,,Speak, Speaking On  ;Alt 0160 hard space before On
 } 
  IniWrite, %SetSpeak%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak
Return

The first time this conditional is encountered the variable SetSpeak does not exist and can't equal 1. Therefore the script jumps to the ELSE portion of the conditional. SetSpeak is set to 1 (SetSpeak := 1). The font color is changed to green (Gui,Font,Cgreen), then the GuiControl command is used to set that font color to the control Speak. The same control is then set to the new text "Speaking On."
For some reason, the word "On" would not display when executing this portion of the conditional, but placing a hard space (hold down the ALT key while entering 0160 with the number keypad to enter a hard space before On) before the word "On" in the code seemed to fix it. (Bug? I didn't have the problem on the "Off" side of the conditional.) If you copy-and-paste this code rather than downloading it from the Dropbox page (Reminder 1.6.ahk), you may need to add the hard space yourself.
The Reminder setup windows will now show the "Speaking On" link (see Figure 3).
Figure 3. The Reminder setup window shown with the speaking feature on.
The last line of code uses the IniWrite command to save the new SetSpeak value to the INI file.
If you click the now green text, it will enter the first part of the conditional and execute the opposite of what appears above and disabling the speaking feature.
To put the selected setting into effect when a reminder is displayed, another conditional needs to be added to the ShowReminder label in the original script:
IfExist, %A_WinDir%\nircmd.exe
  {
    If SetSpeak = 1     ;new line
     SetTimer, TalkToMe, 15000
  }

This double conditional will only set the speaking timer if SetSpeak is set to 1.

Changing the Cursor to a Pointing Finger

One of the more powerful features of AutoHotkey is its ability to access built-in Windows features. In this case, I wanted to change the cursor to a pointing finger whenever it hovered over the new text speaking toggle link. This would make the text link even more obvious as a place to click (see Figure 4).Figure 4. The cursor in the Reminder setup window changes to a pointing finger when hovering over the speaking toggle.
This is one of the more advanced features of AutoHotkey which I found while digging around examples. I'm sure that there is much more than merely changing cursors which can be accomplished with these techniques, but I haven't had time to look into the possibilities. Until I start exploring more advanced AutoHotkey features in this column (there is plenty of intermediate level work to do), I will only offer an overview of the following code and attest to the fact that it works:
IDC_HAND := 32649
hCursor := DllCall("LoadCursor", "UInt", NULL,"Int", IDC_HAND, "UInt")
OnMessage(0x200,"WM_MOUSEMOVE")

WM_MOUSEMOVE(wParam, lParam) {
  global hCursor
  MouseGetPos,,,, ctrl
 if (ctrl == "Static2")
  DllCall("SetCursor","UInt",hCursor) ; To set the cursor
}

The first three lines of code I copied unaltered from an example. The AutoHotkey OnMessage command is used to call the standard Windows message WM_MOUSEMOVE found in the List of Windows Messages. The WM_MOUSEMOVE function uses the data setup hCursor and IDC_HAND to make a DllCall—which is a little cryptic. The only value that I added was the "Static2" ( if (ctrl == "Static2")) which designates the Speak control on the GUI window. (I found that value with the Window Spy utility, Chapter Ten, by hovering over the speaking text link in the GUI window.)
It works, but I'll have to save deeper investigation for more advanced level scripting—when I get that far along myself.
The last piece of the puzzle is adding a line to the GuiClose label to "Destroy" the cursor (another DllCall):
GuiClose:
  Gui, Destroy
  HotKey, ^#R, ON
  Menu, Tray, Enable, Set Reminder
  DllCall("DestroyCursor","Uint", hCursor) ;line added to remove hand cursor
Return

Whew!

The Final Reminder App Script

I'm not sure that scripts are ever completed. I plan to eventually make this Reminder script capable of setting multiple simultaneous reminders, but for now I'll be moving on to creating a simple database and calculating ages. In the next part, I discuss a GrandKids app which maintains a simple database of grandchildren and calculates all of their ages from years down to the months and days. The techniques are useful in many different apps, although creating the function to calculate their ages was not as easy as it might seem.
Here is Reminder 1.6.ahk which can also be found on the Web for download:
Menu, Tray, Add,Set Reminder,SetReminder

IfExist, C:\Users\%A_UserName%\Reminder\Reminder.ini
  {
    GoSub, ReadIni
    If(RemTime > A_Now)
      {
        MyTime := RemTime
        MyNote := RemMemo
 
        GoSub, ButtonSubmit    
      }
     Else
      {
        MyTime := A_Now
       MyNote := "Remind Me!"
      }
  }

IfNotExist, C:\Users\%A_UserName%\Reminder\
    FileCreateDir, C:\Users\%A_UserName%\Reminder\

Hotkey, ^#R, SetReminder, On

Return

SetReminder:

HotKey, ^#R, Off
Menu, Tray, Disable, Set Reminder

Gui, Font, s12, Arial
Gui, Add, Text,, 1. Select time`n2. Add note`n3. Submit`n4. Close popup
Gui, Add, DateTime, vMyTime w350 1 Range%A_Now% Choose%MyTime%,dddd MMMMd, yyyy hh:mm tt
Gui, Add, Edit, vMyNote w350, %MyNote%
Gui, Add, Button, Default, Submit
IfExist, %A_WinDir%\nircmd.exe
  {
   Gui, Font, s10
   If(SetSpeak = 1)
     Gui, Add, Text, cGreen X+150 yp+10 vSpeak gSpeakToggle, Speaking On
   Else
     Gui, Add, Text, cRed X+150 yp+10 vSpeak gSpeakToggle, Speaking Off
  }
Else
  {
   Gui, Font, underline s10
   Gui, Add, Text, cRed x+150 yp+10 gNircmd, Speaking Not Available
  }

IDC_HAND := 32649
hCursor:=DllCall("LoadCursor", "UInt", NULL,"Int", IDC_HAND, "UInt")
OnMessage(0x200,"WM_MOUSEMOVE")



Gui, Show,  , Reminder
Return




ButtonSubmit:
Gui, Submit, NoHide
NewTime := MyTime

If (MyTime > A_Now)

  {
    EnvSub, NewTime, A_Now, s

    FormatTime, Schedule, %MyTime%
    MsgBox,4160,Your Reminder, "%MyNote%" is scheduled for`n%Schedule%

    #Persistent
    RemMessage := "Better get going!"
    GoSub, WriteIni
    SetTimer, ShowReminder, % NewTime*1000
  }
Else
  {
    MsgBox, The time must be later than right now!
    GuiControl,,MyTime, %A_Now%
  }
Return

GuiClose:
  Gui, Destroy
  HotKey, ^#R, ON
  Menu, Tray, Enable, Set Reminder
  DllCall("DestroyCursor","Uint", hCursor)
Return

WM_MOUSEMOVE(wParam, lParam) {

global hCursor

MouseGetPos,,,, ctrl

if (ctrl == "Static2")

  DllCall("SetCursor","UInt",hCursor) ; To set the cursor

}

ShowReminder:
FormatTime, RightNow
#persistent

IfExist, %A_WinDir%\nircmd.exe
  {
    If SetSpeak = 1     ;new line
     SetTimer, TalkToMe, 15000
  }

Hotkey, IfWinActive, Your Reminder!
Hotkey, Esc, StopEscape

SetTimer, ChangeButtonNames, 50 
MsgBox,20800,Your Reminder!, %MyNote% %RemMessage%`n%Rightnow%
MyNote := "New Reminder!"
SetTimer, ShowReminder, Off
 
IfMsgBox, OK
   SetTimer, TalkToMe, Off
   Hotkey, IfWinActive
return

ChangeButtonNames: 
  IfWinNotExist, Your Reminder!
    return  ; Keep waiting.
  SetTimer, ChangeButtonNames, off 
  WinActivate 
  ControlSetText, Button2, Waiting,Your Reminder!
return

TalkToMe:
run, nircmd.exe speak text "%MyNote%"
Return

StopEscape:
  Return

WriteIni:
  IniWrite, %MyTime%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
 IniWrite, %MyNote%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
 IniWrite, %SetSpeak%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak
Return

ReadIni:
 IniRead, RemTime, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Time
   IniRead, RemMemo, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, Memo
   IniRead, SetSpeak, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak
Return

Nircmd:
  MsgBox, 4,Download Nircmd.exe?,You don't have Nircmd.exe utility
        ,`nfor activating speech, installed
     .`nWould you like to download it?
  IfMsgBox Yes
     Run http://www.nirsoft.net/utils/nircmd.html
Return

SpeakToggle:
 If(SetSpeak = 1)
 {
  SetSpeak := 0
  Gui,Font,Cred
  GuiControl,Font,Speak
  GuiControl,,Speak, Speaking Off
  IniWrite, %SetSpeak%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak
 }
 Else
 {
  SetSpeak := 1
  Gui,Font,Cgreen
  GuiControl,Font,Speak
  GuiControl,,Speak, Speaking On       ;Alt 0160 hard space before On
  IniWrite, %SetSpeak%, C:\Users\%A_UserName%\Reminder\Reminder.ini, Settings, SetSpeak
 } 
Return

Please let me know it you find any errors.

Part VIII: Introduction to Databases, Calculating Ages, and Formatting Problems

“A quick app which calculates the age of grandkids uses an INI database and special time calculation function.”
While introducing an age calculation function, an INI file is used as a database of grandchildren. The formatting and placement of controls in a GUI object is explained.
In Part VIII the GrandKids app is used as an introduction to databases, a look at calculating ages and options for formatting GUI controls. The GrandKids app only shows how an INI file can be used to store data. After a great deal of mind bending, an age calculation function is written. Finally, after a look back two ways to format text in GUIs are shown—the hard way and the easy way.
Chapter Thirty-three: A GrandKid's Age Calculating App
Remembering your grandchildren's ages with an INI database.
Here is how to write an app with a simple AutoHotkey database to remember how old your grandkids are.
Chapter Thirty-four: The Age Calculating Function
A function for dealing with the complications of calculating years, months and days since birth.
For the AutoHotkey obsessed user, the function for age calculation is explained.
Chapter Thirty-five: Text Formatting in GUIs
Text positioning options in AutoHotkey GUI windows explained.
AutoHotkey has tools for positioning and sizing controls in AutoHotkey windows, but you need to understand how they work.

Index to Part VIII: Introduction to Databases, Calculating Ages, and Formatting Problems

#SingleInstance, Chapter Thirty-three
A_Index, Chapter Thirty-three
Age calculation, Chapter Thirty-four
Array index (A_Index), Chapter Thirty-three
Calculating, if leap year, Mod(), Chapter Thirty-four
Concatenating (adding) strings (text), Chapter Thirty-five
Database, simple AutoHotkey, Chapter Thirty-three
EnvAdd and EnvSub, difference in time calculation, Chapter Thirty-four
EnvSub commandChapter Thirty-four
FormatTime commandChapter Thirty-three
Formatting options chart, GUI, Chapter Thirty-five
Formatting text, Chapter Thirty-five
Function, HowOld(), Chapter Thirty-threeChapter Thirty-four
Functions described, what are they?, Chapter Thirty-four
Mod(), remainder function, Chapter Thirty-four
Modulo, Chapter Thirty-four
Padding strings, Chapter Thirty-four
Parsing dates, Chapter Thirty-four
Remainder function, Mod(), Chapter Thirty-four
Section in GUI formatting, Chapter Thirty-five
SubStr function,Chapter Thirty-fourChapter Thirty-five
SubStr, parsing dates, Chapter Thirty-four
SubStr, using to pad characters (left or right), Chapter Thirty-four
Tabs, (`t), Chapter Thirty-five
Ternary Operator, one-line IF statement, Chapter Thirty-four
Time difference calculation, Chapter Thirty-four
Understanding formatting GUI controls, Chapter Thirty-five

Chapter Thirty-three: A GrandKid's Age Calculating App

“Remembering your grandchildren's ages with an INI database.”
Here is how to write an app with a simple AutoHotkey database to remember how old your grandkids are.

A Simple AutoHotkey Database

Some of the most powerful software applications use databases to store information. When information is saved to a more permanent form on a hard drive or other persistent storage, it is often in the form of a database. What distinguishes a database from other saved files (txt, doc, jpg, etc.) is the use of records of consistent data types with common fields making it easier to search, sort, and report on those fields. The address book in your e-mail program is an example of a database.
Databases are not needed for most of the AutoHotkey apps we've discussed so far. AutoHotkey has a powerful set of commands which adds tools to your Windows computer without resorting to saved data. Most of the apps offered so far in this book are not dependent upon data stored on the computer. One exception would be the latest version of the Reminder app which saves the current reminder to disk, although in its present form it is a database of only one record. In order to expand the Reminder app to handle multiple reminders, the app would need a true database with multiple records.
If you want to add more power to your AutoHotkey apps, you can add simple databases. There are many times when a small database will solve a problem. (If you're working on really big problems, then AutoHotkey may not be the program for you. Sometimes it's better to investigate dedicated database systems (Microsoft Access, SQL, etc.) which have the tools for structuring files and handling the sorting and reporting built-in, rather than trying to make a utility scripting language into a database manager. It can be done, but you will need to build most of the tools yourself.)

Using INI Files for Simple Databases in AutoHotkey

While there are always multiple ways to solve problems in any programming language, I look for the simplest way first. In the case of AutoHotkey, one of the easiest ways to set up a database is with an INI file. The reason for this is AutoHotkey already has commands for reading from (IniRead) and writing to (IniWrite) INI files. It assumes a basic, flexible structure which makes it ideal for simple applications. If I were looking at more complicated apps with larger databases, I may prefer using Comma Separated Values (CSV) files, then parsing the records of data for access. AutoHotkey does have commands which make it easier to work with CSV files, but the scripting would be more complicated. For now, if an INI file will do the job, I'm going to use it. (See Chapter Thirty-one for more details on using INI files to save data.)
The GrandKids AutoHotkey App
In my example, I set up a database of grandchildren which includes their names and birthdays. When you have a number of grandchildren it can be difficult to remember how old each one is. In my GrandKids app, a database of grandchildren is read from an INI file with the current age of each being calculated on the fly, then displayed in an AutoHotkey window (See Figure 1).
Figure 1. The GrandKids.ini file is read, ages calculated, then displayed.
As it turned out the simplest part of the GrandKids app was setting up and reading the database. The formatting of the GUI window and writing the age calculation function was much more problematic.

Setting Up the GrandKids.ini File

In the Reminder app the script automatically set up the INI file used to save the current reminder settings. Since the GrandKids.ahk script doesn't do that, you'll need to create the GrandKids.ini file yourself or you can copy my sample INI file, GrandKids.ini:
[Settings]
Number=6
[Grand1]
First=Liam
Second=Patrick
Last=Smith
Birthday=20080229
[Grand2]
First=Seamus
Second=Alan
Last=Smith
Birthday=20081103
[Grand3]
First=Freyja
Second=Lynn
Last=Smith
Birthday=20091007
[Grand4]
First=Eveline
Second=Elizabeth
Last=Smith
Birthday=20111231
[Grand5]
First=Lyla
Second=Emilia
Last=Jones
Birthday=20120214
[Grand6]
First=Baby
Second=Girl
Last=Smith
Birthday=20130228

Copy-and-paste the above lines into a text editor (Notepad, etc.) and save it to a file named GrandKids.ini in a new GrandKids folder created in your My Documents folder. Since the data will rarely, if ever, change, it is easier to directly edit the INI file than it would be to add editing and writing features to the app. (We'll save editing and writing the INI file for a future version of the app in another book.) It's important to create the INI file in the proper location before running the script since it will not display any information without this data file in place.
There are two parts to an INI file. The first is the section which may equate to an individual record (although it may not). A section is designated by the square brackets ([]) which surround the section name. In the above data file the sections are [Settings], [Grand1], [Grand2], [Grand3], [Grand4], [Grand5], and [Grand6], respectively. When the IniRead and IniWrite commands are used, the section name tells AutoHotkey where to look in the INI file for the specific data.
Listed under each section are the keys which store the data. These equate to the fields in a database, although they can be anything. While the keys for each section starting with "Grand" are the same (First, Second, Last, and Birthday) it is quite possible to include varying keys—as long as the script knows how to find the data. The first section ([Setting]) is there to tell AutoHotkey how many records (the key Number) are in the database. This is important for the app to have a way to know how many records exist, since it will only look for that number of grandchildren. Be sure to adjust your INI file key Number for the number of records you included.
The format of each key is "Key=Value" where "Key" is the key name and "Value" is the value of the data. For example, in the section Grand1First (first name) has a value of "Liam", Second (middle name) has a value of "Patrick", Last (last name) has a value of "Smith", and Birthday has a value of "20080229" in the standard year, month, day format. All of this data must be entered by hand since the since the script does not currently have the IniWrite capability. Frankly, for this app it's easier to edit the INI file than it would be to add the editing features in AutoHotkey.
Once the GrandKids.ini file is set up, the app is ready to run.

Reading the GrandKids.ini Data File

The AutoHotkey code for reading the GrandKids.ini file is relatively short and simple:
IniRead, Howmany, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Settings, Number

Loop, %Howmany%
  {
    IniRead, First, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, First
    IniRead, Second, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Second
    IniRead, Last, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Last
    IniRead, Birthday, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Birthday

    HowOld(Birthday,A_Now)

    FormatTime, Birthday , %Birthday%, dddd, MMMM d, yyyy
    RowSpace := First . " " . Second . " " . Last . "                    "
    RowSpace := SubStr(RowSpace,1,35)
    BDSpace  := Birthday . "                         "
    BDSpace := SubStr(BDSpace,1,40)
    Gui, Add, Text, , %RowSpace%`t%BDSpace%`t%Years% Years, %Months% Months, %Days% Days
  }

FormatTime, Now_Time, A_Now , Longdate
Gui, Show, w575 ,Grand Kids -- %Now_Time%

In the first line of code the IniRead command is used to retrieve the number of records in the INI file GrandKids.ini. The Number key value (6) under the Settings section is stored to the variable HowMany.
The second line of code starts a normal loop (Loop, %Howmany%) to read the individual records. It is set to repeat HowMany times.
As the script loops through the INI file the IniRead command is used retrieve the data for each record by using the loop variable A_Index (the number of the loop iteration) to create the appropriate section name (Grand%A_Index%) for each data record. For example, on the second pass through the loop A_Index would hold the value of 2. Grand%A_Index% then becomes "Grand2", the name of the second "Grand" section, thereby retrieving the second set of data.
Once the data is retrieved for the current record, the birthday is sent to the HowOld() function to calculate the current age, the data is formatted and added to the GUI with the Gui, Add, Text command. I won't go into detail about the age calculation function, HowOld(), or the GUI formatting at this time. Both have enough complications to be the subject of another chapter.
Once the loop is completed, the last line uses the Gui, Show command to display the accumulated and calculated information shown in Figure 1. The entire code for the GrandKids app is as follows:
#SingleInstance Force
IniRead, Howmany, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Settings, Number

Loop, %Howmany%
  {
    IniRead, First, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, First
    IniRead, Second, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Second
    IniRead, Last, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Last
    IniRead, Birthday, C:\Users\%A_UserName%\Documents\GrandKids\GrandKids.ini, Grand%A_Index%, Birthday

    HowOld(Birthday,A_now)

    FormatTime, Birthday , %Birthday%, dddd, MMMM d, yyyy
    RowSpace := First . " " . Second . " " . Last . "                    "
    RowSpace := SubStr(RowSpace,1,35)
    BDSpace  := Birthday . "                         "
    BDSpace := SubStr(BDSpace,1,40)
    Gui, Add, Text, , %RowSpace%`t%BDSpace%`t%Years% Years, %Months% Months, %Days% Days
  }

FormatTime, Now_Time, , Longdate
Gui, show, w575 ,Grand Kids -- %Now_Time%


HowOld(FromDay,ToDay)
  {
    Global Years,Months,Days

; If born on February 29
If SubStr(FromDay,5,4) = 0229 and Mod(SubStr(ToDay,1,4), 4)
      != 0 and SubStr(ToDay,5,4) = 0228
     PlusOne = 1

; To calculate this month's length set ThisMonth to current year and month (yyyymm)
    ThisMonth := SubStr(ToDay,1,6)

; Step 1. Set ThisMonthLength equal to next month
    ThisMonthLength := % SubStr(ToDay,5,2) = "12" ? SubStr(ToDay,1,4)+1 . "01"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)+1,-1)

; Step 2. Convert ThisMonthLength from date to days saved in ThisMonthLength
    EnvSub, ThisMonthLength, %ThisMonth%, d

; Set ThisMonthDay to FromDay or (if FromDay higher) last day of this month
    If SubStr(FromDay,7,2) > ThisMonthLength
        ThisMonthDay :=  ThisMonth . ThisMonthLength
    Else
        ThisMonthDay :=  ThisMonth . SubStr(FromDay,7,2)

; Step 1 to calculate last month's length
    LastMonthLength := % SubStr(ToDay,5,2) = "01" ? SubStr(ToDay,1,4)-1 . "12"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)-1,-1)
    LastMonth := LastMonthLength

; Step 2 days in last month saved in LastMonthLength
    EnvSub, LastMonthLength, %ThisMonth% ,d
    LastMonthLength := LastMonthLength*(-1)

; Set LastMonthDay to FromDay or (if FromDay higher) last day of last month
    If SubStr(FromDay,7,2) > LastMonthLength
        LastMonthDay :=  LastMonth . LastMonthLength
    Else
        LastMonthDay :=  LastMonth . SubStr(FromDay,7,2)

; Calculate Years
    Years  := % SubStr(ToDay,5,4) - SubStr(FromDay,5,4) 
            < 0 ? SubStr(ToDay,1,4)-SubStr(FromDay,1,4)-1 
            : SubStr(ToDay,1,4)-SubStr(FromDay,1,4)
 
; Calculate Months
    Months := % SubStr(ToDay,5,2)-SubStr(FromDay,5,2) 
            < 0 ? SubStr(ToDay,5,2)-SubStr(FromDay,5,2)+12 
            : SubStr(ToDay,5,2)-SubStr(FromDay,5,2)

    Months := % SubStr(ToDay,7,2) - SubStr(ThisMonthDay,7,2) < 0 ? Months -1 : Months
    Months := % Months = -1 ? 11 : Months

; Calculate Days
    TodayDate := SubStr(ToDay,1,8)          ; Remove any time portion of date/time
    EnvSub, ThisMonthDay,ToDayDate , d
    EnvSub, LastMonthDay,ToDayDate , d

    Days  := % ThisMonthDay <= 0 ? -1*ThisMonthDay : -1*LastMonthDay

; If February 28 
    Years := % PlusOne = 1 ? Years +1 : Years
    Days := % PlusOne = 1 ? 0 : days

    If (TodayDate <= FromDay)
       Years := 0, Months := 0,Days := 0
  }

If you edit the GrandKids.ini file to suit your needs then you will have a reminder list of the ages (or years married) of your grandkids, kids, friends, or anyone else you have in mind.

GrandKids App Notes

The GrandKids app does not use a hotkey combination. It immediately displays the window when it is run. If you want to display it again, you need to either right-click on the System Tray icon (green box with H inside) and select Reload This Script or reload the program itself. Adding the line #SingleInstance Force to the script prevents the error if the script is already loaded and reloads.
If you want to add a hotkey combination to launch the app or turn it into a label to run in another app, The combination or label name would be added at the beginning of the script and the Return command should be added just before the HowOld() function.
It took me a little while to come up with the HowOld() function for calculating age. I had a number of false starts. I couldn't find a good AutoHotkey age calculation function on the Web, but I think this one does the job. If you find any errors, please let me know. I want to get it right. This age calculation function is the subject of Chapter Thirty-four
Formatting the GUIs is not always easy. I went through some gymnastics on this one. Although this approach seems to work, there is an easier way to do it which is covered in Chapter Chapter Thirty-five.
I've added both the GrandKids5.ahk file and the GrandKids.ini sample file to the AutoHotkey Dropbox for downloading.

Chapter Thirty-four: The Age Calculating Function

“A function for dealing with the complications of calculating years, months and days since birth.”
For the AutoHotkey obsessed user, the function for age calculation is explained.

Calculating Ages

In the last chapter I introduced the GrandKids app which used AutoHotkey to calculate the age of grandchildren (or anyone else). All that's needed is the date of birth which is stored in an INI file. It uses a function I wrote for making the calculations. I must admit that it took me some time and a number of mental gymnastics to finally get an age calculating function that works. (If you find a problem with it, please let me know.) It is much shorter than most of the other age functions I found, although I don't know if it's more accurate. It's worth the time to review this function because it gives a better understanding of the issues of working with dates, plus a few other cool AutoHotkey techniques.
Note: In the original ComputorEdge columns I postponed this discussion because as I was going through the function I found some bonehead mistakes. I have since poured over the code correcting a number of errors plus finding better ways to accomplish some of the calculations tasks.

What Is a Function?

An AutoHotkey Function is similar to a subroutine or a label which is a self-contained set of code. Functions are visually distinguished by the set of parentheses that immediately follow the function name (FunctionName() or FunctionName(Para1,Para2)). Functions differ from subroutines in that you may pass data to a function via parameters (the items between the parentheses) or pass no data at all, but the parentheses must be there. Part of the power of functions is that variables are temporarily created as local values which will not affect the main routine. This makes it possible to use the same function in many different scripts with little or no tailoring of the function. A function may return values to the main routine or merely perform a repetitive task. Writing functions often eliminates the need to add repetitive code to a script since any time the code is needed it can be called with the function. It is often possible to use a function for routines where AutoHotkey labels do not work—especially if variable names are dynamically changing within the script.
A function can be called in AutoHotkey by simply placing it in the script as a line of code as is done in the GrandKids app:
HowOld(Birthday,A_now)

where Birthday is just that and A_now is the current date. Function can also be called by setting a variable to a returned value (Variable =: Function()). The command "Return x" is used to return a value from the function. (This is not the same "Return" used at the end of an AutoHotkey script, subroutine, or label to indicate the end of the code set. See the AutoHotkey Functions page for examples.)
The age calculation function in the GrandKids AutoHotkey app is called HowOld(FromDay,ToDay). The name of the function is HowOld using the parameters FromDay and ToDay. The FromDay (from day) would be the birthday (or any other date) and ToDay (to day) equates to the current date in the GrandKids app, but can be any date occurring after FromDay. The HowOld() function is used to calculate the difference in any two dates (years, months, days) as long as the "to day" is after the "from day" in the parameters.

Using the SubStr() Function to Parse Dates

You will note in the code below that the built-in AutoHotkey Substr() function is used a number of times to parse (break apart the components of) dates. The Substr() function is one of the most powerful and useful string (text) manipulation functions. In the case of the normal date/time format (yyyymmddhhmmss) the SubStr() function can be used to extract the year, month and date.
The SubStr() function uses the following format:
SubStr("text" or variable, start point, string length)

When using the date/time format the start point for the year (yyyy) is 1 with a length of 4 characters. The month (mm) starts on character 5 with a length of 2 characters. The day (dd) starts on character 7 with a length of 2 characters. This continues with the hour (hh), minutes (mm), and seconds (ss)—which are not used in this function. The following are the variations used and what they represent:
Year (yyyy) => SubStr(DateTime, 1, 4)
Month (mm) => SubStr(DateTime, 5, 2)
Day (dd) => SubStr(DateTime, 7, 2)
YearMonth (yyyymm) => SubStr(DateTime, 1, 6)
MonthDay (mmdd) => SubStr(DateTime, 5, 4)

The HowOld() Age Calculating Function

The following is the code for the HowOld() function:
HowOld(FromDay,ToDay)
  {
    Global Years,Months,Days

; If born on February 29
If SubStr(FromDay,5,4) = 0229 and Mod(SubStr(ToDay,1,4), 4)
      != 0 and SubStr(ToDay,5,4) = 0228
      PlusOne = 1

; To calculate this month's length set ThisMonth to current year and month (yyyymm)
    ThisMonth := SubStr(ToDay,1,6)

; Step 1. Set ThisMonthLength equal to next month
    ThisMonthLength := % SubStr(ToDay,5,2) = "12" ? SubStr(ToDay,1,4)+1 . "01"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)+1,-1)

; Step 2. Convert ThisMonthLength from date to days saved in ThisMonthLength
    EnvSub, ThisMonthLength, %ThisMonth%, d

; Set ThisMonthDay to FromDay or (if FromDay higher) last day of this month
    If SubStr(FromDay,7,2) > ThisMonthLength
        ThisMonthDay :=  ThisMonth . ThisMonthLength
    Else
        ThisMonthDay :=  ThisMonth . SubStr(FromDay,7,2)

; Step 1 to calculate last month's length
    LastMonthLength := % SubStr(ToDay,5,2) = "01" ? SubStr(ToDay,1,4)-1 . "12"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)-1,-1)
    LastMonth := LastMonthLength

; Step 2 days in last month saved in LastMonthLength
    EnvSub, LastMonthLength, %ThisMonth% ,d
    LastMonthLength := LastMonthLength*(-1)

; Set LastMonthDay to FromDay or (if FromDay higher) last day of last month
    If SubStr(FromDay,7,2) > LastMonthLength
        LastMonthDay :=  LastMonth . LastMonthLength
    Else
        LastMonthDay :=  LastMonth . SubStr(FromDay,7,2)

; Calculate Years
    Years  := % SubStr(ToDay,5,4) - SubStr(FromDay,5,4) 
            < 0 ? SubStr(ToDay,1,4)-SubStr(FromDay,1,4)-1 
            : SubStr(ToDay,1,4)-SubStr(FromDay,1,4)
 
; Calculate Months
    Months := % SubStr(ToDay,5,2)-SubStr(FromDay,5,2) 
            < 0 ? SubStr(ToDay,5,2)-SubStr(FromDay,5,2)+12 
            : SubStr(ToDay,5,2)-SubStr(FromDay,5,2)

    Months := % SubStr(ToDay,7,2) - SubStr(ThisMonthDay,7,2) < 0 ? Months -1 : Months
    Months := % Months = -1 ? 11 : Months

; Calculate Days
    TodayDate := SubStr(ToDay,1,8)          ; Remove any time portion of date/time
    EnvSub, ThisMonthDay,ToDayDate , d
    EnvSub, LastMonthDay,ToDayDate , d

    Days  := % ThisMonthDay <= 0 ? -1*ThisMonthDay : -1*LastMonthDay

; If February 28 
    Years := % PlusOne = 1 ? Years +1 : Years
    Days := % PlusOne = 1 ? 0 : days

    If (TodayDate <= FromDay)
       Years := 0, Months := 0,Days := 0
  }

The first line inside the function is:
Global Years,Months,Days

The Global command makes the variables YearsMonths, and Days available to the main script. Otherwise they would be local variables—not available to the same variables in the main script. This line could also be located in the main script, but here the script becomes more universal with YearsMonths, and Days available to any AutoHotkey script which includes the function. Remove the line and there is no output for the main script to use.

The Problem with Calculating Your Age

There is very little consistency in age units as described in Chapter Twenty-six. Most years have 365 days where one in four has 366 days. There are always twelve months, but the number of days in a month varies from 28 days to 31 days. A birthday can be any one of those days including a date which only occurs every four years (February 29th). All of this needs to be taken into account and each variation can affect not only the number of days since a birthday, but the months, and years.
Since it is the most unusual aberration, the first item taken into account is whether a person was born on February 29th:
If SubStr(FromDay,5,4) = 0229 and Mod(SubStr(ToDay,1,4), 4)
        != 0 and SubStr(ToDay,5,4) = 0228
        PlusOne = 1

This conditional uses a couple of tricks to determine the matching date. First the AutoHotkey SubStr() function is used with the birthday (FromDay) to determine if it is February 29 ("0229"). No fancy calculations are needed here because it either is or isn't. (If someone enters a date which doesn't exist the function will still run although it will not calculate the days.) The second part of the conditional (Mod(SubStr(ToDay,1,4), 4) != 0) uses the AutoHotkey Mod() function (Modulo) to check if the current year is evenly divisible by 4. The Mod() function returns the remainder from the quotient of a division calculation. If the remainder is 0, then it's a leap year and February 29 exists. In this situation, the condition is met only if it is not a leap year (the remainder does not equal (!=) zero, Mod() != 0). Finally, today is February 28th, SubStr(ToDay,5,4) = 0228.
It turns out in age calculation that the only day that matters when you are born on February 29 is February 28 of non-leap years. This is because on those years you turn one year older on February 28—the last day of the month. On March 1 you will be one year and one day older. For purposes of this function, if this is a non-leap year, the birthday is February 29, and today is February 28th, then the variable PlusOne is set to the value of 1. At the end of this function, Years will be incremented by +1, and Days are set to zero to account for the missing February 29th.

Getting Older

On the day of your birthday date in any given month, you become one month older. Prior to that date, you are the number of days older from the birthday date in the previous month. In order to calculate the how many days you have added since last month, we need to know how many days there were in the last month. Plus, since someone may have a birthday date this month which does not exist (29, 30, or 31), we need to know how many days are in this month.
Calculating the length of a month in AutoHotkey is a two-step process. First, the year and month (yyyymm) for next month must be set. Then, to calculate the days in this month the EnvSub command is used to find the difference in days between this month and next month. (There may be an easier way to calculate the days in a particular month in AutoHotkey, but I didn't find one.)
The variable ThisMonthLength is used temporarily to store the date (yyyymm) of next month. We start the month length calculation with:
ThisMonthLength := % SubStr(ToDay,5,2) = "12" ? SubStr(ToDay,1,4)+1 . "01"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)+1,-1)

This first step uses the Ternary Operator "? :" as shorthand for an "If" conditional statement using only one line. The single percent sign (%) tells AutoHotkey an expression is following. In this case the % sign represents "If." The question mark (spaces on both sides) starts the true condition (or "then") with the colon (:) designating the "else" or false condition. If this month is December (12), then next month is January (01) of the next year (+1); otherwise next month is this month plus one month.
Based upon the discussion of the SubStr() function and date/time strings, you can see that the year, SubStr(ToDay,1,4), and month, SubStr(ToDay,5,2), are being used to determine the next month. If it's December (12), then the next month is January (01) of the next year (+1). Otherwise, 1 is added to the month. This last part needs more discussion due to a problem with adding or subtracting numerical strings.
If the number 1 is added or subtracted from a number string such as "03" for March, the value is automatically converted to a numeric value and drops the leading zero. The new value becomes "4" or "2" without the space saving zero. This is critical for dates since the date/time string requires two characters for both the month and the day. To properly concatenate (or piece together) a new date after the calculation, any single digit months need to be padded with a zero (0) on the left end.

Padding a Text String

I originally constructed a method for padding the value of the newly calculated month because at the time I hadn't found a built-in AutoHotkey function for padding—which is common in other programming languages. I did this by adding 100 to the new month, then extracting the desired text:
Substr(100+SubStr(ToDay,5,2)+1,2,2)

By adding 100 there are no more leading zeros to be dropped. When the two digit month is parsed from the string, the leading 1 is dropped leaving a two digits string.
Since that time, I found a technique built-into AutoHotkey that makes left padding relatively simple:
Substr("0" . SubStr(ToDay,5,2)+1,-1)

This uses a variation of the SubStr() function to pad any string with any character or blank spaces. The function concatenates (combines) two strings: a string of the padding character, "0", and calculated value, SubStr(ToDay,5,2)+1. By using a negative number for the last parameter, -1, the function is told to extract characters starting at the end of the string. Zero is one character, -1 for two, -2 for three, and so on. The string of padding characters needs to be long enough for the maximum amount of padding needed. Since I only would need a maximum of one zero for padding, I use just one.
Note: Whenever concatenating two strings with the " . " operator, it is important to surround the period with spaces.
This built-in method of padding is superior to my technique not because it is shorter, but because it allows the padding for any type of string (text) and the padding character can be almost any character, not merely zeros.
The second step in calculating the number of days in the month is by using the EnvSub command:
EnvSub, ThisMonthLength, %ThisMonth%, d

Remember that the value of ThisMonthLength is actually the year and month (yyyymm) of next month. The EnvSub command when used with one of the date/time parameters (in this case "d" for days), converts the input variable from the date format to the number of days, hours, minutes, or seconds. The original date format value is lost. By finding the difference between ThisMonth and ThisMonthLength (at this point ThisMonthLength is actually the year and month of next month), the latter stores the number of days in this month in ThisMonthLength (converting the value from a date to a number of days).
Note: Unlike EnvSub, EnvAdd does not convert the value from the date format when using the date/time parameters. EnvAdd adds (or subtracts if a negative number) the number of days, hours, minutes, or seconds, then returns the new date in the date/time format.

Did Your Birth Date Exist Last Month?

It is important to know what day a person becomes one month older. If born on a day less than the number of days in the month, then that is the day a month is added. If born on a date after the last day of the month which does not exist in the current month (possibly 29, 30, or 31), then another month is added on the last day of the month. Therefore the birth date day in this month is determined as follows:
    If SubStr(FromDay,7,2) > ThisMonthLength
        ThisMonthDay :=  ThisMonth . ThisMonthLength
    Else
        ThisMonthDay :=  ThisMonth . SubStr(FromDay,7,2)

The process is the same when finding the birth date for the last month later in this same age calculation function.

Calculating the Number of Days in Last Month

The same two step process is used to calculate the number of days in last month as was used in this month except we subtract rather than add. If it's January (01), then the month is changed to December (12) and 1 is subtracted from the year. Otherwise, 1 is subtracted from the month:
; Step 1 to calculate last month's length
    LastMonthLength := % SubStr(ToDay,5,2) = "01" ? SubStr(ToDay,1,4)-1 . "12"
           : SubStr(ToDay,1,4) . Substr("0" . SubStr(ToDay,5,2)-1,-1)
    LastMonth := LastMonthLength

; Step 2 days in last month saved in LastMonthLength
    EnvSub, LastMonthLength, %ThisMonth% ,d
    LastMonthLength := LastMonthLength*(-1)

Next, the birth date of last month is set based upon whether the date exists in last month:
If SubStr(FromDay,7,2) > LastMonthLength
        LastMonthDay :=  LastMonth . LastMonthLength
Else
        LastMonthDay :=  LastMonth . SubStr(FromDay,7,2)

Calculating Years, Months, and Days

First the number of years of age is calculated based upon whether the birthday has passed:
Years  := % SubStr(ToDay,5,4) - SubStr(FromDay,5,4) < 0 
            ? SubStr(ToDay,1,4)-SubStr(FromDay,1,4)-1 
            : SubStr(ToDay,1,4)-SubStr(FromDay,1,4)

This can get a little confusing. Today's month and day minus the birthday month and day will be negative from January 1 until the birthday passes. Then the difference is positive until the first of the next year. If the value is negative (< 0), then the years are one less than a straight difference between the years. Otherwise the calculation is accurate.
Next the number of months is calculated:
Months := % SubStr(ToDay,5,2)-SubStr(FromDay,5,2) < 0 
     ? SubStr(ToDay,5,2)-SubStr(FromDay,5,2)+12 
      : SubStr(ToDay,5,2)-SubStr(FromDay,5,2)

Months := % SubStr(ToDay,7,2) - SubStr(ThisMonthDay,7,2) < 0 
            ? Months -1 : Months
Months := % Months = -1 ? 11 : Months

First, if this month is after January 1 and before the birth month, then the difference between the two will be negative (< 0). If so, then 12 months of age needs to be added to the negative difference between the two months. Otherwise, the number of months is the straight difference between the two dates with a couple of potential adjustments.
The two lines of code that follows adjust the number of months of age for whether the birth date day has passed and if the adjustment goes negative. A person is not one month older until the birth date day in the month (ThisMonthDay)—or the last day of the month if the birth date day doesn't exist in that month. Therefore if the day has not been reach, one month must be subtracted from the total (Months -1). Next if a zero number of months (birth month) goes negative, then it needs to be reset to 11 months.
The last calculation is the remaining Days of age after the Years and Months:
TodayDate := substr(ToDay,1,8)    

    EnvSub, ThisMonthDay,ToDayDate , d
    EnvSub, LastMonthDay,ToDayDate , d

    Days  := % ThisMonthDay <= 0 ? -1*ThisMonthDay : -1*LastMonthDay

First the ToDay variable is trimmed of any time (hhmmss) components and stored in the TodayDate variable (TodayDate := substr(ToDay,1,8)). I found that if I used the date/time stamp without trimming it, in the following calculations it would round up to the next day causing errors in some of the ages. This trimming can be done early on (and probably should), then used throughout the function.
There are two calculations made with the EnvSub command. The first is the number of days from the birth date day of this month to today and the second is the number of days from the birth date day of this month to today. If the first calculation is negative or zero (ThisMonthDay <= 0), then the number days is equal to the days since the birth date day in this month (-1*ThisMonthDay). (Since the calculation will be negative, it's necessary to multiply by -1.) Otherwise, it is the number of days since the birth date day last month—also a negative number (-1*LastMonthDay).

Adjustment for February 29 in Non-Leap Years

Finally, one last adjustment is need for February 28 birthdays in non-leap years when the date is February 28:
    Years := % plusone = 1 ? Years +1 : Years
    days := % plusone = 1 ? 0 : days

First, if it's the 28th, one year is added since there will be no 29th. Second the Days are now zero with the added year. There is no adjustment since it was already calculated to 0 months.
The last item is a trip in case someone enters a birthday after today's date:
    If (TodayDate <= FromDay)
        Years := 0, Months := 0, Days := 0

People don't start to age until they are born. Notice that there are three lines of code on one line separated by commas. No need for curly brackets ({}) here.
I posted the complete GrandKids 6.ahk file in the ComputorEdge AutoHotkey Dropbox for your perusal.

Chapter Thirty-five: Text Formatting in GUIs

“Text positioning options in AutoHotkey GUI windows explained.”
AutoHotkey has tools for positioning and sizing controls in AutoHotkey windows, but you need to understand how they work.

Formatting Controls on GUIs

It would pay off for me to read my own writing—or at least refer to it from time to time. When I started working on the GrandKids app I forgot to look back at what I said about formatting text in a GUI control in Chapter Nineteen. I had spent so much time playing around with the limited MsgBox windows when working on the Reminder app that I dug into writing each line of text with only one command using tabs and padding spaces. It worked, but I made life a little more difficult than necessary.
This is the code I originally came up with:
    FormatTime, Birthday , %Birthday%, dddd, MMMM d, yyyy
    RowSpace := First . " " . Second . " " . Last . "                    "
    RowSpace := SubStr(RowSpace,1,35)
    BDSpace  := Birthday . "                         "
    BDSpace := SubStr(BDSpace,1,40)
    Gui, Add, Text, , %RowSpace%`t%BDSpace%`t%Years% Years
                     , %Months% Months, %Days% Days

The problem with using tabs (`t) to align columns is that you need to know the length of each text field in front of each column. For shorter text lengths, you may need two (or more) tabs, whereas with longer lengths only one. This can quickly become a complex problem with measuring text lengths and adding the appropriate number of tabs.
I opted for a solution that included fixing the length of each text field by padding the field on the right with blank spaces. Then I only need one tab (`t) character. To do that I first formatted Birthday (FormatTime) for display. Then I combined the three names and added ample blank spaces to the end—saving the result in the variable RowSpace. I next used the SubStr() function to shorten each name string line to the same length, SubStr(RowSpace,1,35)—in this case 35 characters.
I then added blank spaces to the end of the formatted Birthday and saved it in the variable BDSpace. I next used the SubStr() function to shorten each birthday text line to the same length, SubStr(BDSpace,1,40)—in this case 40 characters. (Note: The StringTrimRight command could have been used in place of the SubStr() function.)
Once I had the first two strings set to fixed lengths, I combined them with Year, Months, and Days placing a tab character (`t) between each to create aligned columns. While this works, using AutoHotkey GUI control formatting options gives more flexibility and alignment accuracy. I'll save this approach to formatting text for windows such as the MsgBox which does not respond to the GUI positioning options.

Using GUI Control Positioning and Sizing Options

It's not that I completely forgot about the GUI control positioning and sizing options. I was well aware of them and started reviewing the various possibilities. However, I overlooked one of the warnings I had highlighted in the column linked above and just started playing with options. (I didn't plan on spending a lot of time on formatting since the primary purpose of the app was to show the use of an INI database and do an age calculation.) However, the options can be confusing if you don't study how they interact. Make one wrong use of an option and the entire window can seem to go crazy. I was wasting so much time trying to get it to work that I finally resorted to the solution above. I had overlooked the most important of the positioning options, Section, which is similar to creating a new row in a table—although not really.
In any case, I received an e-mail from Charles Clarkson which included a new script for the GrandKids app which he had reworked into AutoHotkey object oriented code. (Charles is a very capable programmer who's comfortable with Object Oriented Programming (OOP). I've only dabbled in OOP.) As I was reviewing his code I notice that he was using the GUI, Add, Text command with the positioning options to achieve almost exactly what I had with my work-around.
I copied the pertinent lines from the classes in his script and replaced the lines I was using in my script:
   FormatTime, Birthday , %Birthday%, dddd, MMMM d, yyyy
    FullName := First . " " . Second . " " . Last . "                    "
    Gui, Add, Text, section w200 x8, %FullName%
    Gui, Add, Text, w200 ys, %Birthday%
    Gui, Add, Text, ys, %Years% Years, %Months% Months, %Days% Days

The key options are section and ys. Using section gives a clean start each time it's used—in this case a new line. Notice that it only occurs in the first GUI, Add, Text line. That's the only time a new line is needed since the ys in the next two lines of code creates new columns on each line. The alignment is much easier.
I decided that it was time for me to get a better understanding of exactly how the positioning options affected the layout of the GUI controls. I started playing with various combinations of options. I encountered a number of interesting effects as I changed and tested the code. Although I did not find the terminology intuitive, after carefully reading the documentation, I eventually came to understand the reasoning behind it.

How GUI Control Positioning and Sizing Works

Once how the positioning and sizing options work is understood, it is much easier to format the layout of controls in a GUI window. There are a few important key concepts.
First, everything originates in the upper left-hand corner. That is where the first control will appear (unless unique coordinates are provided). The GUI window will self-adjust as long as no special numerical parameters are added. Since the sizing is automatic, if possible, it is best to design your GUIs without the addition of specific coordinates or dimensions. Then if future changes are required, the adjustments will be automatic without a great deal of fiddling with the numbers.
Second, unless told otherwise, the next control will always be added below the previous control (not to the right—or to the left if the previous control is already set to the right). If you want to place a control to the right, the easiest method is to create a new column with the ys (new column in Section) option (see Figure 1). Any columns created with the ys option will maintain row alignment (although not column alignment with previous Sections). That means if one column is forced to add new row space for word wrapping, then that space will be added to each column in the same Section.
Figure 1. The coordinates for the AutoHotkey GUI control positioning and sizing options start in the upper left-hand corner. The X coordinates increase to the right and the Y coordinates increase down the window. Controls are always added below the last control unless designated by ys (new column in section), ym (new column at top margin), or a y coordinate (y0) without an x coordinate. Each "Section" designates a new row group. To align columns in different Sections (rows) the first columns must be assign the same widths (Width 1 and Width 2 in blue) respectively.
The Section option is used to separate groups of controls. When the first control is added to a GUI the Section option is assumed. Every control added after the section option is associated with that group. However, the new control with the Section option will continue to fall below and align with the previous control which means, if you've added any columns (ys) and you want the new Section to align on the left, you may need to add either an x coordinate (i.e. x7) or use the xm (new row at left margin) option to move it to the left margin.
If you want to add a new row with columns, then you must add a new Section option. Otherwise, any additional columns (ys) will appear to the right of the original Section—not aligned with the new row.
In other words, any row added to a Section (xs, new row in Section) will appear below the other rows within the Section. However, the ys option, without a new Section, will continue to add columns to the first row in the Section. The ys and xs options should be used separately. Otherwise, controls will most likely be over written.
To align columns between different Sections (rows), all the columns, with the exception of the last column, must be set to the same width (W) as the column in the section just above it. Otherwise, the columns will independently adjust to the size of the controls or text. There is no inherent vertical alignment between the columns in different Sections as there is for horizontal alignment between a row of columns within the same section. Remember, the ys options will only add columns to the first row within any Section.
The xm and ym options are similar to xs and ys options, except that they always place the control at the left margin or top margin, underneath or to the right of any previous controls respectively. The ym option can be used to start a new column of controls at the top right of the GUI. Most likely the Section option should be used to contain the controls in that space. The xm option can be used to start a new row on the left margin below all other controls, however the Section option should be used if more columns are planned for that row.
There are two methods for adjusting the height of a row. However, if no options are used the row height will automatically adjust to the controls placed within it. The R option is calculated in rows. The H option is calculated in pixels. The R option takes precedence over the H option if both are used. The R option will also determine how many items will be displayed in DropDownLists, ComboBoxes, and ListBoxes.
The W option specifies the width of a control. While the width of a control will adjust automatically depending upon the content, it is usually necessary to set the width (W) option for column alignment between Sections unless the controls in the columns are of the same width as the column in the Section just above it.
There are specialized options which add more flexibility to GUI control positioning. The options wp+n and hp+n can be used to copy the width and height respectively of the previous control. The +n (or -n) is used to make adjustments to the copied parameters.
The options xp+n and yp+n position the control in relation to the upper left-hand corner of the previous control. This is useful when working with a GroupBox—which is nothing more than an outlined box with a title. The +n (or -n) is the relative coordinate used to position the control.
The most obvious options available are x and y which are exact coordinates (x,y) within the GUI window. I mention these options last because they are the last you should consider using. While they are exact in many situations the automatic re-sizing of rows and columns may be lost. The x and y options must include a number (i.e. x0x5y0y5) to be valid. Specifying y with no x will start a new column to the right of all other controls. Specifying x with no y will start a new row below all other controls. In most circumstances it is better to use xs and ys or xm and ym to start new rows or columns.
Remember, the GUI control's positioning and sizing options work top to bottom, then left to right only when designated. Once you understand how AutoHotkey handles these options, it's much easier to design your own GUIs.
The moral of the story is don't try to remember everything that you once knew. Even though I had used and written about formatting text in a GUI, I forgot to review my notes—or in this case a previous column. If I had completed a ComputorEdge Web site search, I would have saved some time.

What's Next?

“Although you should already be getting great value out of AutoHotkey, this book is merely the beginning of the journey.”
This book is just a few steps in Jack's travels with AutoHotkey. He will continue this path and exploration hopefully helping other travelers along the way.
If you've made it all the way through this book and understand how the various scripts work, then you are well on your way to doing whatever you want with AutoHotkey. By now, you should have a pretty good understanding of how the AutoHotkey commands work and where to find new commands and answers to your questions.
Don't let yourself fall into the trap of trying to do everything with AutoHotkey. There are many features in Windows that don't require the use of a utility script. For example, a right-click on the Windows Taskbar allows you to set up a new toolbar which turns any Windows folder into a menu. You don't necessarily need to use the QuickLinks app discussed in Chapter Twenty, although I still use it on a regular basis on all of my Windows computers. I've seen people on the AutoHotkey forum who spend days trying to write a script for Windows which doesn't add much value because there are so many other ways to accomplish the same thing without AutoHotkey.
I will continue to work on the Reminder and QuickLinks apps until they do the things I want them to do. Any progress I make you should find in my weekly AutoHotkey column and will eventually appear in the next book. I will continue to dig even deeper in AutoHotkey techniques, ultimately getting into the more advanced commands and apps. As usual, I will concentrate on simple, short apps which will give immediate results. (Of course, some of them will evolve into longer more complicated solutions, but only a little bit at a time.)
I encourage you to continue your own explorations of AutoHotkey. If you have questions, I'm generally available at ceeditor@computoredge.com. While I won't write long applications for you, I will try to point you in the right direction. I'm more interested in teaching you to fish rather than giving you a fish.
If you find errors or omissions that should be included in this book (or a follow up book), please let me know. I want to get it right.

AutoHotkey Index to Chapters

“Alphabetical listing of topics and commands in chapters with links.”
The AutoHotkey commands which appear in this book are linked directly to the AutoHotkey Web site for syntax and options. Plus, the same commands and topics are linked directly to the pertinent chapters.
::, hotkey substitution, Chapter EightChapter Twenty-three
:*:, hotkey substitution * option, Chapter Twenty-three
%, expression evaluation with single %, Chapter ThirteenChapter Twenty-eight
%, variable value (percent signs %), Chapter TwelveChapter ThirteenChapter Fifteen
# Commands, Chapter Thirty
#IfWinActive commandChapter Thirty
#SingleInstance, Chapter Thirty-three
A_Index, Chapter Thirty-three
A_Now (current time), Chapter FifteenChapter Twenty-sixChapter Twenty-nine
A_LoopFileName, Chapter Twenty-one
A_ThisMenu, Chapter Twenty-one
A_ThisMenuItem, Chapter Twenty-one
A_ThisMenuItemPos, Chapter Twenty-one
A_UserName, Chapter FourteenChapter FifteenChapter NineteenChapter Twenty-one
Active window, Chapter Twelve
Adding a Help window (Scratchpad script), Chapter Seventeen
Adding audio with NirCmd, Chapter Twenty-sevenChapter Twenty-eight
Adding menu items to System Tray, Chapter Twenty-two
Adding quotes and brackets, Chapter Two
Adding Special Symbols in Windows with Character Map, Chapter One
Adding speech, Chapter Twenty-eight
Adding text together (concatenate), Chapter Five
Adding to an AutoHotkey control, Chapter Eighteen
Age calculation, Chapter Thirty-four
Ahk_class Shell_TrayWnd, Chapter Twelve
Always-on-top, Chapter Four, (Scratchpad script), Chapter Seventeen
Array index (A_Index), Chapter ThirteenChapter Thirty-three
Array variable, Chapter Thirteen
Audio, adding with NirCmd, Chapter Twenty-sevenChapter Twenty-eight
AutoHotkey commands
AutoHotkey key, mouse buttons and joystick listChapter Eight
Automating AutoHotkey app launch at startup, Chapter Twenty-threeChapter Twenty-five
Automating Windows programs, Chapter Nine
AutoCorrect app, Chapter Twenty-threeChapter Twenty-four
Backup scripts, Chapter FourteenChapter Fifteen
Backup, incremental, Chapter Fifteen
Breaking lines of code, Chapter Nineteen
Calculating, if leap year, Mod(), Chapter Thirty-four
Calculating time, EnvSub, Chapter Twenty-eight
Change button names, Chapter Thirty
Change a keyboard key, Chapter Eight
Change the System Tray icon, Chapter Twenty-two
Character Map, adding special symbols in Windows with, Chapter OneChapter Twenty-four
Check for program installation, NirCmd, Chapter Thirty
Click commandChapter NineChapter FourteenChapter Fifteen
Click coordinates, finding, Chapter Ten
Clipboard variable, Chapter TwoChapter FiveChapter Fifteen
Comments in scripts (;), Chapter Sixteen
Common misspellings in English, Chapter Twenty-three
Compiling AHK scripts, Chapter Twenty-two
Concatenate operatorChapter Five
Concatenating (adding) strings (text), Chapter ThirteenChapter Thirty-five
Concatenating, self-concatenating operator (.=), Chapter Thirteen
Continue commandChapter Twenty-one
Continuing lines of code, Chapter Nineteen
Control names, finding, Chapter Ten
ControlSend commandChapter SixteenChapter Eighteen
ControlSetTextChapter Thirty
Convert .ahk to .exe utility, Chapter Twenty-two
Coordinate system (x,y), screen and window location, Chapter Twelve
Copy text to another window, Chapter Sixteen
Creating an AHK file, Chapter Fourteen
Critical commandChapter Fourteen
Database, simple AutoHotkey, Chapter Thirty-three
Date/time specific GUI commandChapter Twenty-sixChapter Thirty-one
Date/time, limited range, Chapter Twenty-nineChapter Thirty-one
Debugging, MsgBox command for, Chapter Eleven
Disable hotkey combinations temporarily, Chapter Thirty
Disable key in active window, Chapter Thirty
Disabling keyboard keys, Chapter EightChapter Thirty
Disappearing Taskbar Trick, Chapter Seven
Displaying program icons,, Chapter Nineteen
DllCall, Chapter Thirty-two
Dropbox, for updating computers (file sharing), Chapter Twenty-five
Dropdown menu, Chapter Eleven
DropDownList, Chapter ElevenChapter Thirteen
E-mail address input, Chapter Three
EnvAdd and EnvSub, difference in time calculation, Chapter Twenty-eightChapter Thirty-four
EnvSub commandChapter Twenty-sixChapter Twenty-eightChapter ThirtyChapter Thirty-four
Escape character (`) (`), Chapter FifteenChapter Sixteen
EXE, compiling from AHK scripts, Chapter Twenty-two
EXIT, Chapter Twenty-six
Exiting an AutoHotkey app with the System Tray icon, Chapter Twenty-three
FileAppend commandChapter FourteenChapter Fifteen
FileCreateDir commandChapter FifteenChapter Twenty-one
FileDelete commandChapter FourteenChapter Eighteen
Files, hidden and system, Chapter Twenty-one
Finding control and button names, Chapter Ten
Finding window names, Chapter Ten
Folders, looping through files, Chapter Twenty-one
Forcing an expression (%), Chapter Twenty-eight
FormatTime commandChapter Twenty-sixChapter Thirty-three
Formatting, GUI, Chapter ElevenChapter Nineteen
Formatting options chart, GUI, Chapter Thirty-five
Formatting text, Chapter Thirty-five
Function, HowOld(), Chapter Thirty-threeChapter Thirty-four
Functions described, what are they?, Chapter Thirty-four
Global variable, Chapter Thirty-four
GoSub commandChapter Thirty-one
GoSub (Scratchpad script), Chapter Seventeen
GrandKids app, (script), Chapter Thirty-three
GUI (Graphic User Interface) commandChapter ElevenChapter NineteenChapter Twenty-sixChapter Twenty-nine
GUI, displaying current time and date, Chapter Twenty-nineChapter Thirty-one
GUI formatting, Chapter ElevenChapter Nineteen
GUI control positioning and sizing optionsChapter Thirty-five
GuiClose:, (window event label), Chapter Twelve
GuiControl commandChapter ThirteenChapter EighteenChapter Twenty-sixChapter Thirty-two
Gui, AddChapter NineteenChapter Twenty-six
Gui, Add, PictureChapter Nineteen
Gui, Button controlChapter NineteenChapter Twenty-six
Gui, FontChapter NineteenChapter Twenty-sixChapter Thirty-two
Gui, Show, Chapter NineteenChapter Twenty-sixChapter Twenty-sixChapter Thirty-three
Icons, displaying in AutoHotkey, Chapter Nineteen
IF commandChapter TwelveChapter ThirteenChapter Twenty-oneChapter Twenty-sixChapter Thirty-twoChapter Thirty-four
IF statement, one-line, Chapter Thirty-four
IfExist conditionalChapter FourteenChapter EighteenChapter ThirtyChapter Thirty-one
IfNotExist commandChapter FifteenChapter Twenty-one
IfWinActive command (#), Chapter Thirty
IfWin[Not]Exist commandChapter SixteenChapter ThirtyChapter Thirty-two
INI file sections, Chapter Thirty-three
INI file keys, Chapter Thirty-three
IniReadChapter EighteenChapter Thirty-oneChapter Thirty-twoChapter Thirty-three
IniWriteChapter EighteenChapter Thirty-oneChapter Thirty-twoChapter Thirty-three
IniDeleteChapter Eighteen
INI files, Chapter EighteenChapter Thirty-three
Keyboard, change a key, Chapter Eight
Keyboard, disabling a key, Chapter Eight
Keyboard, adding a key (), Chapter Eight
Labels, subroutines, Chapter NineteenChapter Twenty-oneChapter Twenty-sixChapter Twenty-eightChapter Thirty-two
Launching Web sites in a browser, Chapter Nineteen
List open windows (WinGet, OpenWindow, List), Chapter Thirteen
Local variables, Chapter Thirty-four
LOOP (files & folders) commandChapter Twenty-oneChapter Thirty-three
LOOP commandChapter Thirteen
Loop within a loop, Chapter Twenty-one
Menu, add to System Tray, Chapter Twenty-twoChapter Thirty-one
Menu, disable System Tray item, Chapter Thirty-one
Menu, remove (and add) standard items in System Tray, Chapter Twenty-two
Menu, selection, Chapter Eleven
Menu commandChapter TwentyChapter Twenty-oneChapter Twenty-two
Mod(), remainder function, Chapter Thirty-four
Modulo, Chapter Thirty-four
Mouse, simulate, Chapter Nine
Mouse click coordinates, finding, Chapter Ten
MouseGetPosChapter Twelve
Moving windows, Chapter Eleven
MsgBox, Chapter ThirteenChapter Twenty-sixChapter Twenty-nineChapter Thirty
MsgBox command for debugging, Chapter ElevenChapter Thirteen
MsgBox Options, Chapter Thirty
Padding strings, Chapter Thirty-four
Parsing dates, Chapter Thirty-four
Pop-up menu (QuickLinks), Chapter Twenty-one
Program Manager (Windows Desktop), Chapter Twelve
Properties window, Chapter Nineteen
QuickLinks, add to System Tray, Chapter Twenty-two
QuickLinks AutoHotkey app, Chapter TwentyChapter Twenty-one
"Reload This Scrip", Chapter Eleven
Remainder function, Mod(), Chapter Thirty-four
Reminder app, Chapter Twenty-six
Removing special characters (\ and :), Chapter Fifteen
Replacing Windows 8 Start Menu, Chapter NineteenChapter Twenty
Resizing, windows, Chapter Eleven
Return commandChapter FifteenChapter Nineteen
Run commandChapter SixChapter SixteenChapter NineteenChapter Twenty-eightChapter Thirty-two
Run, NirCmd, Chapter Twenty-eight
Saving a file on exit (Scratchpad script), Chapter Seventeen
Saving data, Chapter Eighteen
Scratchpad by Desi QuintansChapter Seventeen
Screen and window location system, (coordinates x,y), Chapter Twelve
Search Web, Chapter Six
Section in GUI formatting, Chapter Thirty-five
Selection menu, Chapter Eleven
Send commandChapter TwoChapter SixChapter FourteenChapter FifteenChapter Eighteen
SetTimerChapter Twenty-sixChapter Twenty-eightChapter Thirty
Sleep commandChapter FourteenChapter Fifteen
Send commandChapter Sixteen
SendInputChapter OneChapter FiveChapter NineChapter Twenty-four
Sharing AutoHotkey files with Dropbox, Chapter Twenty-five
Shell_TrayWnd (ahk_class), Chapter Twelve
Sleep commandChapter TwoChapter FiveChapter Six
Special symbols, adding in Windows with AutoHotkey, Chapter One
Fifteen
Start Menu, make your own, Chapter Nineteen
Startup folder, automate AutoHotkey script launch, Chapter Twenty-threeChapter Twenty-five
Stop a running AutoHotkey app, Chapter Twenty-three
Strings, adding together (concatenate), Chapter Five
StringReplace commandFifteen
Submenu, Chapter Twenty-one
Submit button, GUI, Chapter Twenty-sixChapter Twenty-eight
Subroutines, (labels), Chapter NineteenChapter Twenty-one
Substitution (::), AutoHotkey, Chapter EightChapter Twenty-three
SubStr functionChapter FiveChapter Thirty-fourChapter Thirty-five
SubStr, parsing dates, Chapter Thirty-four
SubStr, using to pad characters (left or right)
System Tray, adding right-click menu, Chapter Twenty-two
System Tray, change icon, Chapter Twenty-two
System Tray, removing (and adding) standard menu items, Chapter Twenty-two
System Tray icon, Chapter Eleven
Understanding formatting GUI controls, Chapter Thirty-five
Variable, array, Chapter Thirteen
Variable value (percent signs %%), Chapter Twelve
Visible, toggling a window (Scratchpad script), Chapter Seventeen

Комментариев нет:

Отправить комментарий