owenG
home learn tableau about
divider
legalific swsom swsom diagram swapps ahk ue








AutoHotKey


Lookup Table to Abbreviation Expansion via AutoHotKey Hotstring

Below is an example that is based on Summation but could be applied to a number of different applications, as long as the abbreviation & expanded full-text data is somehow retrievable. The first part is the script that would be run in the Scripting window of Summation, followed later by the code that would be put into an .ahk script. The code is only in draft form, without any of them there declared varibles and whatnot. Even as a draft the script wound up taking siginficantly longer to create than I had originally estimated, though in large part the additional work was testing-related and had to do with hinkiness particular to the AutoHotKey HotString replacement behavior. The only line in the script that should need to be customized before a given run would be lookupTableName = "_Names" where the lookup table name needs to be appropriate for a particular database:

The general steps performed by the vs code:

  • assign the code that will limit the character(s) that trigger a hotstring expansion, via #Hotstring EndChars declaration
  • create arrays of the values in the Code Column (arrShort) and Expansion Column (arrLong)
  • for each item pair in the arrays, create the appropriate hotstring snippet
    • where some of the snippet code will involve manually removing the initial abbreviation characters and for that we need to know how many characters to remove via the backspace key
  • get the string text that will represent the ahk function for checking to see if the hotstring text is actually being typed into Summation, and that the 'correct' case is open
  • write the final ahk script to a text file and display in a Summation HTML view
VBScript
Call Main

Sub Main
    lookupTableName = "_Names"
    delim = "%"
    blazeType = SystemInfo.BlazeType() 
    thisCase = CurrentCase.Name
    hotstringEndCharCode = ";restrict hotstring activation to colon character" & vbCrLf & _
     "#Hotstring EndChars :" & vbCrLf
    code = hotstringEndCharCode 
    newCode = ""
	
    lookupShort = db.GetLkupEntries(lookupTableName, 1, delim)
    lookupLong = db.GetLkupEntries(lookupTableName, 2, delim)
    arrShort = Split(lookupShort, delim)
    arrLong = Split(lookupLong, delim)
	
    If (UBound(arrShort ) = UBound(arrLong )) Then
	    For i = 0 To UBound(arrShort)
		    newCode = AssembleHotString(arrShort(i), arrLong(i)) 
		    code = code & vbCrLf & newCode
		    If i = 500 Then 
			    Exit For 'escape hatch
		    End If
	    Next 
	
    Else
	    Alert "Mismatch between codecol and expansioncol in lookup table"
    End If
	
    SummFunctionCode =  GetSummationIsActiveText(blazeType, thisCase)
    code = code & vbCrLf & SummFunctionCode 
    Set fso = CreateObject("Scripting.FileSystemObject")
    codeFilePath = CurrentCase.CaseDir & "AHK" & lookupTableName & ".txt"
    Set codeFile = fso.CreateTextFile(codeFilePath, True)
    codeFile.Write code
    codeFile.Close
	
    AddHTMLView "AutoHotKey code for lookup table: " & lookupTableName, codeFilePath
    Set codeFile = Nothing
    Set fso = Nothing
End Sub

Function AssembleHotString(abbr, expand)
    'thisCode = ""
    backspaceString = GetBackSpaces(abbr)
    thisCode = ":B0:" & abbr & "::" & vbCrLf & "If (SummationIsActive())" & vbCrLf & vbTab & _
            "Send " & backspaceString & expand & vbCrLf & "return" & vbCrLf
    AssembleHotString = thisCode
End Function

Function GetBackSpaces(abbr)
    stringLength = Len(abbr)
    returnString = "{BS}"
    For x = 1 To stringLength 
	    returnString = returnString & "{BS}"
    Next
    GetBackSpaces = returnString
End Function

