Add Back and Ahead Buttons
to Your Solutions
Rescue lost users-and make your
programs more intuitive-by adding navigation buttons to your
FileMaker Pro applications.
By Jonathan Stars
PRODUCT: This began with FileMaker Pro 4.0 but it will work with later versions. The solution is very complex and there are probably better ways to implement it. I think it serves more to prove that if you want to do something in FileMaker, you probably can.
Many
users don't always understand how to use the navigation buttons on
their screens. They might do something they didn't intend to, for
example, and end up in the middle of Filesville-with no idea how
to get back to where they were. Even the Main Menu button doesn't
help. However, navigation buttons (similar to the ones used in web
browsers) can be handy tools to help your users navigate your
FileMaker Pro solutions. Read on to find out how to add these
buttons to your applications. But keep in mind that this is a very complex solution. Since it was developed prior to FileMaker 7, these days it would probably be better to park the data that is stored in global fields as records in other tables.
Memorize your moves
What you need is a mechanism to memorize your moves through your
files, layouts and records- and a way to play them back. The
perfect place for the storage is in Global fields. The perfect
tool to play back the moves is ScriptMaker. Before you can create
the fields, you need a file. Select File > New, and name the
file Home. When the Define Fields window opens, create two utility
fields:
|
Field Name
|
Type
|
Options
|
|
"Connect"
|
Calculation
|
=1
|
|
"Record
ID"
|
Number
field
|
Auto-enter
|
All the other fields are Global text
fields. They'll be used for navigation storage. Name them as
follows:
gFile Back
gLayout Back
gRecord Back
gFile Ahead
gLayout Ahead
gRecord Ahead
gGo which File
gGo which Layout
gGo which Record
The first of the three Global fields put
down Hansel-and-Gretal-like bread crumbs, so you can retrace your
steps. When you do go backward, the second three fields make sure
the bread crumbs stay on the trail you're retracing. The last
three bread crumbs (uh, fields) tell you exactly where you are, so
the correct record, layout, and file are displayed. Exit the
Define Fields mode by clicking on the Done button. Normally, you
would go into the Layouts next. In this case, however, the first
layout with buttons and their attached scripts will be duplicated
to a second layout. So, you need to define the Scripts
first.
Scripts
One of the great things about FileMaker Pro is that there are
a number of ways of performing any given task. For instance,
another way to do much of the scripting I'm going to show you can
be accomplished using calculation fields. With Calculations, you'd
still need as many script steps. Each of them would just be
shorter, referring to an If calculation-but that means you
wouldn't be able to see everything that's going on. I prefer using
scripts because I can see exactly what's happening. Plus, I only
have to go to the script to debug any problems. In ScriptMaker,
create a Script called Remember Where You've Been. Now, this is
going to look like a lot, but much of it is quite repetitious. (If
you chicken out, you can go to http://www. advisor.com and
download a copy of the files). Enter the following steps (I'll
explain what's happening afterward):
Script Name: Remember Where You've
Been
If ["not
IsEmpty(gFile Ahead)"]
Set Field ["gFile Back", "If(IsEmpty(gFile
Back), Status(CurrentFileName), gFile Back & "¶"
Status(CurrentFileName))"]
Set Field ["gLayout Back",
"If(IsEmpty(gLayout Back), Status(CurrentLayoutNumber), gLayout
Back & "¶" & Status(CurrentLayoutNumber))"]
Set Field ["gRecord Back",
"If(IsEmpty(gRecord Back), Record ID, gRecord Back & "¶"
& Record ID)"]
Loop
Set Field ["gFile Back",
"gFile Back &"¶" & Right(gFile Ahead, Length(gFile
Ahead) - Position(gFile Ahead, "¶", 1, PatternCount(gFile
Ahead, "¶")))"]
Set Field ["gLayout
Back", "gLayout Back &"¶" & Right(gLayout Ahead,
Length(gLayout Ahead) -Position(gLayout Ahead, "¶", 1,
PatternCount(gLayout Ahead,"¶")))"]
Set Field ["gRecord
Back", "gRecord Back &"¶" & Right(gRecord Ahead,
Length(gRecord Ahead) -Position(gRecord Ahead, "¶", 1,
PatternCount(gRecord Ahead,"¶")))"]
Set Field ["gFile Ahead",
"Left(gFile Ahead, Position(gFile Ahead, "¶", 1,
PatternCount(gFile Ahead "¶"))-1)"]
Set Field ["gLayout
Ahead", "Left(gLayout Ahead, Position(gLayout Ahead, "¶", 1,
PatternCount(gLayout Ahead,"¶"))-1)"]
Set Field ["gRecord
Ahead", "Left(gRecord Ahead, Position(gRecord Ahead, "¶", 1,
PatternCount(gRecord Ahead,"¶"))-1)"]
Exit Loop If
["IsEmpty(gLayout Ahead)"]
End Loop
Else
Set Field ["gFile Back", "If(IsEmpty(gFile
Back), Status(CurrentFileName), gFile Back & "¶"
&Status(CurrentFileName))"]
Set Field ["gLayout Back",
"If(IsEmpty(gLayout Back), Status(CurrentLayoutNumber), gLayout
Back & "¶" & Status(CurrentLayoutNumber))"]
Set Field ["gRecord Back",
"If(IsEmpty(gRecord Back), Record ID, gRecord Back & "¶"
& Record ID)"]
End If
Set Field ["gFile Ahead", """"]
Set Field ["gLayout Ahead", """"]
Set Field ["gRecord Ahead", """"]
The basic part of this script comes after
the Else step. As you make a move to a different layout, record,
or file, the script remembers the place you leave and puts a
return between entries. Everything above the Else step is there to
let you jump from anywhere in the Back and Ahead chain to a new
place, while retaining all your moves since the beginning of the
session. The Loop section keeps all your moves in the order they
were originally in. The next five scripts only have two steps in
them. The first step in each of them is:
Perform Script
[Sub-scripts, "Remember where you've been"]
Here are the definitions for those
scripts:
Script name: Go to Layout 1
Perform Script
[Sub-scripts, "Remember where you've been"]
Go to Layout ["Layout #1"]
Script name: Go to Layout 2
Perform Script
[Sub-scripts, "Remember where you've been"]
Go to Layout [original layout]
Script name: Go to next Record
Perform Script
[Sub-scripts, "Remember where you've been"]
Go to Record/Request/Page [Next]
Script name: Go to previous Record
Perform Script
[Sub-scripts, "Remember where you've been"]
Go to Record/Request/Page [Previous]
Script name: Go to File #2
Perform Script
[Sub-scripts, "Remember where you've been"]
Perform Script [Sub-scripts,
<unknown>]
You can't, however, tell the "Go to
Layout 2" script to go to Layout 2 because the Layout hasn't been
made yet. Plus, there's no external script to take you to the
other file in "Go to File #2". Some of what follows will only be
necessary so you can create these files with these layouts to
literally watch how this solution works. When you do this for your
own solution in existing files, these layouts and the Global
fields need never be seen. The scripts will be subscripts within
your own scripts attached to your buttons. As you move back and
forth between the files, the Land Here script is used by the other
file as a place to, well... land. For now, let the "Go to
Record/Request/Page" step remain on [First]. We'll come
back and change it later.
Script Name: Land Here
Go to Layout ["gGo
which Layout"]
Go to Record/Request/Page [First]
Halt Script
The Clear script empties out all the
navigation fields whenever the file opens, preparing for the
session. You can also use it to start over when you run
experiments with the file:
Script Name: Clear
Set Field ["gFile
Back", """"]
Set Field ["gLayout Back", """"]
Set Field ["gRecord Back", """"]
Set Field ["gFile Ahead", """"]
Set Field ["gLayout Ahead", """"]
Set Field ["gRecord Ahead", """"]
Find All
Enter Browse Mode
Before going on to the final scripts,
click on OK > Done to exit ScriptMaker. Select Edit >
Preferences > Document. Look at the section in the dialog under
the heading When opening "Home".
Click on the box next to Perform script.
Now, click on the pop-up menu on the same line and choose the
Clear script you just created.
Finally, the scripts you've all been
waiting for: Back and Ahead! They're pretty lengthy. However, like
the Remember Where You've Been script I showed you earlier, most
of it is repetitious.
These scripts refer to a file I haven't
shown you yet called Away. Because most of the scripts, layouts,
and buttons will be duplicated over there, it'll be easier to
refer to them now with incomplete script steps. Later, you can
copy the file and make the necessary changes. The place where the
changes will go are highlighted with double asterisks **. Open
ScriptMaker and get started!
Script Name: Back Button
If ["not IsEmpty(gFile
Back)"]
Set Field ["gGo which File", "Right(gFile
Back, Length(gFile Back) - Position(gFile Back, "¶", 1
PatternCount(gFile Back, "¶")))"]
Set Field ["gGo which Layout",
"Right(gLayout Back, Length(gLayout Back) - Position(gLayout Back,
"¶", 1, PatternCount(gLayout Back, "¶")))"]
Set Field ["gGo which Record",
"Right(gRecord Back, Length(gRecord Back) - Position(gRecord Back,
"¶", 1, PatternCount(gRecord Back, "¶")))"]
Set Field ["gFile Ahead", "If(IsEmpty(gFile
Ahead), Status(CurrentFileName), gFile Ahead & "¶"
Status(CurrentFileName))"]
Set Field ["gLayout Ahead",
"If(IsEmpty(gLayout Ahead), Status(CurrentLayoutNumber), gLayout
Ahead & "¶" & Status(CurrentLayoutNumber))"]
Set Field ["gRecord Ahead",
"If(IsEmpty(gRecord Ahead), Record ID, gRecord Ahead &
"¶" & Record ID)"]
Set Field ["gFile Back", "Left(gFile Back,
Position(gFile Back, "¶", 1, PatternCount(gFile Back,
"¶")) - 1)"]
Set Field ["gLayout Back", "Left(gLayout
Back, Position(gLayout Back, "¶", 1, PatternCount(gLayout
Back, "¶")) - 1)"]
Set Field ["gRecord Back", "Left(gRecord
Back, Position(gRecord Back, "¶", 1, PatternCount(gRecord
Back, "¶")) - 1)"]
If ["gGo which File = "Away""]
** Set Field []
** Perform Script
[Sub-scripts, <unknown>]
End If
Go to Layout ["gGo which Layout"]
** Go to Related Record []
End If
For the Back Button, the script is
saying: Grab the last bread crumb, put it in the "Ahead" box, and
take me to where it was lying on the path in the woods. The Ahead
button does the same thing in the opposite direction. The script
looks for the last line (defined by the last return) in the gFile
Back, gLayout Back, and gRecord Back Global storage fields. It
peels off that information and puts it in the g_Ahead set of
fields. It also sends a copy to the gGo which fields, so it knows
where to send you. Then, it takes you there.
Script Name: Ahead Button
If ["not
IsEmpty(gFile Ahead)"]
Set Field ["gFile Back", "If(IsEmpty(gFile
Back), Right(gGo which File, Length(gGo which File) - Position(gGo
which File, "¶", 1, PatternCount(gGo which File, "¶"))),
gFile Back & "¶" & Right(gGo which File, Length(gGo
which File) - Position(gGo which File, "¶", 1,
PatternCount(gGo which File, "¶"))))"]
Set Field ["gLayout Back",
"If(IsEmpty(gLayout Back), Right(gGo which Layout, Length(gGo
which Layout) -Position( gGo which Layout, "¶", 1,
PatternCount(gGo which Layout, "¶"))), gLayout Back &
"¶" & Right(gGo which Layout, Length(gGo which Layout) -
Position(gGo which Layout, "¶", 1, PatternCount(gGo which
Layout, "¶"))))"]
Set Field ["gRecord Back",
"If(IsEmpty(gRecord Back), Right(gGo which Record, Length(gGo
which Record) -Position( gGo which Record, "¶", 1,
PatternCount(gGo which Record, "¶"))), gRecord Back &
"¶" & Right(gGo which Record, Length(gGo which Record) -
Position(gGo which Record, "¶", 1, PatternCount(gGo which
Record, "¶"))))"]
Set Field ["gGo which File", "Right(gFile
Ahead, Length(gFile Ahead) - Position(gFile Ahead, "¶", 1,
PatternCount(gFile Ahead, "¶")))"]
Set Field ["gGo which Layout",
"Right(gLayout Ahead, Length(gLayout Ahead) - Position(gLayout
Ahead, "¶", 1, PatternCount(gLayout Ahead,
"¶")))"]
Set Field ["gGo which Record",
"Right(gRecord Ahead, Length(gRecord Ahead) - Position(gRecord
Ahead, "¶", 1, PatternCount(gRecord Ahead,
"¶")))"]
Set Field ["gFile Ahead", "Left(gFile
Ahead, Position(gFile Ahead, "¶", 1, PatternCount(gFile
Ahead, "¶")) - 1)"]
Set Field ["gLayout Ahead", "Left(gLayout
Ahead, Position(gLayout Ahead, "¶", 1, PatternCount(gLayout
Ahead, "¶")) - 1)"]
Set Field ["gRecord Ahead", "Left(gRecord
Ahead, Position(gRecord Ahead, "¶", 1, PatternCount(gRecord
Ahead, "¶")) - 1)"]
If ["gGo which File = "Away""]
** Set Field []
** Perform Script
[Sub-scripts, <unknown>]
End If
** Go to Related Record []
Go to Layout ["gGo which Layout"]
End If
The Ahead button does essentially the
same thing as the Back button, only in reverse. It removes
information from the g_Ahead fields, places it in the g_Back and
gGo which fields, then takes you there.
Layouts
Go into Layout mode and move the fields around, resizing them
until they look like figure 1. Under the "Go" header from left to
right, the fields are in this order: gGo which File, gGo which
Layout, gGo which Record. Under the "Back" header, the fields are
in this order: gFile Back, gLayout Back, and gRecord Back.
Finally, under "Ahead," the fields are gFile Ahead, gLayout Ahead,
and gRecord Ahead. Using the Button tool, create some buttons,
attach the appropriate scripts and add text to them to match
figure 1. The white area between the "Previous" and the "Next"
buttons is the "Record ID" field. You can delete the "Connect"
field from the layout. For those of you who will be adding this to
an existing solution, you won't even need any of the fields on the
layout. However, if you do want to place them on the layout just
to see how it works, you'll have to use the field tool and drag
the new fields onto the layout. Now, select Mode > Duplicate
Layout, and change the Layout 1 title to Layout 2 (and vice versa)
on the Layout 2 button. Double-click on what is now called the
Layout 1 button and assign the "Go to Layout 1" script to it. In
ScriptMaker, redefine the "Go to Layout 2" script so it goes to
Layout 2. In Browse mode, create three or four more records by
selecting Mode > New Record.
Figure 1: Field and button layout - Try to make your
screen look something like this, so the instructions in the body
of the article will make sense.
The second file
Now, select File > Save a Copy As.
Then, make a copy of the file you've been working in and name it
Away, using the "Type - copy of current file" option. Go
ahead and open the new Away file. Select File > Define >
Relationships. Create a relationship back to the Home file using
the Connect field:
|
Relationship Name
|
Relationship
|
Related File
|
|
Home
|
Connect =
::Connect
|
Home
|
Most of the fields won't be needed in
this file. Because FileMaker Pro is a relational database, you can
access the fields in the Home file that store your maneuvers. To
display the fields from the Home file, go into Layout mode.
Double-click on the upper-most field, gGo which File. When the
Specify Field window appears, click on the upper box with the
words Current file ("Away") in it. Drag it down to Home and let
go. Click on ::gGo which File to select it and click on OK. Do the
same to convert the other fields to their related counterparts,
but leave the Record ID field as it is because it refers to
records in this file.
While you're at it, do the following:
1. Change the text to indicate this is File 2 instead of File
1.
2. Change the text on the Go to File 2 button so it reads: Go to
File 1.
3. Copy these fields in a group.
4. Switch to the second layout.
5. Delete the corresponding fields in Layout 2.
6. Paste the fields from the clipboard into the same position.
7. Change the header to File 2, and change the background color if
you decide to do that.
I also used a different background color
on both layouts in the second file. This way, when I'm clicking
through the files, I have another visual cue that I've arrived
somewhere else. Of course, there's no need to do that if you're
working with an existing solution.
Four of the scripts in the Away file are
now going to need some surgery so they all refer back to the Home
file. In ScriptMaker, edit the Remember Where You've Been script.
Everywhere you see the small letter "g" as part of a field name,
replace it with Home::g. For instance, If ["not IsEmpty(gFile
Ahead)"] becomes If ["not IsEmpty(Home::gFile
Ahead)"]. All the Set Field steps need changes when you
Specify Field... and Specify... the calculation. Of course, you'll
have to choose which field you set by hand. When you're done with
the Remember script, do the same to the Back Button and Ahead
Button scripts. Change the Land Here script so it reads as
follows. Use the Specify: Layout number from field... for the
first step. For the second step, use the Specify: By Field
Value...:
Script name: Land Here (File:
Away)
Go to Layout ["Home::gGo
which Layout"]
Go to Record/Request/Page ["Home::gGo which Record]
Halt Script
Click on "Go to File 2" script and rename
it "Go to File 1". Click on the "Edit..." script button. The
second step reads:
Perform Script
[Sub-scripts, <unknown>]
Click on the Specify... button and come
down the External Script... Choose the "Home" file and come down
to the "Land Here" script. Click on OK, click on OK again, and
click on the Done button to exit the ScriptMaker.
Under the File menu, choose Define
Fields. Rename gFile Back to gRecord. Now you can delete all the
other Global fields. When you're done, you should only have three
fields left: Connect, Record ID, and gRecord.
Go back into Define Relationships by
selecting File > Define > Relationships. In the dialog that
appears, click on New. When the File dialog appears, select the
Away file and create the following:
|
Relationship Name
|
Relationship
|
Related File
|
|
Which
Record
|
gRecord = ::Record
ID
|
Away
|
Usually, relationships are created
between two different files. However, this relationship
effectivelly connects the file to itself. This so-called
"self-join" relationship lets you paste a record number into the
Global "gRecord" field and immediately go to that record in the
Away file using the "Go to Related record" script step. As long as
the record is in the current found set, Go to Related Record has
the added advantage of leaving any found set alone. Otherwise, it
performs a sort of "Find All." This only happens if you don't
select the "Show only related records" check box. Before you leave
the Away file, go back into ScriptMaker. In the Back and Ahead
buttons scripts, the last seven steps are different than those in
the Home file. They can now be filled out because the
relationships to the Home file are complete.
Script Name: Back Button (File: Away)
(last seven steps)
If
["Home::gGo which File = "Home""]
Perform Script
[Sub-scripts, "External: "Home" [Land Here]"]
End If
Set Field ["gRecord", "Home::gGo which
Record"]
Go to Layout ["Home::gGo which
Layout"]
Go to Related Record ["Back Record"]
End If
Note that if the second step above, the
words "Land Here" refer to the name of the script in the Away
file. I'll use that convention in the scripts that
follow:
Script Name: Ahead Button (File: Away)
(last seven steps)
If
["Home::gGo which File = "Home""]
Perform Script
[Sub-scripts, "External: "Home" [Land Here]"]
End If
Set Field ["gRecord", "Home::gGo which
Record"]
Go to Layout ["Home::gGo which
Layout"]
Go to Related Record ["Which
Record"]
End If
Home again, home again
Go back to the Home file. Select File > Define >
Relationships. Create the following:
|
Relationship Name
|
Relationship
|
Related File
|
|
Which
Record
|
gRecord = ::Record
ID
|
Home
|
|
Away
|
Connect =
::Connect
|
Away
|
Now, go back into ScriptMaker and
complete the final steps in the Back Button, Ahead Button, and
Land Here scripts:
Script Name: Back Button (File: Home)
(last seven steps)
If
["gGo which File = "Away""]
Set Field
["Away::gRecord", "gGo which Record"]
Perform Script
[Sub-scripts, "External: "Away" [Land Here]"]
End If
Go to Layout ["gGo which Layout"]
Go to Related Record ["Which
Record"]
End If
Script Name: Ahead Button (File:
Home) (last seven
steps)
If ["gGo which File = "Away""]
Set Field
["Away::gRecord", "gGo which Record"]
Perform Script
[Sub-scripts, "External: "Away" [Land Here]"]
End If
Go to Related Record ["Which
Record"]
Go to Layout ["gGo which Layout"]
End If
Script Name: Land Here (File:
Home)
Go to Layout ["gGo which Layout"]
Go to Record/Request/Page ["gGo which Record"]
Halt Script
Finally, click on the "Go to File 2"
script and click on the "Edit..." script button. The second step
reads: Perform Script [Sub-scripts, <unknown>].
Click on the "Specify..." button and come down the "External
Script..." Choose the "Away" file and come down to the "Land Here"
script. Click on OK, click on OK again, then click on Done to exit
ScriptMaker.
Try it out
Whew! At last, you're ready to see what this solution can do.
Start clicking the buttons in any order you can dream up. Watch
the info in the Global fields change. Try the Back and Ahead
buttons. When you get all the way back, nothing happens. You can
add a step that beeps if the gFile Back field is empty. Same with
the Ahead button. You might make the buttons dim if they're
unusable. You might even add a Home and End button to take you to
either end of the Back and Ahead chain. You'll certainly need a
"New Record" button. All these options and a few others are
available at http://www.advisor.com.
You can use this with any number of
files. Notice the If statement near the end of the Back and Ahead
buttons scripts, If ["gGo which File = "Away""]. You'll
need an If for each additional file you'll be using. You'll have
to duplicate that script with the "Home::gGo which File" in all
the external files as well. One of the advantages of this system
is that you don't need any of the fields to appear anywhere on any
layouts. All this can happen invisibly. Because of the complex
scripting, you may want to build the method into some of your
regular templates so you don't have to start from scratch each
time you start a project. There are some limitations in the
system. First, this plan can't undo anything. If a record is
deleted, it's gone! Second, it cycles through the same layouts,
records, and files as many times as you go to them. If you want to
keep going back and forth between the same layouts a hundred
times, that's what will happen. This plan doesn't store just a
single location reference.
Another restriction is that you have to
attach the Remember Where You've Been script to all navigation
buttons in all Layouts. You'll probably want to keep people away
from the Window menu and the Layouts pop-up menu. Any other forms
of navigation they use will not be recorded for playback. The more
knowledge your client has about backdoor navigation, the less
valuable the system. You might want to add the Toggle Status Area
[Hide, lock] script step to the Clear button in each of
the files, to prevent users from accessing the Layouts
pop-up.
A warning to those of you who want to
include Find pages in this process. Depending on how you set up
your finds, your user could end up on a page where they're used to
entering data into empty fields while in Find mode. You don't want
someone going there and overwriting the data in live records! The
same warning applies to landing in layouts that normally display
reports in Preview mode. Without the proper Sort and Preview Mode
commands, those pages could look completely foreign. The idea is
to make things easier for your clients. To prevent accidentally
accessing those pages, don't add the sub-script Remember Where
You've Been to any of the buttons that go to those
pages.
Summing up
Using techniques from the files I've just shown you, you'll be
able to add browser-like Back and Ahead buttons to your
solutions. This capability adds power to an already great set of
files. Now, the question is: Can you get your users to pay for
it?
< Back to In the
News