Dynamic Finds in FileMaker 10

In an earlier article we explored an isolation technique that was specifically designed to work on ID’s and that’s it. There is a lot more that can be done with the new Set Field By Name step and in this article we’ll take a look at a slightly more complex but much more versatile search method. This technique will rely on the use of our dictionary functions, so taking a look at this article is a definite prerequisite.

The Basics

First off lets go over the basic concept. Essentially, the idea is that with Set Field By Name step, we can now pass in the field we want to search as well as the value to search for. So, instead of having to script a bunch of hard coded Set Fields we can call one find script, pass in a bunch of find criteria and have that script set the fields we want dynamically. With this new system, you can have one script perform just about any find you need.

Before our script will work, we need a system to pass the name of the search field as well as the value to search. Using our parameter passing technique it’s actually pretty simple to send the information. To pass in a specific field name we can use the GetFieldName() in conjunction with #(). Here’s the example#(getfieldname(yourfieldnamehere); criteria). So, let’s say you have the NAME field in a table called CUSTOMER and you want to find Sarah. Here’s what I need to pass into the script:

#( GetFieldName( CUSTOMER::NAME ) ; "Sarah" )

At this point we’ve determined what we want to search for and where we want to search. Now we need to write a script that will take this information and generate our search. In concept it’s pretty simply. Just loop through any parameters and simply set the specified fields to the value provided. Now, since we are using theSet Field By Name script step, be sure that the field name you pass in is the fully qualified field name and that you have the right context before you call the find script. So, if you are on a layout on the CUSTOMER table and you want to search the ID field be sure that the field name you pass in is CUSTOMER::ID. If you pass in ORDERS::ID, you’ll never find anything!

Here’s the actual script:

A script that finds records using Set Field by Name

The first order of business is to take all of the parameters that were passed into the script and stick them into a variable so we can operate on them more easily. What you end up with in the $dict variable is the full dictionary of passed in parameters. Now, we enter find mode, enter a loop and iterate through the dictionary values to create our individual criteria.

The loop is a little complicated. It uses a couple different of our custom functions in our previous article. We use DictFirst to grab the name of the parameter on the top of the list, then DictGet to get the value that corresponds with the name we’ve found. Those two values are then used to Set Field By Name. Finally we need to remove that entry from the dictionary. Once we’ve emptied all the entries out of the dictionary, we exit the loop.

Note: If you pass in the same field multiple times the final value passed in will be the one ultimately used. You’re basically just overriding the value every time you pass in the same field.

To perform the find, we use our tried and true Safe Find script and then return the error to make sure the calling script knows what happened.

This system has the potential to save us a tremendous amount of time. Basically, you can construct your finds on the fly by calculating the names of the search fields and the according search value. This find script is relatively simple and we’ll be fleshing it out to allow for multiple requests and omits, but this is a good first step.

Note: The find script itself does not navigate to any layouts. We’ve designed it around the context independent scripting principles that we here at Six Fried Rice love so much. Before calling the find script, make sure you are in the correct context for the find you are trying to perform.

find-database

Script Triggers: Monitoring and Restoring Tabs

FileMaker 10’s new Script Triggers feature just keeps delivering. You might, at first, think triggers are limited to layouts and fields, but it turns out your triggers can fire with all kinds of objects. In this article we’ll show you how to run scripts when users switch tabs on your tab controls. In the end, we’ll also revisit a common FileMaker problem: tabs switch when you switch layouts. We’ll show you how to solve this problem quickly and easily using script triggers.

Triggers with Tabs

It is unfortunately not entirely obvious, but Script Triggers work with tab controls. Specifically:

A tab control with keyboard focus, showing the thick black border around the active tab.

Watching the Tab Change

Once you know all that, keeping tabs on your tabs (sorry) is easy. For example, suppose you want to show a special message under some circumstances when someone switches to a particular tab. Setting this up couldn’t be easier:

  1. Write a script that does what you want.
    For example, it might show a dialog box, calculate a total, set a global, or clear a selection.

  2. In Layout mode, select your tab control, then choose Format -> Set Script Triggers…
    The Set Script Triggers dialog box appears.

  3. Turn on the OnObjectModify checkbox.
    You’ve just told FileMaker you want to run a script, so it asks you which script to run.

  4. Select the script you created in step 1.
    When you’re done here, click OK, then switch to Browse mode. You’re ready to test.