Function GetSummationIsActiveText(productName, currentCase)
    functionText = "SummationIsActive()" & vbCrLf & "{" & vbCrLf & _
    vbTab & "local status  ;declare variable" & vbCrLf & _
    vbTab & "SetTitleMatchMode, 1 ;window title must begin with selected string" & vbCrLf & _
    vbTab & "IfWinActive " & productName & "  ;match product name" & vbCrLf & _
    vbTab & vbTab & "SetTitleMatchMode, 2 ;window title must contain string somewhere" & vbCrLf & _
    vbTab & vbTab & "IfWinActive '" & currentCase & "'  ;match current case name" & vbCrLf & _
    vbTab & vbTab & vbTab & ";optionally could have assembled full window title using sw's " & _
        "SystemInfo and CurrentCase object" & vbCrLf & _
    vbTab & vbTab & vbTab & "status = true" & vbCrLf & _
    vbTab & "return status" & vbCrLf & "}" & vbCrLf
    GetSummationIsActiveText= functionText
End Function

When the .vs script above is executed in the P. Franc demo case of Summation, something like the following text should appear in a new HTML view:

ahk
;restrict hotstring activation to colon character
#Hotstring EndChars :

:B0:BCO::
If (SummationIsActive())
	Send {BS}{BS}{BS}{BS}Beth Cook
return

:B0:BED::
If (SummationIsActive())
	Send {BS}{BS}{BS}{BS}Bruce Edwards
return

...  (bunch more hotstring snippets)  ...

:B0:WJA::
If (SummationIsActive())
	Send {BS}{BS}{BS}{BS}William Jackson
return

SummationIsActive()
{
	local status  ;declare variable
	SetTitleMatchMode, 1 ;window title must begin with selected string
	IfWinActive CT Summation iBlaze®  ;match product name
		SetTitleMatchMode, 2 ;window title must contain string somewhere
		IfWinActive 'CopyCase P. Franc'  ;match current case name
			;optionally could have assembled full window title using sw's SystemInfo
			;and CurrentCase object
			status = true
	return status
}

And then you might notice that something moderately unpleasant has happened and that there are a few hundred hotstring snippets even though this particular _Names table only has 31 records. Looks like db.GetLkupEntries could use some fixin', where it appears to be returning all occurrences/references of a Lookup table on the current form - presumably there are 13 fields on the e-form that link to _Names since the method returned the same 31 values 13 times. I'm sure there is a workaround (at the least, if in iBlaze, a SELECT via a direct ADO connection would be cleaner) but this is a one-time kind of need so it is easier to first just lop off the '#Hotstring EndChars :' string. To that add the 31 correct snippets (WARNING: last & first of 31 abbreviations were concatenating as a single string) + code for SummationIsActive function and paste those into a text file with a .ahk extension.

