Script Triggers: Using the Keystroke Trigger

Adding Script Triggers has opened up a world of possibilities for FileMaker developers. Each different type of script triggers has it’s own nuances and pitfalls to be aware of. In fact, with script triggers, FileMaker, in some ways, begins to expose the complexity of more advanced programming environments. Nothing illustrates this more than the `OnLayoutKeystroke` and `OnObjectKeystroke` triggers. In this article, we’ll look closely at keystroke triggers: the most powerful and complex of the triggers in FileMaker 10.

>Download: We have developed a free (public domain) tool to help explore and discover keystroke modifiers and codes. You can download the [Keystroke Companion][key] and use it as you see fit.

[key]: http://sixfriedrice.com/wp/wp-content/uploads/2009/01/keystroke-companion.zip “Download the Keystroke Companion helper database”

##Overview

In this article we’ll dig deep into the `OnObjectKeystroke` trigger. With this trigger, whenever someone presses **any key** while an object has keyboard focus, the script you specify will run. It’s pretty straight forward in concept.

>Note: Keyboard Focus, to FileMaker, means different things to different objects. The most typical case is a field. When you click into a field, it has focus. If that field object has an `OnObjectKeystroke` trigger, it will fire with each key. You can also attach keystroke triggers to buttons and tab controls. These have focus when you add them to the tab order, then tab to them, so that the button or one of the tabs has the thick black border. Portals and Web Viewers never have keyboard focus (but a *field* on a portal can, of course).

This trigger becomes extremely useful when you are trying to give your user immediate feedback about invalid input. For instance, say you have a phone number and you don’t want any letter’s in it or perhaps you only want someone to be able to enter a maximum of one character in the middle initial field. The best way to explain is to show it in action, so I am going to create a Keystroke Trigger that will prevent the user from entering more then 5, numerical characters into a field called `ZIP_CODE`. You can do this sort of thing with a field validation, but when you do, the user has to _make a mistake_ before you tell them what went wrong. With a keystroke trigger, you can process the input as it is typed, giving immediate feedback. This is something that simply wasn’t possible before FileMaker 10.

##Figuring out the keys

To limit our `ZIP_CODE` field to 5 numerical characters we first have to determine which keystrokes the user has typed. FileMaker 10 has a new function `Get ( TriggerKeystroke )` which gives the value of the keystroke that triggered the script. So if I was typing in my `ZIP_CODE` field and I hit the number 1 it would return 1 when I call `Get ( TriggerKeystroke )`. Simple enough. But we don’t really want to have to write a list of all the values we are going to allow so filemaker gave us another new function `Code ()` which give us the Unicode Value for a given character. For Instance `Code (“1”)` yields the number 97. This doesn’t seem like a whole heck of lot of use to us but the Unicode Values are organized in such a way that it’s much easier to isolate the values you need. There are many lists out there for finding the unicode value of a character. [This is the one][ascii] I used.

[ascii]: http://didgood.com/programing/datatheory/ASCII%20Table%20-%20Extended%20ASCII%20Codes.htm

>Note: Unicode is a _vast_ character set. Luckily, for the most basic roman alphabet letters, numbers, and punctuation, the codes match the simpler and more familiar ASCII standard. None the less, if you need to deal with Japanese, Arabic, or Desert characters (for instance) you’ll have to deal with more complexity.

##Allowing Certain Keys
Here are two very useful functions to show how using the unicode value makes life much easier:

KeystrokeIsLetter:

Let(
keyCode = Code ( Get ( TriggerKeystroke ) );

( keyCode >= 97 and keyCode <= 122 ) or //all Lowercase letters ( keyCode >= 65 and keyCode <= 90 ) //all Uppercase letters ) The above function results in true whenever a letter (lower or uppercase) is typed. If you wanted to check for all these letters you would have to written a case statement with 56 different test cases! That would not have been fun. Also you'll notice that a lowercase "a" (97) is different then an upper case "A" (65). This is actually really great because when you want to get more a little tricker with your triggers you can isolate upper or lower case letters as needed. We're not really interested in letters here so lets look at the comparable custom function for numbers: KeystrokeIsNumber: Let( keyCode = Code( Get ( TriggerKeystroke ) ); keyCode >= 48 and keyCode <= 57 ) Using this function we can make sure that they hit a number key, so we're ready to get started. ##Putting It All Together Now that I can isolate the number keys it's time to write on our script. The script that runs should return false if the incorrect keys are pressed. This causes FileMaker to _cancel_ the keystroke, meaning it never appears in the underlying field. You've blocked it in your trigger script. Our script should be very straight forward using the custom functions above. Here is the code of our script: If[not KeystrokeIsNumber ] Beep Exit Script[Result: False] End Now that we disallow any non-numerical keys we need to make sure that we don't allow entry of more than five characters, so lets add that logic: If[not KeystrokeIsNumber or Length ( TEST::ZIP_CODE ) >= 5]
Beep
Exit Script[Result: False]
End Id

If you test this field, you’ll quickly discover a problem (and a pretty serious problem at that). Since we only allow number key presses, we’ve inadvertently blocked several useful keys: Arrow keys, the Delete key, the Tab key, and even the Enter key. When it comes time to edit a value you’ve already entered, nothing works. This is one of the problems that you will run into when you use these filters. You pretty much always want to allow the navigation keys to go through. Don’t worry though, here is another custom function for finding editing keys:

KeystrokeIsEditing:

Let(
keyCode = Code( Get ( TriggerKeystroke ) );

keyCode = 8 or
keyCode = 9 or
keyCode = 10 or
keyCode = 13 or
( keyCode >= 27 and keyCode <= 32 ) or keyCode = 127 ) Adding the editing keys to the code yields: If[not KeystrokeIsEditing] If[not KeystrokeIsNumber or Length ( TEST::ZIP_CODE ) >= 5]
Beep
Exit Script[Result: False]
End
End

Now we’re are really close. There is just one final issue that we need to address. This one is even more difficult to spot then the arrow key problem. Let’s say someone were to select the number in `ZIP_CODE` field and then start typing. What would happen? Well if there were 5 characters already in the field, they just get a beep. But since they have text selected, and they are _replacing it_ with a new entry, the keystrokes really should be allowed. That’s a problem. To fix it we can use another handy function that FileMaker has graciously provided: `Get (ActiveSelectionSize)`. This function returns the length of the selection in the active field. In other words, if you select the word “Cardinals” in a field containing “The Cardinals are…imperfect”, then `Get (ActiveSelectionSize)` would return `9`.

## Final Answer

Our 100% complete script now looks like so:

If[not KeystrokeIsEditing]
If[not KeystrokeIsNumber or (Length ( TEST::ZIP_CODE ) >= 5 and Get (ActiveSelectionSize) = 0)]
Beep
Exit Script[Result: False]
End
End

Now that we have our script we simply attach it to our field and we are done. To do this just right click on the `ZIP_CODE` field object and select `Set Script Triggers…` from the drop down menu. A window pops up like the one shown below. Now select the `OnObjectKeystroke` event and finally select your script. I would make sure that only browse mode is checked because you have many more keys that you would want to allow in find mode.

FileMaker-10-Script-Trigger

Phew, we made it through. Script Triggers are definitely a little bit of work to setup but I think they provide a wide range of new and interesting functionality. Happy Script Triggering everyone.

Leave a Comment

22 thoughts on “Script Triggers: Using the Keystroke Trigger

  1. A very interesting article on a topic that is a real iceburg. Script triggers require a whole new way of thinking and can lead to quite unexpected results. It took me quite a while to realise that onobjectexit object scripts run and before the object is exited. To trigger navigation scripts I needed to create onobjectentry triggers on the next field in the tab sequence.
    I honestly feel that the script triggers will have as big an impact on our database designs as the shift from Filemaker 6 to 7. I think there will be a big learning curve, but the the possibilities to create amazing things are almost unlimited.
    Thanks for your great efforts in helping us come to grips with all the new stuff.

  2. I have this problem. When I use the space bar as a keyboard script to set the current time. I think go to next field. It put the space in the next field.

    Oreste

  3. Oreste:

    If your triggered script exits with a non-true result, the space keystroke will be canceled, meaning it won’t have its normal effect of putting a space in a field. Just add this to your script:

    Exit Script [ False ]

    Geoff

  4. Geoff:

    I sent you some emails today touching on this subject in regards to limitting field entry, however completely missed this article for some reason, so feel free to ignore the emails.

    I could have saved myself some figuring out if only I had looked.

  5. To keep the script dynamic in terms of field names you may wish to use

    Length ( Evaluate(Get(ActiveFieldName)) ) >= Get(ScriptParameter) and Get (ActiveSelectionSize) = 0

    The script parameter in my case being the length I wish to restrict the field to.

  6. In the _Figuring out the keys_ paragraph, last sentence: “This is the one I used” link. If you click on it you’re taken to a ASCII chart page, but I thought I was going to be taken to the Unicode chart that was mentioned in the previous sentence. Did I miss something or somebody slipped me a cup of decaf coffee this morning? 🙂

  7. Rich:

    I will draw your attention to the “Note” box right after the link 😉 It is true that FileMaker uses Unicode, and the chart Jesse linked is an ASCII table. But Unicode and ASCII match each other, as far as FileMaker is concerned, for the most common roman text, numbers, and punctuation.

    The trouble is that unicode can’t really be put in a table of any reasonable size. The Character Palette in Mac OS X, which has a table of unicode values, shows 196,607 spots in the table for characters (many of which are empty). If all you got was this big table in a static form it would be largely useless. If you need to deal with more of unicode, you’re best off with an interactive tool (like Mac OS X provides — not sure if there is an equivalent on Windows).

    So we were trying to keep it simple 🙂

    Geoff

  8. Thanks, Geoff–I guess I was being too editorially picky with the syntax more than the thought behind the text. (“Should ‘anal retentive’ be hyphenated?”) You’re right, though: Unicode differs quite a bit from ASCII when it comes to common punctuation characters but otherwise it’s identical. Now where can I find those cuneiform characters…

    One question, though: in the same paragraph I mentioned earlier, where it says, “For instance, Code (“1”) yields the number 97. For grins, I created an Auto Enter calculation defined as such but when I enter a 1 I see a 1, not 97. Referring back to the ASCII chart, 97 yields a lower-case “a.” So, am I coding this example incorrectly, or…?

  9. I downloaded the Keystroke Companion and when I opened it, it won’t take any character. I get the alert “Before typing, press Tab or click in a field, or choose the New Record menu command.” I also see in the Keystroke Trigger script, lines 4-6 have “Function missing”.

  10. Excellent solution! Thanks for sharing. Do you have your Zip Code solution available as a download? Thanks!

  11. Thanks for the great article and your demo file is a great help.

    I have been experimenting with using ‘OnLayoutKeystroke’ with a Web Viewer to run keyboard macros for ‘Back’, ‘Home’, ‘Forward’ and ‘Grab’ (grabs specific content in a web page).

    I get erratic behavior. When the page first loads, without clicking within the web viewer, the commands will work, where appropriate (loads the Home Page and Grab). However, once the user clicks on a link within the web viewer, I have to click outside the web viewer to get the LayoutKeystroke recognized. I tried assigning the script to ‘OnObjectKeystroke’ but it did not seem to help. This behavior seems contrary to what you state above “Portals and Web Viewers never have keyboard focus.” It does seem to make a difference.

    Also, I will often get the error “Before typing, press Tab or click in a field, or choose the New Record menu command.” I have added an Exit Script right after I run my keystroke script. I also tried turning on Error Capture. Neither seemed to help.

    Any suggestions?

  12. I realise this is an old article however wanted to thank you for the keystroke companion, it saved me considerable time figuring out combinations to use for some interface trickery..

    I thought I would also mention a simple one

    For quite some time I have longed to remove the dialog box which shows ‘Before typing, press Tab or click in a field, or choose the New Record Command’.

    As you all know, this triggers when a key is pressed if a field is not in focus or a record not present, fair enough but on layouts where data entry is not the focus it confuses the user if he/she accidentally taps a key, telling them to click or press something which doesnt exist on a particular layout not to mention them then having to close the dialog before they can proceed.

    So a simple OnLayoutKeystroke set to trigger a script prevents this dialog ever showing

    If (Get(ActiveFieldName) = “”)
    Exit Script[Result:False]
    End IF

    Obviously the script could be expanded to allow Page Up / Down or other combinations which may be used.

  13. My thanks for your efforts to help spread the knowledge that you have gained the hard way.

    Much appreciated,
    Jim

  14. Robert: I’m not sure what you mean exactly. Do you want to chance the index language for a field to/from Unicode from a script? If so, then no, there is no way I know of. If you mean you want FileMaker to use something other than unicode to store text, then definitely no. If you mean something else, let me know 🙂

  15. Geoff,

    well then i quess not!! i just think its easier to manipulate text when sometimes its on or off. thanks

  16. i want to trigger a script after pasting text into a field. Generally the pasting is done by using apple keyboard keys: command + V.

    so bottom line: how do i do that and can one actually Get ( TriggerKeystrokes values for all the apple keyboard keys??

    rob

    ps its much better if you emailed me directly i am so busy i keep forgeting to come back here for your answers. thank you for your time.