As this video attests, your script now runs as soon as the user switches to any tab:

Which Tab

If you watched the video closely, you may have noticed that the scripted dialog box showed which tab was selected. With only one trigger script, how do you manage this? You just have to ask.

FileMaker’s esoteric GetLayoutObjectAttribute function can tell you all kinds of information about objects on your layout. Of particular interest, it can tell you if a tab panel is front most. To use it, though, you first need to give your tabs object names. We’ve showed you how to do this before but here’s a quick refresher. If you’re already an object names expert, you can skip ahead.

  1. In Layout mode, click on your tab control to select it.
    When you click, the first tab gets a thick black border. This tells you that specific tab panel is selected. (You can have an entire tab control selected, without selecting any of its individual panels. You’ll see this in a moment.)

  2. Choose View -> Object Info to show the Object Info palette.
    This is where you go to set object names (among other things). Since its a palette, you can simply leave it open while you work through all your tabs.

  3. In the Object Name box in the Object Info palette, type a unique and identifying name for your tab.
    You might call it “tab1″ or “customer tab” or whatever you feel is appropriate.

  4. Click the second tab in your tab control.
    The second tab comes to the front but it is not selected. Notice it doesn’t have a thick border.

  5. Click once again on the second tab.
    This time it gets its thick border. This is important. You can only name a tab panel once you have it properly selected.

  6. Give this tab a unique name, then repeat these steps to name every tab in your tab control.
    If you want to find out if a particular tab panel is in the front, it must have a name, so you’re best of naming them all.

Once you’ve given every tab panel a name, you’re ready to start asking FileMaker who’s in front. This part is easy. Here’s the long-but-simple formula to see if a tab is in front:

GetLayoutObjectAttribute ( "myTab", "isFrontTabPanel" )

Notice that this formula only looks at one particular tab panel (in this case it is looking at the panel called myTab so be sure to put one of your tab panel object names there instead). It will return a True result if the tab panel is in front, and a False result otherwise. To determine which of all the tabs is in front, you can check them one by one:

Case(
   GetLayoutObjectAttribute ( "myFirstTab", "isFrontTabPanel" ); "First tab is in front";
   GetLayoutObjectAttribute ( "mySecondTab", "isFrontTabPanel" ); "Second tab is in front";
   GetLayoutObjectAttribute ( "myThirdtTab", "isFrontTabPanel" ); "Third tab is in front"
)

Or, in a script:

If [ GetLayoutObjectAttribute ( "myFirstTab", "isFrontTabPanel" ) ]
   # do stuff for the first tab
Else If [ GetLayoutObjectAttribute ( "mySecondTab", "isFrontTabPanel" ) ]
   # do stuff for the second tab
Else If [ GetLayoutObjectAttribute ( "myThirdtTab", "isFrontTabPanel" ) ]
   # do stuff for the third tab
End If

Restoring Tab State

Now that you know how to watch for tab changes, and figure out which tab is in front, you can take it one step further, and solve an age-old FileMaker problem in the process. Last year, we wrote up a laborious technique to remember which tab is in front before you leave a layout, then bring it back to the front when you come back. This way, if your scripts have to quickly zip to another layout to get some work done, your users don’t get distracted by tab controls that keep switching back to the first tab.

That technique worked, but it was tedious. You had to write two long and complicated scripts. Then you had to call one every time you leave the layout, and the other every time you come back. What a nuisance.

Using Script Triggers, you can save all the hassle. Now two very simple scripts can get the job done, and they can do it automatically every time you return to the layout.

Note: In fact, this new, simpler version is even better than the old way. The tab will restore itself even if you manually leave the layout and come back later, making things a touch more stable for your users.

First, you need a simple script to remember the front most tab:

Set Variable [ $$FRONT_TAB;
   Case(
      GetLayoutObjectAttribute ( "myFirstTab", "isFrontTabPanel" ); "First tab is in front";
      GetLayoutObjectAttribute ( "mySecondTab", "isFrontTabPanel" ); "Second tab is in front";
      GetLayoutObjectAttribute ( "myThirdtTab", "isFrontTabPanel" ); "Third tab is in front"
   )
]