The first line of the generated ahk script relates to the triggering of any hotstring. By default there are a number of 'ending characters' and codes ( -()[]{}':;"/\,.?!`n `t) that, when typed after the abbreviation characters, result in expansion. Those default trigger characters might work OK here but there were enough _Names entries, most quite short, that they could have been easily triggered by mistake. Simply type the word 'bed' followed by hitting the space bar and that word would always be replaced with 'Bruce Edwards', perhaps totally out of context. Instead we tell AutoHotKey that the hotstrings should only act as hotstrings if they are suffixed with a colon, now need to type 'bed:' in order to get the full 'Bruce Edwards' response.

ahk
    #Hotstring EndChars :

Looking at the specifics of a given hotstring:

ahk
    :B0:BCO::
    If (SummationIsActive())
	    Send {BS}{BS}{BS}{BS}Beth Cook
    return

The first line represents the abbreviation that will trigger the expanded replacement string and we can compare that entire snippet to a hotstring using the default AutoHotKey syntax:

ahk
    ::BCO::Beth Cook

In the standard hotstring syntax, type the characters 'BCO' (case-insensitive by default, which can be changed) pretty much anywhere in any program, and they will be instantly replaced with the characters 'Beth Cook'. In our version the first difference is the 'B0' (letter B followed by zero) between the first two colons, which relates 'Automatic Backspacing' behavior. Automatic Backspacing = true is the default behavior - type the shortcut and the shortcut characters are removed and replaced with the expansion. In our case we don't want to do that since the hotstrings should only trigger when the text is being typed into Summation. With automatic backspacing, the expansion would occur before we had the chance to check what the active window is.

After the hotstring declaration, there is a check to see if Summation is the currently active window and then a series of characters is sent to the screen:

C#
    If (SummationIsActive())
        Send {BS}{BS}{BS}{BS}Beth Cook
    return

The '{BS}' reference is an AutoHotKey constant representing the Backspace key. In this example, there are four backspaces sent, one for the ':' colon character that triggered the expansion + three for the number of characters in 'BCO' (calculated in the Summation script by the GetBackSpaces function). After the abbreviation is cleared out, the expansion text is delivered and the hotstring function is complete. In theory, the multiple backspaces could have been replaced with by sending keystrokes that would represent 'Shift+Ctrl+LeftArrow', 'LeftArrow', thereby highlighting the 'BCO:' text. Sending keystrokes after that would delete the highlighted text. However, if the 'select left' keystroke combo spec'd out above is executed in the Column view of Summation, the result will be unhelpful - cursor focus will move left by a couple of cells and that's all.

The SummationIsActive function is responsible for checking to see if the Summation is the application currently receiving UI input. The design here is only one approach, where we first see if the title of active window begins with 'CT Summation iBlaze®' and if so, check to see if the designated case name (determined by the initial .vs script when it is run) is part of that window title. If both those conditions are true, the status return variable is set to true.

ahk
SummationIsActive()
{
	local status  ;declare variable
	SetTitleMatchMode, 1 ;window title must begin with selected string
	IfWinActive CT Summation iBlaze®  ;match product name
		SetTitleMatchMode, 2 ;window title must contain string somewhere
		IfWinActive 'P. Franc Vs. K. Morris (version 2.5)'  ;match current case name
			;optionally could have assembled full window title using sw's SystemInfo
			;and CurrentCase object
			status = true
	return status
}

One area for improvement in the SummationIsActive function would be with the product name check, where the .vs script could have used SystemInfo.BlazeName in order to dynamically set the correct window prefix. That way the Enterprise product line would be covered also. Another alternative would have been to use the ahk_class of the main Summation window, e.g. SummationBlazeClass, in place of the window title. Using the ahk_class, which can be discovered via the Window Spy utility that ships with AutoHotKey,is particular helpful in scenarios where the title of the target window is dynamic and can't be matched extactly. Also helpful in scenarios where the section of the title that remains constant could conceivably appear as part of another program's title. As an example, the IfWinActive search could look for the string 'Mozilla Firefox' but be tricked if it finds an already open IE tab that contains 'Mozilla Firefox' in its title. Substituting Firefox's ahk_class, _MozillaUIWindowClass, would get around that problem.


Now some of the caveats:

  • General to ahk usage:

    • The AutoHotKey program must either be installed on the target machine in order for the .ahk script to run in the background, or the .ahk must be compiled into a standalone .exe, see Convert a Script to an EXE on the AutoHotKey website for further details.
    • The Hotstring behavior as a whole almost always works but once in a while the abbreviation will not auto-replace into the longer string. Try again immediately and it works as expected.
    • Some options described above, like the setting the EndChars variable to accept only the colon character, are universal to the script and might affect other AutoHotKey behavior, should the app already be in use for other purposes. The ahk codebase is robust enough that, in such scenarios, there are almost definitely workarounds available.
  • Specific to this project:

    • Since the SummationIsActive function checks the title of the currently active window, there are certain areas of Summation in which the hotstrings will not be activated because the active window no longer has 'CT Summation...' in its title. Examples include being within a transcript note and/or within a given view whose state has been set to Floated. It may be possible to capture sub-windows using additional title checks or testing the ahk_class, values for which can be retrieved via Window Spy. Or, depending on the distinctiveness of the Lookup table entries, the SummationIsActive could be removed and the hotstring activation set to be case-sensitive.