That’s it (and its really a one line script, although we put it on a few lines here so it would be easier to read. Just set a global variable to the name of the font most tab.

Note: If you have multiple tab controls to keep track of, make sure you use a unique variable name (or repetition) for each one. Otherwise, changes to one set of tabs will impact the others.

Now attach an OnObjectModify trigger to your tab control and set it to run the script. With these two steps complete, a new global variable will always remember which tab you last visited.

Next, you need to make FileMaker restore the front most tab whenever you come to this layout. This part is equally easy. Start with a simple script:

If [ Not IsEmpty($$FRONT_TAB) ]
   Go to Object [ $$FRONT_TAB ]
End If

This script first checks to see if the $$FRONT_TAB variable has a value. If it does, it uses the Go to Object step to bring that tab to the front.

Finally, tell FileMaker to run this script whenever the layout loads. You do this with (shocker) a script trigger. Choose Layout -> Layout Setup, then switch to the Script Triggers tab. Turn on the OnLayoutLoad trigger and point it to your script.

Here’s a video showing the tab behavior with and without the triggers:

In the first version (before the fade-to-black) FileMaker switches back to the firs tab every time you leave the layout and come back. In the second, it seamlessly keeps things in order. This magic works whether your user leaves the layout manually, or you do it with script. Easy as pie.

Make the Status Toolbar Work for You

FileMaker 10’s most visible new feature is the completely redesigned Status toolbar (formerly called the Status area). Perhaps because of its prominent position, or because it is such a striking departure from a 20-year FileMaker mainstay, this change has been met with its fair share of controversy. In this article we’ll show you why you don’t need to worry, and how you can make the new Status toolbar work for you.

In FileMaker 10, the standard Status toolbar looks something like this:

The standard Status toolbar in FileMaker Pro 10

If you leave the Status toolbar open, your user will see prominent New Record and Delete Record buttons (among others). At first this might seem nuts. After all, many of you have special buttons on your layouts specifically for record creation and deletion, and you certainly don’t want people willy-nilly switching layouts to the wrong view, confusing themselves in the process. But it turns out you have more control over these buttons than most people realize. You cannot add your own buttons or custom icons, but you can:

Note: If you have the equivalent menu commands disabled in your database, then the buttons are already disabled too. A database with locked-down user interface in 9 is just as locked down in 10.

Disabling Buttons

There are three ways to disable buttons in the Status toolbar (not all apply to all buttons). You can set up custom record privileges to control who can create and delete records. You can restrict access to menu commands using a privilege set; and finally, you can use custom menus to remove troublesome menu commands. Which you use is a matter of your security and user interface needs.

Custom Record Privileges

Using custom record privileges you can remove the ability for a user to create or delete records for any particular table. When you do, the associated status toolbar button will disable as appropriate. This even works for so called “record level access” privileges. For instance, you can remove the ability to delete a record only once its Status field has been set to “shipped.” When you do, the Delete Record button will disable when you view shipped records.

Setting this up is easy:

  1. In your database, choose File -> Manage -> Accounts and Privileges.
    The Accounts & Privileges dialog box appears.

  2. Open one of your existing privilege sets, or create a new privilege set.
    You’ll probably need to switch to the Privilege Sets tab. If you’re doing this for the first time, it is probably easiest if you duplicate the [Data Entry Only] privilege set so you start off with some reasonable privileges.

  3. From the Records pop-up menu, make a reasonable choice.
    For example, if you want to remove the ability to delete records of any kind, choose “Create and edit in all tables.” For the ultimate in control, choose “Custom privileges.” This window shows a privilege set configured so users can create records in any table, but can only delete Vendor records:

The Custom Record Privileges dialog box with Delete permission removed for one table.

Once you assign an account to this privilege set, when that user logs in, she’ll see the Delete Record button disabled when she looks at Customer records:

Restricted Menu Access

The record privileges don’t effect the other toolbar buttons (since they have nothing to do with creating or deleting records). Also, for databases where security isn’t terribly important, and preventing casual or accidental unwanted actions is all you need, restricting record privileges can be heavy handed and hard to script around.

For cases like this, the easiest solution is to restrict menu access. If you revisit the Privilege Sets tab of the Manage -> Account & Privileges dialog box, you’ll discover that you can make gross restrictions to the menu commands your users get:

You can limit menu access for a privilege set.

If you switch to Editing Only or Minimal, FileMaker takes away all the powerful commands (like Import and Export, New Record, Delete Record, Find, and so forth). All the associated Status toolbar buttons will disable as well.

Custom Menus

For the utmost in control, you can turn to Custom Menus. This power feature is only available in FileMaker Pro Advanced, and gives you almost complete control of the menus, sub-menus, and commands your users see when they use your database. You’ll see some step-by-step instructions for using custom menus at the end of this article. For now, it will suffice to say that if you remove a menu command from a menu set, then when that menu set is active, the associated button in the Status toolbar will be disabled. In this way you can selectively disable any button you choose.

Renaming and Redefining Buttons

Disabling buttons is all well and good, if you’re a fan of taunting your users with permanently-out-of-reach temptation. But why not take it a step further. Embrace those buttons and make them do your dirty work. Taking control of the toolbar buttons requires use of custom menus (and, by extension, FileMaker Pro Advanced).

Imagine you have a database of customers and vendors. Your business rules require that you carefully control the process of creating and deleting records. For instance, you may need to set up web site access for any new customers, which requires running your fancy SQL scripts. Here’s how to express your authority and keep the Status toolbar buttons:

  1. From the File Menu, choose Manage -> Custom Menus.
    The (quite complex) Manage Custom Menus dialog box appears. This is grand central station for every menu command FileMaker can muster.

Note: In this example, you’ll use the “Custom Menu Set 1″ menu set FileMaker creates for you. Adjust the steps as necessary if you have your own custom menu sets already.

  1. In the Custom Menus tab, select the Records Copy menu.
    FileMaker ships with a customizable copy of all the standard menus to make it easy to squeeze small changes into otherwise-normal menus. This is the copy of the usual Records menu, where the New Record and Delete Record commands live.

  2. With the menu selected, click Edit.
    The Edit Custom Menu dialog box appears. On the left side, you see all the menu items in this menu.

  3. Select the New Record menu item.
    When you select a menu item, the right side of the dialog box fills in with options for the item. There’s lots of power here, so feel free to explore. You can see the options here:

The configuration options for a custom menu item.

  1. Look next to “Command” and make sure it says “New Record.”
    This is the key to this entire technique. This menu item is associated with the New Record command, which is a core command in FileMaker. When you adjust the behavior of the menu item associated with the New Record command, you also modify the behavior of the New Record button.

  2. Turn on the Action checkbox and select the script you use to create new records.
    When you add an action to the menu item, you get to pick either a single step or a script. In most cases you’re best off using a script (even if it is a one-liner) because it makes it easy to change later, even if several different menu sets include items that do the same thing.

  3. If you want, turn on the Title checkbox, click its associated Specify button, and enter a calculation to determine the menu title.
    For instance, you might decide you want your menu called “New Customer,” “Create Record,” or Pi * Random. Ok, probably not the last one, but the point is, you can use all the power of calculations to generate a dynamic and interesting title for your menu. Anything you do here will also influence the label under the New Record button in the Status toolbar.

  4. Select the Delete Record menu item and configure as you wish.
    Just make sure you keep Command set to “Delete Record…” so FileMaker knows you’re adjusting behavior for the delete action.

  5. Click OK to close the Edit Custom Menu dialog box.
    You’re now back in the Manage Custom Menus dialog box.

  6. From the “Default menu set for this file” pop-up menu, choose “Custom Menu Set 1.”
    When you make this change, you’re telling FileMaker you want the customized menu set to be used automatically for this database. You can also configure custom menu sets on a per-layout basis (See the Layouts -> Layout Setup dialog box in Layout Mode) or switch menu sets via scripts (using the Install Menu Set script step).

  7. Click OK once more to close this dialog box.
    Now you’re ready to test your changes. If all went well, the New Record and Delete Record buttons in the Status toolbar now bend to your will.

Dealing With the View As Buttons

The steps above show you how to attach your own actions to the New Record and Delete Record toolbar buttons. But at the outset, we also mentioned the potential confusion your users might get by accidentally clicking the View As toolbar buttons. Once again, you have two options here.

To disable any of these buttons you can disable a particular view on a per-layout basis. For instance, your Customer List layout only makes sense in List view, so turn off Form and Table views. This is super easy. In Layout mode, choose Layouts -> Layout Setup, switch to the Views tab, and uncheck the views you don’t want your users to use. When you do, the associated buttons in the Status toolbar will be disabled.

Note: You might take this opportunity to disable Table view in many of your layouts, since it has some potentially unwanted power. We discuss this in more detail here.

To get these buttons to run your own script, just override the View as Form, View as List, and View as Table commands using custom menus. You’ll find them in the View Copy menu if you use the built in custom menu set.

To Infinity and Beyond

Combining the various custom menu techniques, plus some clever naming conventions and scripts, you can get the Status toolbar really working for you. In this video demo, you can see a simple database that customizes the behavior of several toolbar buttons so this standard FileMaker user interface integrates seamlessly with your carefully controlled database.

Note: You can download the database in this video right here: My Database.fp7

In this example (which is anything but fully baked) we use the Get(LayoutName) function and some custom scripting to get the “View as Form” and “View as List” buttons to switch to appropriate layouts. We’ve also added a custom script to the New Record and Delete Record buttons, and renamed them so it is more obvious what they will do.

This kind of integration has several powerful advantages:

A lot of people have expressed frustration about the new Status area, complaining that FileMaker has taken away control they need. I hope this article makes it clear that we have more control than ever before.

Opinionated Note: A lot of the frustration about the Status toolbar seems to stem from the fact that it is now more obvious that you can, for example, delete a record. These commands used to be squirreled away in menus, and now they’re front-and-center. But of course a less visible menu command is just as dangerous in the wrong hands. So the new toolbar, perhaps, has simply made it more obvious to developers that they need to be careful.

The reaction to the new prominence of these commands illustrates another interesting reality. Menus have become no-man’s land. More and more in modern, popular applications, the commands you need are readily available in an explorable, friendly busy-box of interface goodness. People expect to see what they can do. I think this phenomenon really underscores the wisdom in the new Status toolbar. To keep current, FileMaker has to get a more modern user interface. It did, and suddenly people notice what FileMaker can do. That is, in my opinion, A Good Thing™. Editorializing over.

Script Triggers: Filter as You Type

For years, FileMaker developers have been devising various filter techniques. The idea is that you type all or part of a name, part number, description, etc… and a list of results filters down to show relevant matches. As handy as these techniques are, they always stop just short of perfect because, before FileMaker 10, you had to exit the field before the filter would take effect. Using FileMaker 10’s powerful Script Triggers, you can make the impact of your filtering more immediate.

Note: We’ve never done this before, but the idea, scripts, and sample file for this technique come directly from FileMaker luminary and Six Fried Rice reader David Graham of Bit Tailor. We’re publishing it here (with David’s permission) because it is an excellent example of Script Triggers solving an age-old problem in an elegant way.

There are as many ways to filter lists in FileMaker as there are developers implementing it. The beauty of this technique is that it doesn’t matter if you prefer exploded keys and filtered portals, aggregate text fields and a find, or (my personal favorite) scripted multi-request finds. Whatever you want, when it is fast, clean filtering you’re after, it is always better if the results appear as you type. This sort of thing was essentially impossible with FileMaker 9. Once a user started entering data in a field, you were hands-off until they finished.

Some people dreamed up horrifying solutions with looping scripts and all manner of wackiness. These techniques, in my opinion, never worked well. Consequently, we were always resigned to the “press Enter when you’re done” model. But when the results appear as you type, the user gets immediate feedback, only needs to go as far as necessary to get the result she wants, and doesn’t have to click back into the field to correct or make a change. So it is clearly a better model. See for yourself:

David’s technique relies on the handy OnObjectModify trigger on the filter field to kick off a script every time the field changes. This includes:

You may be tempted to use the OnObjectKeystroke trigger for something like this, but OnObjectModify gets you more bang for the buck: It handles cut and paste, and doesn’t require all the complexity of keystroke triggers.

The Code

I’ll let you explore David’s sample file for the full scoop, but the core of his technique can be found in two places. First, he uses an exploded key filtering technique, which I won’t cover her, except to say that the idea is to process some text data and produce a multi-line key that includes partial match values. In other words, if the value is “Test” then the key would match “T”, “Te”, “Tes” and “Test.” In this way, you can type all-or-part of the value and see the results. Look at David’s ExplodeToMultikey custom function to see how he accomplishes this.

If you prefer, you can use scripted finds and a list view layout to do your filtering instead.

But the interesting part is this very simple trigger script:

Set Variable [$currentObject; Get ( ActiveLayoutObjectName )]
Commit Records/Requests []
Go to Object [ $currentObject ]
Set Selection [ Start: Length (Get(ActiveFieldContents)) + 1]

That’s it. This script really just does two things:

  1. It commits the record, causing anything you’ve typed in the filter field to take effect.
  2. It returns you to the field, so if you type again, your keystrokes will go right on the end of the field.

In this way, as you type, your changes reflect immediately. Very cool.

Note: If you were using finds instead of exploded keys, you would add a a few more steps to your script to switch to Find mode, build a find request or two, and then perform the find.

You can download the working file right here.

Thanks to David for the great technique!

Script Triggers: Using Layout Triggers for a Dynamic Tab Orders

If you’re a male, do you really need to be asked you if you’re pregnant when you are at a clinic? If your female, do you need a prostate exam? Probably not. There are little nuances to data entry that occur with just about every solution that make it just slightly more tedious to collect information with just a standard tab order. We’re going to remedy that with using a simple layout keystroke trigger.

Dynamic Tab Order for FileMaker 10

In FileMaker there is only one way to navigate the tab order. This can be annoying to an end user who has to tab through a bunch of useless fields to get to the good stuff. The gender question above is a perfect problem to solve using a dynamic tab order in FileMaker 10.

Note: As Mikhail Edoshin points out, this process could be accomplished by attaching a script trigger directly to the field in question. The reason I chose to attach the trigger to the layout is so all tab order changes are centralized in one script. For instance if you had 4 or 5 different modifications to the tab order, you would still only have one script when using the layout trigger. If you were to place a trigger on each individual field you would need 4 or 5 scripts. That was really the main idea of this whole post but I left it out for some stupid reason.

Thanks for the heads up Mikhail.

A better way for our tab order to progress in our example is to just skip fields depending on which gender is choosen. Here is the psuedocode for what I think the tab order should look like:

IF the active field is Gender THEN
  IF Gender = Male THEN go to the prostate question END
  IF Gender = Female THEN go to the pregnancy question END
END

Finding our Key

The TAB, ENTER and RETURN as they are the three keys that filemaker uses to move to the next field.

Note:  You can specify one, two, three of these keys to progress to the 
next field in the Format >> Field/Control >> Behavior...

So using our knowledge from the previous article about keystroke triggers and consulting our ascii tables we know the following will return true if any one of those keys is pressed:

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

  keyCode = 9 or //Tab Key
  keyCode = 10 or // Enter
  keyCode = 13 // Retun
)

Overriding the tab order

Assuming that my table name is TEST and my field names are GENDER, PROSTATE_EXAM? and PREGNANT, we can write our script.

If[ Let( keyCode = Code( Get ( TriggerKeystroke ) ); keyCode = 9 or keyCode = 10 or keyCode = 13) ]
  If[Get( ActiveFieldTableName ) & "::" & Get( ActiveFieldName ) = getFieldName( TEST::GENDER )]
    If[ TEST::GENDER = "F"]
      Go to Field [ TEST::PREGNANT? ]
      Exit Script [Result: False]
    Else If [ TEST::GENDER = "M" ]
      Go to Field [ TEST::PROSTATE_EXAM? ]
      Exit Script [Result: False]
    End
  End
End

Notice on the second line of the script we combined two get functions to determine the table and name of the field and then compared that to the result of getFieldName( TEST::GENDER ):

Get( ActiveFieldTableName ) & "::" & Get( ActiveFieldName ) = 
getFieldName( TEST::GENDER )

The reason we do this is to make sure that if for some reason you ever decide to change the name of either the table or field the script won’t break.

You’ll also noticed that I use the step Exit Script [Result: False] after I navigate to each field. The purpose of this step is to reject the TAB, ENTER or RETURN key press. If we were to leave the Exit Script step out, FileMaker would process the keystroke and therefore go the the next field in the tab order after the field I just told it to go to. This would drive everyone crazy!

Once I’ve played with this technique a little more I’m sure it will prove quite useful. It definitely makes data entry a little more user friendly.

Next Page →