



	   ################################################
	   #                                              #
	   # ##   ## ###### ####### ##    ## ## ##     ## #
	   # ##   ## ##  ## ##      ###   ## ##  ##   ##  #
	   # ##   ## ##     ##      ####  ## ##   ## ##   #
	   # ##   ## ###### ######  ## ## ## ##    ###    #
	   # ##   ##     ## ##      ##  #### ##   ## ##   #
	   # ##   ## ##  ## ##      ##   ### ##  ##   ##  #
	   # ####### ###### ####### ##    ## ## ##     ## #
	   #                                              #
	   ################################################






 
 
 
	 The following paper was originally presented at the

		     Third Annual Tcl/Tk Workshop
		 Toronto, Ontario, Canada, July 1995

	   sponsored by Unisys, Inc. and USENIX Association



	    It was published by USENIX Association in the
		  1995 Tcl/Tk Workshop Proceedings.
 
 
 
 
        For more information about USENIX Association contact:
 
                   1. Phone:    510 528-8649
                   2. FAX:      510 548-5738
                   3. Email:    office@usenix.org
                   4. WWW URL:  http://www.usenix.org
 
 
 
 
 
^L
        TkReplay:  Record and Replay in Tk
            Charles Crowley
            Computer Science Department
            University of New Mexico
            crowley@cs.unm.edu


Abstract

Record and replay of user interactions with a GUI are useful for
regression testing and demonstrations.  Recording is implemented by
intercepting each user action at some point in its processing and
saving it in a script file.  During replay, the script is read and the
user actions (or their effects) are emulated.  In X windows, one
program can intercept user input events before they get to another
program by putting a wrapper around the program.  This level of
recording is sensitive to changes in window position, fonts, etc.
Recording events at the widget level is more robust and closer to the
semantics of the program than recording at the X event level.  The
TkReplay program does this for the Tk toolkit.  A number of factors
have to be considered when implementing record and replay and these are
discussed in the paper.  Recording is implemented in Tk by modifying
each binding to call a recording program.  TkReplay operates as a
separate program and uses send to communicate with the Tk program being
recorded.  Hooks have recently been added to the X toolkit to allow
implementation of record and replay at the Xt widget level.


Introduction

Normally a program with a GUI is operated interactively by a user who
sits at a workstation and enters input using the mouse and keyboard.
In this paper we will consider a facility that allows you to record a
sequence of user inputs in a script file and to replay them at a later
time.  During replay, it is not necessary to have a user at the
workstation entering input, instead the input that was recorded will be
replayed exactly as it was originally entered.  The program will
receive this input and repeat the responses it made to the original
input.  This is akin to running a character- based program in batch
mode where all input comes from a file.

There are (at least) three reasons why one would want to record and
replay a user interaction:  for demonstrations, for regression testing
and for scripting.

We can record a demonstration of a program in a script and run the
program from the script to show the capabilities of the program.  The
demonstration might be used in a help system, in a tutorial or in a
marketing presentation.

Regression testing involves running a set of tests on a program each
time any change is made to make sure that the changes do not affect
other functions that were already working.  Regression tests for a
program should be recorded so that it is easy to repeat the tests after
each change to the program.  Automated testing of GUIs is probably the
most important reason for record and replay.

A script automates the performance of a task with a program so that you
do not have enter all the commands interactively each time you want to
perform the task.  But many programs can only be run interactively and
do not have a scripting language.  A record and replay facility can act
as a scripting language for such a program.

In this paper I will look at the issues in implementing record and
replay in GUI systems and then look at the implementation of TkReplay,
a tool for doing this in Tcl/Tk programs.  I will look at the problems
that were encountered and how they were handled, the things in Tk that
made it hard to implement record and replay and briefly discuss
implementing record and replay in the X toolkit.

Some definitions

The program doing the recording (or replaying) will be called the
replay application.  The program being recorded (or replayed) will be
called the target application.  User input takes the form of a sequence
of actions.  A record of these actions is called a script which is
stored in a file.

There will be another use of the word script in this paper.  A Tk
binding attaches a script of Tcl commands to an event in a widget.  We
will always refer to this as a Tcl script to avoid confusion with a
script of actions.


Issues in Record and Replay

To implement record and replay, we must intercept and record user
actions and then, when replaying, regenerate these actions and send
them to the program as if the user had taken the actions.  It is
necessary to record enough information about the users action so that
the replay mechanism can regenerate the action and so that it will
produce exactly the same response in the program.  The following
diagram shows the parts of a record and replay system.

The recording mechanism intercepts and records the events and then
sends them on to the program so that the user can see the results of
these actions.  Since the script is a file, we could create the script
directly instead of recording an actual interaction or we could edit a
script so that the replayed interaction is similar to, but not exactly
the same as, the originally recorded actions.

The issues to be considered are:
* What to record.
* What to replay.
* How much to modify the target application.


Levels of Feedback

In order to answer the question of what to record and replay we will
discuss the types of feedback present in GUIs.  It is not necessary to
replay all of this feedback.  It is useful to divide the processing of
user input into the three levels taken from linguistic analysis:
lexical, syntactic and semantic.

The following diagram shows these levels.  The downward arrows are
inputs to each level and the upward arrows are the feedback from each
level.  Some examples of each are given in the diagram.

The lexical level consists of mouse button press and release events,
mouse motion events and keyboard key press and release events.  The
input processing at this level provides lexical feedback consisting of
moving the mouse pointer on the screen (feedback for mouse motion) and
echoing characters (feedback for keystrokes).  There is generally no
lexical level feedback for mouse button presses or releases (but they
usually produce some syntactic level feedback from the syntactic level
inputs they generate).  Lexical level input events are then processed
at the syntactic level to generate syntactic level input events.

The syntactic level is tied to the widgets displayed on the screen.
Syntactic level inputs are things like: selecting an object, pushing a
button, dragging a scrollbar slider or selecting from a menu.  The
syntactic feedback from these events would be: the object is shown in
reverse video, the button changes relief (it looks as if it is pressed
in), the scrollbar slider follows the mouse pointer and the menu items
change color to follow the mouse pointer.  Some syntactic level events
generate calls to semantic level actions.

The lexical and syntactic levels involve the specification of the
action to perform.  At the semantic level, the action is finally
performed via calls to the application.  These calls are called
semantic actions and generally they modify the data the program is
managing.  Semantic actions are commands like delete a shape or rotate
a shape.  The semantic feedback from these actions involves an update
of the screen representation of the data, that is, the updated display
with the object deleted or rotated.  In addition, there are other
semantic actions (updates to the program database, messages sent to
other processes, etc.) that are not immediately represented on the
display>

One or more lexical level input events generate a syntactic level input
and one or more syntactic level inputs generate a semantic action.  The
lexical and syntactic events, and their feedback, occur before the
semantic action, which may generate its own semantic feedback.

The primary purpose of user actions is to cause semantic actions to be
executed.  The feedback allows the user to see what has been done,
however a replay mechanism may not need to replicate all levels of
feedback.  It must, of course, replay the semantic actions and the
semantic feedback is produced by the program as a side effect of the
semantic actions. The syntactic feedback is not necessary for scripting
or for testing (unless we are testing the syntactic feedback itself)
but it is important in a demonstration so the people seeing the
demonstration can follow what is going on.  They will see the buttons
get pressed and then commands get executed.  Lexical level feedback is
not strictly necessary, but it is nice for demonstrations since it adds
to the realism of the replay.


Changing the Target Application

Ideally, the replay mechanism should not make any changes in the target
application and a single replay application should work with all
programs.  Unfortunately this is not always possible and so our replay
mechanism will execute some code in the target application.  However,
it will not be necessary to scan and modify the entire source code of
the target application in order to do record and replay.  We will
provide a generic mechanism that we can add to a target application
that does not require changes in the code for each widget or each event
binding.  We will require the target application to make a single call
to the replay application to set things up.


Infallible Record and Replay is Impossible

I should point out that perfect record and replay is impossible.  Up to
now, I have been making an implicit assumption that the behavior of the
program is determined by the user input.  If that is the case, then
record and replay is possible, but, this is not always the case.  For
example, suppose that a program displays a message every hour on the
hour.  This occurs as a result of the system clock ticking and is not
related to any specific user input.  The hourly display might happen
during recording but it will not happen during replay unless the
recording and the replay are executed at exactly the same times.

A perverse (or very clever) program might look at the clock and create
one kind of interface in the morning and another kind of interface in
the afternoon.  We cannot reproduce this in a record and replay system
unless we can record and reproduce the system time.  Normally a program
gets the time from the system.  We could probably intercept these calls
and fool the program but it is not possible to control everything about
the environment of a program.

Lets take a more common example.  Suppose you record a script that
calls up a file selection box and selects a file.  During replay the
file system might be different and the file selected during recording
might have been deleted.  Replaying this script is no longer possible.

The lesson here is that you have to consider all the inputs the program
uses (not just user inputs) and control all of these which might affect
the operation of the program.


Record and Replay at the Lexical Level

One can implement record and replay by recording events at any one of
the lexical, syntactic or semantic levels.  Each of these options has
advantages and disadvantages.  In this section we will look at record
and replay at the lexical level.  In a later section we will look at
record and replay at the syntactic level.  Finally, when we discuss
record and replay in the X toolkit, we will look at record and replay
at the semantic level.


Character Based Programs

Some programs are character based (that is, their input is just a
stream of characters typed by the user).  The user types and the
program responds.  A script for a character-based program is a file
containing the characters typed.  Replaying a script is done by running
the program with the input taken from the script file instead of the
keyboard.  In UNIX, and most modern operating systems, this is trivial
to do with the input redirection facility [11].

Recording is a little harder.  The general solution is to put a wrapper
around the program.  A wrapper reads the input, records it and passes
it on to the real program you are running.  This is also easy to do in
most operating systems and it can be done generically so that a single
wrapper program will record keystrokes for any program.  The following
diagram shows the wrapper strategy.

The wrapper approach is a little harder for full-screen character mode
programs.  Some programs will only run when attached to a real
terminal.  Pseudo-ttys were developed to deal with this problem
[12,7].


GUI Programs

In GUIs, the lexical level (for input) consists of the keyboard and
mouse events.   This level deals with screen pixels and low level user
input actions.  The syntactic level is the widget level and events are
grouped by the widget to which they are directed. We can record and
replay at either of these two levels.  The wrapper model is restricted
to the lexical level.

The Wrapper Model in X

The wrapper model can be used in the X window system.  You must capture
the stream of events going to the application (there are hooks to allow
you to intercept events), record the events in a file and later replay
them.  There is an X library procedure that allows you to generate and
send an event, so you can simulate mouse and keyboard input.

The basic X server allows you to do basic record and replay of X events
but there are several technical problems.  As a consequence of these
implementation problems there have been several extensions to the X
server that have been proposed and implemented over the years that
allow an easier and more complete implementation.  The XTest extension
[4] is sufficient to implement a replay mechanism and the record
extension (still a proposed standard) [13] allows for recording.  These
extensions are basically wrappers around the X server that allow the
interception and injection of events.  The diagram below shows the
structure of events in the X window system.

The problem with the wrapper approach is that it is fragile [2,6].  The
mouse events are based on absolute screen coordinates.  If the windows
move, then the replay may not work.  It is also sensitive to other
changes.  For example, the size of the borders placed around the window
by the window manager can make a difference.  Slight changes in the
layout of the program may cause the replay script to fail.  Also, many
user interfaces are user customizable.  The user can decide which fonts
to use for example.  A different size font might move things around.

The wrapper approach works at the lexical level since that is the level
of X events.  The wrapper records a mouse click at a certain screen
location, not over a certain button. If the button moves, the location
of the event will not, because there is nothing to connect the event
and the button.  Nevertheless, this approach does work if you are
careful.  There are testing tools based on this approach.

Implementing Record and Replay at the Tk Widget Level

The general X wrapper approach will work with any X program including
Tk programs.  Alternatively we could record and replay at the
syntactic, or widget, level.  The widget level is implemented inside
the application with libraries, so we must make changes to the program
itself in order to implement record and replay at this level.  The
following figure shows the event handling model for Tk.  The semantic
routines are Tcl scripts.

Tk  directs events to widgets.  The basic mechanism for handling events
in Tk is the binding mechanism [9].  A binding specifies an event
sequence and a Tcl script to run when the event sequence occurs in the
widget. Some Tk widgets also have callbacks (called command options).
We will discuss them later.

In the next few sections I will examine various issues in the
implementation of record and replay in Tk.

Rebinding Widget Bindings

Recording at the widget level in Tk requires that we intercept each
call generated by the binding mechanism.  There is no way to do this
centrally, so we have to do it by changing each binding to ensure that
the recording mechanism gets called.  The following Tcl/Tk code will
rebind all the widgets for recording.

proc RebindAllWidgets {} {
  RebindWidgetAndChildren .
}
proc RebindWidgetAndChildren {w} {
  RebindEvents $w
  foreach child [winfo children $w] {
    RebindWidgetAndChildren $child
  }
}
proc RebindEvents {w} {
  global Bindings
  # find all the events that have an
  # associated binding
  foreach tag [bindtags $w] {
    foreach event [bind $tag] {
      # get the binding for this
      # tag and event
      set binding [bind $tag $event]
      # remember the binding for
      # later use
      set Bindings($tag,$event) \
        $binding
      # find out which % fields are
      # used in "binding"
      set percentFields \
        [FindPercentFields $binding]
      # rebind to the event to our
      # event handler which will
      # record the event, do the %
      # substitutions and call the
      # original script 
      bind $tag $event "RecordEvent \
        $tag $event $percentFields"
    }
  }
}

We start at the root and visit all the widgets in the interface.  For
each widget, we find all the tags bound to it.  Then we find each event
that is bound to the tag and rebind it.  For each binding, we save the
original script in a table and rebind the event to call our recording
procedure, which records the event and calls the original script.

This process catches all the class bindings since they are found in the
bindtags list for widgets of that class.  We remember what we have
already rebound and only rebind each tag once.  Widget bindings are
just a tag with the same name as the widget.

Capturing X Event Fields

The Tk event handling mechanism uses %-fields to transfer X event data
into the binding.  It looks for strings of the form %x (where x is some
letter) and replaces them with the appropriate field from the X event
structure.  We must duplicate this process when we call the event
handler.  In order to do this, we must capture any %-fields required in
the binding.  We do this by scanning the binding and recording which
%-fields are present.  RecordEvent will substitute the %-fields in the
binding before it is called.

FindPercentFields returns a list of the form {{W $W} {x %x} {y %y}...}
where each required %-field is represented.  The binding mechanism will
fill in the values and the record or replay code will take care of
inserting the %-fields into the Tcl script of the binding.

Capturing the current object on a canvas

Canvases must be treated specially because canvas bindings often make
use of the current object on the canvas, that is, the object that is
directly under the mouse pointer.  During recording we capture the
object id of the current object.  It is not possible to set the current
object except by actually moving the mouse pointer over the object.
What we do during replay is replace all instances of the string current
in the binding with the id of the current object captured during
recording.

Handling bindtags

In Tk4 each widget can have a set of associated tags and each tag can
have events bound to it.  So a single event might activate several
bindings and several Tcl scripts may be executed.  It is possible to
break the chain of Tcl scripts by returning a break return code from a
Tcl script called as an event handler.  During recording we are
executing the Tcl scripts ourselves so we have to capture and pass on
the return code so the program will work as expected.  During replay we
execute the same Tcl scripts as we executed during replay and ignore
the return codes.  We do not give a Tcl script a chance to change its
mind and return a different error code on replay than it returned
during recording.

Callbacks

Several Tk widgets have command options or callbacks as they are called
in X.  The Tk widgets that have command options are: button,
checkbutton, menubutton, radiobutton, scrollbar, scale, menu
(postcommand) and menu entries.  All of these callbacks (except for
postcommand) are implemented with bindings so redefining the bindings
automatically handles the callbacks.

The one exception is the postcommand option that is called just before
a menu is posted.  This gives the application a chance to modify the
menu according to current conditions.  This callback is called as part
of the post subcommand to the menu widget.  Since the post command is
almost always executed in a callback we do not have to worry about
redefining it.

Internal Widget Bindings

The canvas and text widgets each allow bindings to internal objects and
have their own separate binding systems.  These internal binding
systems are virtually identical to the main binding system and are
handled in a similar way.  When we rebind a canvas or text widget, we
first rebind the external bindings and then we go through all the
internal bindings and rebind those also.

One detail that makes this harder is that canvas widgets allow bindings
to both canvas objects and canvas tags.  But there is no way to
enumerate all the tags in a canvas.  (This is done with $text tag names
in the text widget.)  The workaround is to enumerate all the objects
and accumulate all the tags associated with these objects.

Handling Dynamic Changes

Any time Tcl code is executed it could change the bindings of any
widget, create widgets or destroy widgets.  Our recording mechanism is
based on having rebound all bindings of all widgets.  If a binding
changes or a widget is created then we have to redefine the bindings
before it gets called, so we have to detect these changes and make the
necessary rebindings.  The simplest way to deal with this is the
redefine the bind command and all the widget creation commands.

The new widget creation commands will call the original widget creation
command and then call a procedure to rebind all the tags of the new
widget.  There is one detail that we must consider.  The frame and
toplevel commands are implemented with the same code which looks at the
first letter of the command name.  If this first letter is t then a
toplevel is created, otherwise a frame is created.  So we have to be
sure to rename toplevel to another name which starts with t.

In order to catch any internal rebinding in canvas and text widgets we
have to rename the individual widget command (whose name is the path
name of the widget) also.  When this command is called we see if it is
a bind subcommand and, if it is, redefine the binding.

Mouse Pointer Warping

If you are replaying a demonstration, you may want the mouse pointer to
move just as it did when you did the recording.  This involves two
things, knowing where to move the mouse pointer and actually moving it.
There is an X library procedure called XWarpPointer that moves the
mouse pointer, but pointer warping is a controversial subject.  Many
human-computer interface experts advise that a program should never
warp the pointer because the pointer should be under the control of the
user.  In fact, the documentation for the XWarpPointer command cautions
that this command can confuse the user and should only be used in
exceptional circumstances.  As a consequence (or maybe for other
reasons), Tk does not have a command to warp the mouse pointer.  I had
to add that functionality as a new Tk command.  The problem with this
is that the replay system will not work with the regular wish but needs
a custom wish with pointer warping added.

One reason you want to move the mouse pointer is to call attention to
what is happening during the replay.  There is a version of TkReplay
that does not use pointer warping but instead has a small window with a
red arrow in it that moves around and points to the widget where the
next event takes place.

It is easy to know where to move the pointer because the X records the
x and y coordinates of the mouse for all mouse events.  We can capture
these with %-fields and use them to know where to warp the mouse
pointer.

I should note that it is irritating to have the pointer moved around
for you.  During the debugging of the program I could not stop replays
because I could not get control of the pointer long enough to set the
focus and kill the application.  I had to put in a special binding to
stop the replay when a mouse button is clicked.

Name Conflicts

TkReplay defines several new procedures and a global array in the
target application and has to worry about name conflicts.  It uses
names that end in __rd.  This string must be changed in the file to be
sourced if there is a conflict with other names in the target
application.  It could try several names and see which ones are not
being used but even this will not work in all cases.  A program might
define new names at any time and these might conflict with any name you
choose, even if there was no conflict when you first defined the name.

Potential Deadlocks

Window systems are event oriented and work by calling application code
when user events are detected.  Sometimes it is necessary for user code
to wait for a specific event to occur, usually the answer to a question
in a dialog box.  Tk provides this facility with the tkwait command
that allows a you to wait for an event inside a Tcl script.  This is
implemented by running a local event loop.

The tkwait facility can interact badly with a replay facility.  if you
are not careful it is easy to get into deadlocks.

The replay application replays an action by sending it to the target
application using the Tk send command.  The send command sends the
command given in its arguments and blocks while it waits for a reply.
If the command it sends executes a tkwait  then it will not return and
the send command will not complete.  Because of this problem we cannot
execute the binding directly but instead use the after command so that
the send can complete.  But then the completion of the send command no
longer indicates that the bindings Tcl script has completed so we have
to send a response back to the replay program when the Tcl script has
completed.  But we have observed that the binding may not complete
because it calls a tkwait.  To solve this problem we have to set a
timeout that will send a reply back after some time delay if the script
has not completed.  It is easiest to have both the timeout and the
command send replies.  The replay program accepts the first reply as
signal to move on the next event to replay and ignores the second
reply.

Here is the sequence of events when an action is replayed.

1. Get the next user action to replay.
2. Send the action to the target application.
3. The target application schedules the action using the after command,
starts a timer (also using the after command) and completes the send.
4. When the action is complete a completion message is sent to the replay
application.
5. When the timeout occurs another completion message is sent to the replay
application.
6. The replay application continues after it gets the first completion message.
It will ignore the second completion message that it will get later.

Let's look at the code to handle this.  When an action is being replayed
the replay program sends a command that calls the ReplayAction procedure:

proc ReplayAction {uid evid subs} {
  after $timeout send $replayApp \
    [list ActionEnd $uid]
  after 1 DoAction $uid \
    $Bindings($evid) $subs
}

The uid is a unique identifier assigned to the action dynamically by
the replay application.  It is used to identify the action in the
action completed or time out messages that will be returned.  The evid
is the subscript in the Binding table where the code for the binding
was saved.  The subs are the %-field substitutions to make.

First we set up the timeout and then we schedule the action itself.
Then the procedure returns and releases the send.  The replay
application then waits for the action to end or the timeout (whichever
comes first).  The DoAction procedure looks like this:

proc DoAction {uid action subs} {
  RealDoAction $action $subs
  send $replayApp \
    [list ActionEnd $uid]
}

So two ActionEnds are sent for every action and three sends are
required for each action.  The replay application looks at the uid in
the ActionEnd, ignores old replies and waits for an ActionEnd for the
current action.

The TkReplay Program

I have implemented record and replay for Tk4.0 in a program called
TkReplay (there is also an older Tk3.6 version).  It takes into account
all of the considerations described in the previous section.  It
records user actions in a script which can be saved in a file.  The
script can be later read in and replayed.  You can load an existing
script file, record additional actions (which are added to the script),
delete actions and save the modified script.  Scripts are in a simple
ASCII format and can be edited and combined with a text editor

The first step in recording a script is to start the target application
and connect to it.  When TkReplay connects to an application it sends
it a command to source a file of Tcl procedures and commands that
redefine all the bindings and rename the commands TkReplay must
monitor.  Both loading and connecting can be made part of the script so
that replaying the script will automatically load and connect to the
target application.

It is possible to connect to several applications at the same time and
record a combined script that includes user actions from two or more
target applications.

Once you are connected to an application you can start recording.
Actions show up in a list box as they are recorded.  After you stop
recording you can rewind the script and play it.  You can start from
any place in the script by selecting that action in the list box.

Adding Comments and Pauses

If you are recording a demonstration then you may want to display
comments on what is happening in the demonstration.  You might want
comments for regression testing as well to inform the tested what to
look for during replay.

TkReplay has a facility to display a comment after any event in the
demonstration and remove it after some later event.  The comment is in
a popup text window.

There is also a command to add a pause in the replay.

Megawidgets and New Widgets

Megawidgets that are built up from existing Tk widgets work
automatically with TkReplay but the scripts will not reflect the
megawidget structure.  The Tix widgets, for example, work with
TkReplay.

TkReplay depends on redefining each binding and so it must know when
widgets are created and when bindings are redefined.  So new widgets
must be added to TkReplay by hand.  For widgets with no internal
bindings, this consists of adding their name to a list.  For widgets
with internal bindings, custom code must be added to handle the
internal bindings.  The pad widget [3] is an example of this.

Problems with TkReplay

There are a few problems with the timing of the replays.  TkReplay
records the time delay between events and delays for that period when
replaying the actions.  But the replay mechanism itself takes time
which cannot be easily predicted and so the replay is always slower
than the recording.  The replay will be affected if there are parts of
the program that depend on timings.  For example, suppose you have a
button that autorepeats as you hold it down.  The down time is the
elapsed time between the button down event and the button up event.
This elapsed time will be longer in the replay and the button will
autorepeat more times in the replay.  This could affect scrolling.

Since we only record mouse positions when events (like enter, exit,
button down, button up) occur the mouse motion seems a little jerky.

Record and Replay with Xt Widgets

Most X widget toolkits, like Motif, are built on an intermediate layer
called the X toolkit (or Xt) layer.  This is a layer of routines to
support the implementation of widgets.  The Xt layer has a mechanism
for binding user actions to application code that is based on action
procedures and translation tables.  A translation table is a text
description that binds input events to action procedures [1].  It is
very similar to the Tk bind mechanism.  An action procedure is a C
procedure that the application or widget has packaged so that it can be
called from a translation table

Given these close analogies between Xt/Motif and Tk, we could use the
same strategy we used in Tk to implement record and replay. This
requires the same introspection facilities and hooks available in Tk.
That is, we have to be able to traverse the widget tree and change all
the bindings and we have to detect when bindings are added or changed
and when new widgets are created.  These facilities have recently been
added to Xt in an extension to X11R6 called the Remote Access Protocol
(RAP).

The Xt level also offers a facility to allow and easy implementation of
record and replay at the semantic level.  There is a specific hook that
is exactly what you need, a procedure called XtAppAddActionHook.  You
pass it the name of a procedure that will be called just before any
action procedure is called.  Thus we do not need to rebind individual
bindings, a single call does it all.  Of course, recording at the
semantic level of action procedures will not allow you to record or
replay and lexical and syntactic feedback that occurs before the action
procedures is called.

Another problem in Xt is the existence of event handlers.  Event
handlers are the X level mechanism for responding to user events.  All
other mechanisms (such as translation tables and Tk bindings) are
implemented using event handlers.  Programmers are discouraged from
using event handlers directly and most Xt level applications do not use
them.  But if a program does use event handlers, any hope of recording
all events is lost because there is no hook to get control before event
handlers and there is no way to find out what event handlers are in
effect or to override them.

Other Uses of Recording Hooks

The same hooks needed for record and replay can be used to create an
alternative interface to a program, for example, an alternative
interface to a GUI program for blind users [5].

The user interface of a program is an external model that is a
reflection of an internal model that the program implements.  The
internal model is the important one and the user interface is a way of
presenting that internal model to the user.  The user interface is a
way to inspect the internal model and to perform operations on it
[10].

The widgets we normally use are appropriate for an ideal user with good
vision and the ability to use a keyboard and a mouse easily.  If a user
does not fit this profile then the normal widgets might not be
appropriate.  What is important is to provide an interface to the user
that reflects the internal model of the program.  Suppose we had a
blind user.  It would be possible to redesign the user interface of a
program to use sound and touch and to effectively present the internal
model of the program.

But there are a range of possible disabilities and it is not feasible
to change all programs to best suit a wide range of users.  The best
compromise is to provide generic methods of translating the normal Tk
interface to one appropriate for a particular class of users, such as
blind users.  The generic mechanism can transform the interface into
one based on, for example, sound and touch.  There are many ways to do
this and experimentation about the best way to do it is appropriate.

Implications for Tk

It was fairly easy to implement record and replay in Tk because two
important features of Tcl/Tk.  First, Tk is introspective and allows us
to ask just about anything about the current state of the interface.
Second, Tcl is dynamic and allows us to redefine the procedures we want
to monitor and add the hooks we need.  We can rebind existing widgets
easily because Tk will tell you the current bindings.  This means that
we do not have to get control when an application first starts but can
connect to it at any time.  Because we can redefine key procedures we
can find out when important events occur (that is, widget creations and
new bindings) and deal with them.

It would be useful if Tk added a function that is equivalent to the Xt
function XtAppAddActionHook.  This would allow a very simple
implementation of record and replay at the semantic level.  It would
allow you to define a function that is called just before a binding
script is about to be called.  It should pass you the necessary
information, like what the event is, what widget it was in, what the
binding is, and have a way to get the X event fields.  The return value
of the procedure would determine whether the binding was called.

It would be a good idea to implement frame and toplevel with two
different C procedures so that people would be free to rename toplevel
to whatever name they want.

A few additional features would be handy.  These include: the ability
to enumerate tags in a canvas, the ability to set the current object in
a canvas, and the ability to warp the mouse pointer.

Other Work

There has been work on record and replay at the X level for several
years and this has resulted in the X server extensions to support
record and replay.  There are several tools for testing GUIs that use
these extensions (e.g., XRunner).

Record and replay at the widget level are just starting to get
attention.  Jan Newmark [8] has implemented a replay mechanism for Tk
and has recently extended it to do recording (using
XtAppAddActionHook).

Summary

In this paper I have examined at the general issue of record and replay
in GUIs, looked at the various levels at which record and replay can be
done, and looked at the issues that must be addressed when implementing
this facility in Tk.  In the course of the discussion I have identified
a few changes to Tk that would make implementing record and replay
simpler.

Availability

TkReplay is available from ftp://ftp.cs.unm.edu and from the Tcl/Tk
archive.

Acknowledgments

Some of this work was supported by a contract from Sandia National
Laboratories.  I want to thank Gene Hertel of Sandia National
Laboratories for supporting the work and giving me the original idea to
work of record and replay.

References

[1] Asente, P. and Swick, R. with McCormack, J, X Window System Toolkit:
A Complete Programmer's Guide and Specification, Digital Press, 1990.

[2] Azulay, A. Automated Testing for X Applications, X Journal, May-June 1993.

[3] Bederson, B. B. and Hollan, J. D. Pad++: A Zooming Graphical Interface
for Exploring Alternate Interface Physics, Proc. ACM User Interface Software
and Technology (UIST'94), 17-26.

[4] Drake, K. X11 XTest Extension.
ftp://ftp.x.org/pub/R6untarred/xc/doc/hardcopy/Xext/xtest.PS.Z

[5] Edwards, W. K., Mynatt, E. D. and Stock, K. Access to Graphical Interfaces
for Blind Users, Interactions, 2, 1, January 1995, 54-67.

[6] Kepple, L. R. Testing GUI Applications, X Journal, July-August, 1993.

[7] Libes, D. Exploring Expect. O'Reilly & Associates, 1995.

[8] Newmarch, J. Using Tcl to Replay Xt Applications.  AUUG94 Conference,
Melbourne, Australia, Sept. 1994,

[9] Ousterhout, J. Tcl and the Tk Toolkit. Addison Wesley, 1994.

[10] Preece, J. Human Computer Interaction Addison Wesley, 1994,
chapters 6 and 7.

[11] UNIX shell manual page (man 1 sh).

[12] UNIX pty manual page (man 4 pty).

[13] Zimet, M. Extending X for Recording (public review draft, 10 Feb 1995).
ftp://ftp.x.org/pub/R6untarred/xc/doc/hardcopy/Xext/record.PS.Z

From konstan@cs.umn.edu  Mon May 22 15:22:43 1995
Received: from cs.umn.edu (mail.cs.umn.edu) by usenix.ORG (4.1/1.29-emg890317)
	id AA02364; Mon, 22 May 95 15:22:43 PDT
Received: from saturn.cs.umn.edu (saturn.cs.umn.edu [128.101.224.56]) by mail.cs.umn.edu (8.6.11/8.6.6) with ESMTP  for <tclascii@usenix.org>
Received: from localhost.cs.umn.edu by saturn.cs.umn.edu (8.6.8.1) id RAA20446; Mon, 22 May 1995 17:21:50 -0500
Message-Id: <199505222221.RAA20446@saturn.cs.umn.edu>
To: tclascii@usenix.ORG
Subject: Ascii and Postscript for Tcl/Tk 95 Paper #23
Mime-Version: 1.0
Content-Type: multipart/mixed ;
	boundary="===_0_Mon_May_22_17:19:29_CDT_1995"
Date: Mon, 22 May 95 17:21:40 -0500
From: Joe Konstan <konstan@cs.umn.edu>
Status: RO

This is a multipart MIME message.

--===_0_Mon_May_22_17:19:29_CDT_1995
Content-Type: text/plain; charset=us-ascii

Enclosed are ASCII and PS for Paper #23 (Panel):
	Tcl and Tk in the Classroom:  Lessons Learned




--===_0_Mon_May_22_17:19:29_CDT_1995
Content-Type: text/plain; charset=us-ascii
Content-Description: Panel.txt

Tcl and Tk in the Classroom: Lessons Learned

Charles Crowley	          Joseph A. Konstan*	    Michael J. McLennan
University of New Mexico  University of Minnesota   AT&T Bell Laboratories
crowley@cs.unm.edu	  konstan@cs.umn.edu	    mmc@mhcnet.att.com

*Panel Contact

Abstract

How are Tcl and Tk taught and used in the classroom? 
What lessons can be learned in the trenches to ease the 
way to teaching Tcl and Tk, and to teaching using Tk? 
This panel brings together a diverse group of individuals 
who use Tcl and Tk in the classroom. One teaches 
industrial short courses on Tcl, Tk, and extensions. The 
others use Tk and STk. in academic classrooms as part 
of teaching user interfaces, software engineering, and 
programming. The panelists share the challenges they 
have faced and the lessons they have learned in the pro-
cess.

Introduction

Tcl and Tk have grown rapidly in popularity and are 
now being used as prototyping and development tools 
by a large number of users. The earliest users of Tcl and 
Tk were trained through manual pages, trial and error, 
and the generous support of other users. Today, there are 
many more users coming aboard and a much higher 
demand for training. At the same time, Tcl and Tk have 
become useful tools for teaching other subjects. Tcl and 
Tk are making their mark in the classroom.

This panel brings together different perspectives on Tcl, 
Tk, and the classroom. Each one uses Tcl and Tk as part 
of his research and development. And each one has 
brought Tcl and Tk into the classroom. One has devel-
oped a pair of industrial short courses to teach program-
ming in Tcl/Tk and [incr Tcl]. The others teach 
university courses using Tcl, Tk, and STk as part of 
teaching user interfaces, software engineering, and pro-
gramming. The panelists discuss their motivation for 
using Tcl and Tk in the classroom, their educational 
objectives and techniques, the challenges they faced 
along the way, and the lessons they have learned and 
insights they have developed for teaching with Tcl and 
Tk.

Panelist Statements

Charles Crowley is an Associate Professor of Computer 
Science at the University of New Mexico. His teaching 
interests are in human computer interfaces, program-
ming languages, software engineering and operating 
systems. He has used Tcl/Tk in three classes: user inter-
face design, software engineering and Scheme program-
ming. His research interests are in human computer 
interfaces and the use of models in design. He uses Tk in 
much of his research: a Tcl/Tk based multiple view edi-
tor, a Tk record and replay system and an interface 
builder for non-programmers.

What should CS students learn?

Students need to acquire the skills necessary to produce 
software products.

1.	Competence in a programming language, so they 
can produce programs. 

2.	Some competence in a second language with a dif-
ferent philosophy, so they will have exposure to dif-
ferent approaches to problem solving. 

3.	Software engineering, so they understand the 
design process and the steps involved. 

4.	Design, so they can develop solutions. 

5.	Specific skills and knowledge (operating systems, 
database systems, AI, data structures, interpreters 
and compilers, human factors), so they can carry 
out the design steps 

6.	Theory, so they can formally describe and analyze 
their designs.

Why do we have programming in classes?

Programming supports several of the skills listed above. 
Students need practice to become competent program-
mers. Teaching software engineering and design are 
more fun if students implement their designs. Program-
ming helps students understand specific skills and 
knowledge (in operating systems, for example).

How do Tcl/Tk fit into this?

Tcl is a good "second" language because it has a differ-
ent philosophy than languages like FORTRAN, C, C++, 
etc. It is an example of a scripting language intended for 
high "whipupitude" (a term Larry Wall used to describe 
Perl). It shows students a different way of looking at 
programming. It is a more dynamic language than most 
students have seen before. It is not really a different 
"paradigm" but it is very different.

Tcl/Tk together are a fast way to prototype GUIs. This is 
important because the ability to produce prototypes 
quickly is fun and gives the students a feeling of power. 
It makes them like computing and draws them into the 
field. Once they see the great things that can be done 
with computers they will be able to see why you may 
want to go to more trouble to get it really right and see 
the point in the discipline of SE.

Prototyping is also important in learning the importance 
of specification and design. Using Tcl/Tk means that in 
a few days you have a working version and you are 
forced to face up the real problem, "what, exactly, do 
you want this program to do?" By not bogging them 
down in programming details, we allow them to see that 
creating a software product is a hard job even if the pro-
gramming part is easy. It teaches the importance of 
design and specification of the human computer inter-
face.

Because Tcl lets you get into trouble with program 
design, students learn the importance of planning (or, at 
least, reorganizing so it looks like you planned it). Hav-
ing only two name spaces shows the impact of name 
space pollution. You see the need for modules. And 
there is a place to go, itcl, for relief.

Tcl/Tk shows the value of prototyping because it is such 
a good prototyping language. Students can see the value 
of iterating over a number of versions of a program and 
see it improve.

How do we use Tcl/Tk at the University of New Mex-
ico?

1.	We have used in our software engineering class so 
the students can learn about prototyping and to 
reduce the influence of programming in the class.

2.	We have used it in our user interface class to allow 
students to prototype and experiment with inter-
faces.

3.	We have used STk (Scheme/Tk) in our second 
course on programming that covers Scheme. We 
used Scheme because we thought it is a better lan-
guage to learn, especially this early, that Tcl.

What have we learned from this?

Generally Tcl and Tk have worked. They motivate stu-
dents, allow prototyping and are relatively easy to learn. 
Tcl and Tk have showed us that GUI programming can 
be fast and easy. Such a language has many benefits in a 
CS curriculum. 

*	Learning a prototyping language.

*	Allowing students to "whip up" programs and have 
fun. 

*	Reducing programming time so we can concentrate 
on the topic we are actually learning about: AI, 
algorithms, design, specification, SE, user inter-
faces, etc.

Despite the ease of Tcl and Tk, it takes a while to learn 
it, at least a week for a basic competence. Even then, 
students are not confident in the language. This takes up 
class time that could be used for other things. This is 
even true in the Scheme class since it takes a while to 
learn to use the Tk widgets (even without learning Tcl).

I think that all students should learn a prototyping lan-
guage early on, maybe in the first programming class. 
This should include Tk which is a nice widget set. If we 
could count on people already knowing Tcl/Tk or STk, 
then we could use it in more classes where we want to 
do some programming but we don't want it to take a lot 
of class (or home work) time. We gain time, first 
because the students already know it and second 
because Tcl/Tk is very fast to program in.

Joseph A. Konstan is an Assistant Professor in the 
Department of Computer Science at the University of 
Minnesota. His research and teaching focus on user 
interfaces, specifically for distributed multimedia appli-
cations. He uses Tk in the classroom as the implementa-
tion language for user interface projects and is 
introducing a new course on UI toolkits that will teach 
students to write widgets, geometry managers, and 
other toolkit extensions. Tcl and Tk also figure promi-
nently in his research including Tcl streams as a media 
type in multimedia, and data propagation (formulas) in 
Tcl. 

Teaching a user interface design, development, and sys-
tems course at Minnesota has many challenges. The big-
gest of these are that we have only a 10-week quarter in 
which to teach, and that students generally have not 
been exposed to any toolkits or frameworks for interface 
development. Fortunately, students are generally famil-
iar with programming (in both C++ and scheme) and the 
Unix environment, so it seemed that Tcl and Tk would 
not be too large a stretch for them to learn. 

This winter alone, 35 groups of two to four students 
completed projects using Tcl and Tk. Along the way, 
each group learned enough about Tcl and Tk to meet its 
project needs, and many of the groups learned enough to 
use Tk in future course and research projects. In the pro-
cess, I have gained experience in teaching about Tcl and 
Tk and in supporting students who are learning to use 
them.

Like most programming, Tk is best learned by example. 
I have found that it is possible to teach the fundamental 
concepts of both the Tcl language and the Tk toolkit in 
75 minutes by making available a large set of examples 
for independent study. Fortunately, the FTP archives are 
replete with examples. The greatest difficult students 
have is identifying appropriate examples, and therein 
lies the real value of an experienced instructor and 
knowledgeable teaching assistants. 

Furthermore, the parts of Tcl and Tk that really need to 
be taught are very small. The critical concepts that stu-
dents need assistance with are the basics of UI toolkits: 
the notion of event-driven programming and callback 
functions, and the ideas of a window hierarchy and 
geometry management. Beyond these concepts, the only 
"teaching" needed was to review basic Tcl syntax (quot-
ing and substitution rules), variable scoping, and the 
general Tcl/Tk architectural model (though almost no 
students linked in C code within their applications). 

The results have been very pleasing. In two years of 
offering this course, students have produced a wide 
range of interesting applications. Several of these are in 
actual day-to-day use (e.g., a tutorial introduction to 
using X Windows, a mailing list sign-up tool, and a 
structured editor and interface to the POVray graphics 
package). Students have reported back that they con-
tinue to use the tools after the class, and several research 
projects now use Tk (or STk) as their primary interface 
development tool.

Michael J. McLennan is a Member of Technical Staff at 
AT&T Bell Laboratories in Allentown, PA. He became 
involved with Tcl/Tk three years ago, while using it to 
develop CAD tools for semiconductor process, device 
and circuit simulation. Since then he has developed 
numerous extensions, including [incr Tcl], an object-
oriented extension of the Tcl language, and [incr Tk], a 
facility for building compound "mega-widgets". Dr. 
McLennan has also developed two introductory 
courses: "Building Applications with Tcl/Tk", and 
"Object-Oriented Programming with [incr Tcl]".

I think John Ousterhout's book "Tcl and the Tk Toolkit" 
provides an excellent overview of Tcl/Tk. Only one 
other source of information is better: the Tcl/Tk man 
pages. So when I set out to develop my introductory Tcl/
Tk course, I wanted to include things that were not 
already documented. In my course, I try to show how 
the Tcl language stitches the Tk widgets into a larger 
application. Instead of showing each widget by itself, I 
try to show small combinations of widgets, to illustrate 
the interplay between widgets that occurs in normal 
applications. I also try to explain some of the philosophy 
behind the building of applications: Start small and let 
the application grow; avoid hard-wired values for wid-
get options; use the canvas to create intuitive controls.

Above all, I try to make the course fun. After all, Tcl/Tk 
development is fun, once you get the hang of it. So we 
build a simple "trek" video game. We build dialog boxes 
to specify landing parties and kill off the "red shirt" 
crewmen. Along the way, we find out how to use the 
canvas, how to manage grabs, and how to build hyper-
tools that communicate with one another via "send".

I encourage students to type along as I speak--to bring 
up a "wish" and test out simple examples as I go along. I 
have found that this gives them hands-on experience, 
while letting the course move forward at a steady pace. I 
also give them one or two lab exercises every day, each 
lasting about an hour. This gives them time to make 
their own mistakes, and to learn how to find them with 
things like "puts $errorInfo".

New students have the most difficulty in Tcl/Tk by far 
with the quoting rules. The rules themselves are quite 
simple; I can explain them on just a few slides. But it 
usually takes the entire course (and then some!) for the 
students to understand how to apply them in various sit-
uations. Another source of confusion is the subtle shift 
between the pass-by-name semantic that some com-
mands have, e.g.,

    lappend names Fred Joe Larry

and the pass-by-value semantic that many other com-
mands have, e.g.,

    lrange $names 2 11

Finally, when it comes to mixing C and Tcl code, many 
students have difficulty with the notion of programming 
on two levels. It is hard for them to visualize how the 
control flow passes between the two worlds. It is also 
hard for them to appreciate what should be written as C 
code, and what should be done with Tcl.

Since we started offering these courses, the response has 
been phenomenal. Developers with diverse backgrounds 
and even more diverse applications are learning to use 
this powerful tool. We have seen an exponential growth 
in the requests for training. I think this speaks volumes 
for the utility of Tcl/Tk, and for its continued success in 
the future. 

--===_0_Mon_May_22_17:19:29_CDT_1995
Content-Type: application/postscript
Content-Description: Panel.ps
Content-Transfer-Encoding: quoted-printable

%!PS-Adobe-2.0
%%BoundingBox: (atend)
%%Pages: (atend)
%%DocumentFonts: (atend)
%%EndComments
%
% FrameMaker PostScript Prolog 3.0, for use with FrameMaker 3.0
% Copyright (c) 1986,87,89,90,91 by Frame Technology Corporation.
% All rights reserved.
%
% Known Problems:
%       Due to bugs in Transcript, the 'PS-Adobe-' is omitted from line 1=

/FMversion (3.0) def =

% Set up Color vs. Black-and-White
        /FMPrintInColor systemdict /colorimage known
                systemdict /currentcolortransfer known or def
% Uncomment this line to force b&w on color printer
/FMPrintInColor false def
% or as shades of gray, based on luminance, on a black-and white printer.=
 The
% following flag, if set to True, forces all non-white colors to print as=
 pure
% black. This has no effect on bitmap images.
/FMPrintAllColorsAsBlack             false def
%
% Frame products can either set their own line screens or use a printer's=
 =

% default settings. Three flags below control this separately for no =

% separations, spot separations and process separations. If a flag
% is true, then the default printer settings will not be changed. If it i=
s
% false, Frame products will use their own settings from a table based on=

% the printer's resolution.
/FMUseDefaultNoSeparationScreen      true  def
/FMUseDefaultSpotSeparationScreen    true  def
/FMUseDefaultProcessSeparationScreen false def
%
% For any given PostScript printer resolution, Frame products have two se=
ts of =

% screen angles and frequencies for printing process separations, which a=
re =

% recomended by Adobe. The following variable chooses the higher frequenc=
ies
% when set to true or the lower frequencies when set to false. This is on=
ly
% effective if the appropriate FMUseDefault...SeparationScreen flag is fa=
lse.
/FMUseHighFrequencyScreens true def
%
% PostScript Level 2 printers contain an "Accurate Screens" feature which=
 can
% improve process separation rendering at the expense of compute time. Th=
is =

% flag is ignored by PostScript Level 1 printers.
/FMUseAcccurateScreens true def
%
% The following PostScript procedure defines the spot function that Frame=

% products will use for process separations. You may un-comment-out one o=
f
% the alternative functions below, or use your own.
%
% Dot function
/FMSpotFunction {abs exch abs 2 copy add 1 gt =

		{1 sub dup mul exch 1 sub dup mul add 1 sub }
		{dup mul exch dup mul add 1 exch sub }ifelse } def
%
% Line function
% /FMSpotFunction { pop } def
%
% Elipse function
% /FMSpotFunction { dup 5 mul 8 div mul exch dup mul exch add =

%		sqrt 1 exch sub } def
%
%
/FMversion (4.0) def =

/FMLevel1 /languagelevel where {pop languagelevel} {1} ifelse 2 lt def
/FMPColor
	FMLevel1 {
		false
		/colorimage where {pop pop true} if
	} {
		true
	} ifelse
def
/FrameDict 400 dict def =

systemdict /errordict known not {/errordict 10 dict def
		errordict /rangecheck {stop} put} if
% The readline in PS 23.0 doesn't recognize cr's as nl's on AppleTalk
FrameDict /tmprangecheck errordict /rangecheck get put =

errordict /rangecheck {FrameDict /bug true put} put =

FrameDict /bug false put =

mark =

% Some PS machines read past the CR, so keep the following 3 lines togeth=
er!
currentfile 5 string readline
00
0000000000
cleartomark =

errordict /rangecheck FrameDict /tmprangecheck get put =

FrameDict /bug get { =

	/readline {
		/gstring exch def
		/gfile exch def
		/gindex 0 def
		{
			gfile read pop =

			dup 10 eq {exit} if =

			dup 13 eq {exit} if =

			gstring exch gindex exch put =

			/gindex gindex 1 add def =

		} loop
		pop =

		gstring 0 gindex getinterval true =

		} bind def
	} if
/FMshowpage /showpage load def
/FMquit /quit load def
/FMFAILURE { =

	dup =3D flush =

	FMshowpage =

	/Helvetica findfont 12 scalefont setfont
	72 200 moveto
	show FMshowpage =

	FMquit =

	} def =

/FMVERSION {
	FMversion ne {
		(Frame product version does not match ps_prolog!) FMFAILURE
		} if
	} def =

/FMBADEPSF { =

	(PostScript Lang. Ref. Man., 2nd Ed., H.2.4 says EPS must not call X    =
          )
	dup dup (X) search pop exch pop exch pop length =

	4 -1 roll =

	putinterval =

	FMFAILURE
	} def
/FMLOCAL {
	FrameDict begin
	0 def =

	end =

	} def =

/concatprocs
	{
	/proc2 exch cvlit def/proc1 exch cvlit def/newproc proc1 length proc2 le=
ngth add array def
	newproc 0 proc1 putinterval newproc proc1 length proc2 putinterval newpr=
oc cvx
}def
FrameDict begin =

/FMnone 0 def
/FMcyan 1 def
/FMmagenta 2 def
/FMyellow 3 def
/FMblack 4 def
/FMcustom 5 def
/FrameNegative false def =

/FrameSepIs FMnone def =

/FrameSepBlack 0 def
/FrameSepYellow 0 def
/FrameSepMagenta 0 def
/FrameSepCyan 0 def
/FrameSepRed 1 def
/FrameSepGreen 1 def
/FrameSepBlue 1 def
/FrameCurGray 1 def
/FrameCurPat null def
/FrameCurColors [ 0 0 0 1 0 0 0 ] def =

/FrameColorEpsilon .001 def	=

/eqepsilon {		=

	sub dup 0 lt {neg} if
	FrameColorEpsilon le
} bind def
/FrameCmpColorsCMYK { =

	2 copy 0 get exch 0 get eqepsilon {
		2 copy 1 get exch 1 get eqepsilon {
			2 copy 2 get exch 2 get eqepsilon {
				3 get exch 3 get eqepsilon
			} {pop pop false} ifelse
		}{pop pop false} ifelse
	} {pop pop false} ifelse
} bind def
/FrameCmpColorsRGB { =

	2 copy 4 get exch 0 get eqepsilon {
		2 copy 5 get exch 1 get eqepsilon {
			6 get exch 2 get eqepsilon
		}{pop pop false} ifelse
	} {pop pop false} ifelse
} bind def
/RGBtoCMYK { =

	1 exch sub =

	3 1 roll =

	1 exch sub =

	3 1 roll =

	1 exch sub =

	3 1 roll =

	3 copy =

	2 copy =

	le { pop } { exch pop } ifelse =

	2 copy =

	le { pop } { exch pop } ifelse =

	dup dup dup =

	6 1 roll =

	4 1 roll =

	7 1 roll =

	sub =

	6 1 roll =

	sub =

	5 1 roll =

	sub =

	4 1 roll =

} bind def
/CMYKtoRGB { =

	dup dup 4 -1 roll add 						  =

	5 1 roll 3 -1 roll add 						  =

	4 1 roll add 								  =

	1 exch sub dup 0 lt {pop 0} if 3 1 roll 	  =

	1 exch sub dup 0 lt {pop 0} if exch 	      =

	1 exch sub dup 0 lt {pop 0} if exch	  		  =

} bind def
/FrameSepInit {
	1.0 RealSetgray
} bind def
/FrameSetSepColor { =

	/FrameSepBlue exch def
	/FrameSepGreen exch def
	/FrameSepRed exch def
	/FrameSepBlack exch def
	/FrameSepYellow exch def
	/FrameSepMagenta exch def
	/FrameSepCyan exch def
	/FrameSepIs FMcustom def
	setCurrentScreen	=

} bind def
/FrameSetCyan {
	/FrameSepBlue 1.0 def
	/FrameSepGreen 1.0 def
	/FrameSepRed 0.0 def
	/FrameSepBlack 0.0 def
	/FrameSepYellow 0.0 def
	/FrameSepMagenta 0.0 def
	/FrameSepCyan 1.0 def
	/FrameSepIs FMcyan def
	setCurrentScreen	=

} bind def
 =

/FrameSetMagenta {
	/FrameSepBlue 1.0 def
	/FrameSepGreen 0.0 def
	/FrameSepRed 1.0 def
	/FrameSepBlack 0.0 def
	/FrameSepYellow 0.0 def
	/FrameSepMagenta 1.0 def
	/FrameSepCyan 0.0 def
	/FrameSepIs FMmagenta def
	setCurrentScreen
} bind def
 =

/FrameSetYellow {
	/FrameSepBlue 0.0 def
	/FrameSepGreen 1.0 def
	/FrameSepRed 1.0 def
	/FrameSepBlack 0.0 def
	/FrameSepYellow 1.0 def
	/FrameSepMagenta 0.0 def
	/FrameSepCyan 0.0 def
	/FrameSepIs FMyellow def
	setCurrentScreen
} bind def
 =

/FrameSetBlack {
	/FrameSepBlue 0.0 def
	/FrameSepGreen 0.0 def
	/FrameSepRed 0.0 def
	/FrameSepBlack 1.0 def
	/FrameSepYellow 0.0 def
	/FrameSepMagenta 0.0 def
	/FrameSepCyan 0.0 def
	/FrameSepIs FMblack def
	setCurrentScreen
} bind def
 =

/FrameNoSep { =

	/FrameSepIs FMnone def
	setCurrentScreen
} bind def
/FrameSetSepColors { =

	FrameDict begin
	[ exch 1 add 1 roll ]
	/FrameSepColors  =

	exch def end
	} bind def
/FrameColorInSepListCMYK { =

	FrameSepColors {  =

       		exch dup 3 -1 roll =

       		FrameCmpColorsCMYK =

       		{ pop true exit } if
    	} forall =

	dup true ne {pop false} if
	} bind def
/FrameColorInSepListRGB { =

	FrameSepColors {  =

       		exch dup 3 -1 roll =

       		FrameCmpColorsRGB =

       		{ pop true exit } if
    	} forall =

	dup true ne {pop false} if
	} bind def
/RealSetgray /setgray load def
/RealSetrgbcolor /setrgbcolor load def
/RealSethsbcolor /sethsbcolor load def
end =

/setgray { =

	FrameDict begin
	FrameSepIs FMnone eq
		{ RealSetgray } =

		{ =

		FrameSepIs FMblack eq =

			{ RealSetgray } =

			{ FrameSepIs FMcustom eq =

			  FrameSepRed 0 eq and
			  FrameSepGreen 0 eq and
			  FrameSepBlue 0 eq and {
			  	RealSetgray
			  } {
				1 RealSetgray pop =

			  } ifelse
			} ifelse
		} ifelse
	end
} bind def
/setrgbcolor { =

	FrameDict begin
	FrameSepIs FMnone eq
	{  RealSetrgbcolor }
	{
		3 copy [ 4 1 roll ] =

		FrameColorInSepListRGB
		{
				FrameSepBlue eq exch =

			 	FrameSepGreen eq and exch =

			 	FrameSepRed eq and
			 	{ 0 } { 1 } ifelse
		}
		{
			FMPColor {
				RealSetrgbcolor
				currentcmykcolor
			} {
				RGBtoCMYK
			} ifelse
			FrameSepIs FMblack eq
			{1.0 exch sub 4 1 roll pop pop pop} {
			FrameSepIs FMyellow eq
			{pop 1.0 exch sub 3 1 roll pop pop} {
			FrameSepIs FMmagenta eq
			{pop pop 1.0 exch sub exch pop } {
			FrameSepIs FMcyan eq
			{pop pop pop 1.0 exch sub } =

			{pop pop pop pop 1} ifelse } ifelse } ifelse } ifelse =

		} ifelse
		RealSetgray
	} =

	ifelse
	end
} bind def
/sethsbcolor {
	FrameDict begin
	FrameSepIs FMnone eq =

	{ RealSethsbcolor } =

	{
		RealSethsbcolor =

		currentrgbcolor  =

		setrgbcolor =

	} =

	ifelse
	end
} bind def
FrameDict begin
/setcmykcolor where {
	pop /RealSetcmykcolor /setcmykcolor load def
} {
	/RealSetcmykcolor {
		4 1 roll
		3 { 3 index add 0 max 1 min 1 exch sub 3 1 roll} repeat =

		setrgbcolor pop
	} bind def
} ifelse
userdict /setcmykcolor { =

		FrameDict begin
		FrameSepIs FMnone eq
		{ RealSetcmykcolor } =

		{
			4 copy [ 5 1 roll ]
			FrameColorInSepListCMYK
			{
				FrameSepBlack eq exch =

				FrameSepYellow eq and exch =

				FrameSepMagenta eq and exch =

				FrameSepCyan eq and =

				{ 0 } { 1 } ifelse
			}
			{
				FrameSepIs FMblack eq
				{1.0 exch sub 4 1 roll pop pop pop} {
				FrameSepIs FMyellow eq
				{pop 1.0 exch sub 3 1 roll pop pop} {
				FrameSepIs FMmagenta eq
				{pop pop 1.0 exch sub exch pop } {
				FrameSepIs FMcyan eq
				{pop pop pop 1.0 exch sub } =

				{pop pop pop pop 1} ifelse } ifelse } ifelse } ifelse =

			} ifelse
			RealSetgray
		}
		ifelse
		end
	} bind put
FMLevel1 not { =

	=

	/patProcDict 5 dict dup begin
		<0f1e3c78f0e1c387> { 3 setlinewidth -1 -1 moveto 9 9 lineto stroke =

											4 -4 moveto 12 4 lineto stroke
											-4 4 moveto 4 12 lineto stroke} bind def
		<0f87c3e1f0783c1e> { 3 setlinewidth -1 9 moveto 9 -1 lineto stroke =

											-4 4 moveto 4 -4 lineto stroke
											4 12 moveto 12 4 lineto stroke} bind def
		<8142241818244281> { 1 setlinewidth -1 9 moveto 9 -1 lineto stroke
											-1 -1 moveto 9 9 lineto stroke } bind def
		<03060c183060c081> { 1 setlinewidth -1 -1 moveto 9 9 lineto stroke =

											4 -4 moveto 12 4 lineto stroke
											-4 4 moveto 4 12 lineto stroke} bind def
		<8040201008040201> { 1 setlinewidth -1 9 moveto 9 -1 lineto stroke =

											-4 4 moveto 4 -4 lineto stroke
											4 12 moveto 12 4 lineto stroke} bind def
	end def
	/patDict 15 dict dup begin
		/PatternType 1 def		=

		/PaintType 2 def		=

		/TilingType 3 def		=

		/BBox [ 0 0 8 8 ] def 	=

		/XStep 8 def			=

		/YStep 8 def			=

		/PaintProc {
			begin
			patProcDict bstring known {
				patProcDict bstring get exec
			} {
				8 8 true [1 0 0 -1 0 8] bstring imagemask
			} ifelse
			end
		} bind def
	end def
} if
/combineColor {
    FrameSepIs FMnone eq
	{
		graymode FMLevel1 or not {
			=

			[/Pattern [/DeviceCMYK]] setcolorspace
			FrameCurColors 0 4 getinterval aload pop FrameCurPat setcolor
		} {
			FrameCurColors 3 get 1.0 ge {
				FrameCurGray RealSetgray
			} {
				FMPColor graymode and {
					0 1 3 { =

						FrameCurColors exch get
						1 FrameCurGray sub mul
					} for
					RealSetcmykcolor
				} {
					4 1 6 {
						FrameCurColors exch get
						graymode {
							1 exch sub 1 FrameCurGray sub mul 1 exch sub
						} {
							1.0 lt {FrameCurGray} {1} ifelse
						} ifelse
					} for
					RealSetrgbcolor
				} ifelse
			} ifelse
		} ifelse
	} { =

		FrameCurColors 0 4 getinterval aload
		FrameColorInSepListCMYK {
			FrameSepBlack eq exch =

			FrameSepYellow eq and exch =

			FrameSepMagenta eq and exch =

			FrameSepCyan eq and
			FrameSepIs FMcustom eq and
			{ FrameCurGray } { 1 } ifelse
		} {
			FrameSepIs FMblack eq
			{FrameCurGray 1.0 exch sub mul 1.0 exch sub 4 1 roll pop pop pop} {
			FrameSepIs FMyellow eq
			{pop FrameCurGray 1.0 exch sub mul 1.0 exch sub 3 1 roll pop pop} {
			FrameSepIs FMmagenta eq
			{pop pop FrameCurGray 1.0 exch sub mul 1.0 exch sub exch pop } {
			FrameSepIs FMcyan eq
			{pop pop pop FrameCurGray 1.0 exch sub mul 1.0 exch sub } =

			{pop pop pop pop 1} ifelse } ifelse } ifelse } ifelse =

		} ifelse
		graymode FMLevel1 or not {
			=

			[/Pattern [/DeviceGray]] setcolorspace
			FrameCurPat setcolor
		} { =

			graymode not FMLevel1 and {
				=

				dup 1 lt {pop FrameCurGray} if
			} if
			RealSetgray
		} ifelse
	} ifelse
} bind def
/savematrix {
	orgmatrix currentmatrix pop
	} bind def
/restorematrix {
	orgmatrix setmatrix
	} bind def
/dmatrix matrix def
/dpi    72 0 dmatrix defaultmatrix dtransform
    dup mul exch   dup mul add   sqrt def
	=

/freq dpi dup 72 div round dup 0 eq {pop 1} if 8 mul div def
/sangle 1 0 dmatrix defaultmatrix dtransform exch atan def
/dpiranges   [  2540    2400    1693     1270    1200     635      600   =
   0      ] def
/CMLowFreqs  [ 100.402  94.8683 89.2289 100.402  94.8683  66.9349  63.245=
6 47.4342 ] def
/YLowFreqs   [  95.25   90.0    84.65    95.25   90.0     70.5556  66.666=
7 50.0    ] def
/KLowFreqs   [  89.8026 84.8528 79.8088  89.8026 84.8528  74.8355  70.710=
7 53.033  ] def
/CLowAngles  [  71.5651 71.5651 71.5651 71.5651  71.5651  71.5651  71.565=
1 71.5651 ] def
/MLowAngles  [  18.4349 18.4349 18.4349 18.4349  18.4349  18.4349  18.434=
9 18.4349 ] def
/YLowTDot    [  true    true    false    true    true     false    false =
  false   ] def
/CMHighFreqs [ 133.87  126.491 133.843  108.503 102.523  100.402   94.868=
3 63.2456 ] def
/YHighFreqs  [ 127.0   120.0   126.975  115.455 109.091   95.25    90.0  =
  60.0    ] def
/KHighFreqs  [ 119.737 113.137 119.713  128.289 121.218   89.8026  84.852=
8 63.6395 ] def
/CHighAngles [  71.5651 71.5651 71.5651  70.0169 70.0169  71.5651  71.565=
1 71.5651 ] def
/MHighAngles [  18.4349 18.4349 18.4349  19.9831 19.9831  18.4349  18.434=
9 18.4349 ] def
/YHighTDot   [  false   false   true     false   false    true     true  =
  false   ] def
/PatFreq     [	10.5833 10.0     9.4055  10.5833 10.0	  10.5833  10.0	   9=
=2E375   ] def
/screenIndex {
	0 1 dpiranges length 1 sub { dup dpiranges exch get 1 sub dpi le {exit} =
{pop} ifelse } for
} bind def
/getCyanScreen {
	FMUseHighFrequencyScreens { CHighAngles CMHighFreqs} {CLowAngles CMLowFr=
eqs} ifelse
		screenIndex dup 3 1 roll get 3 1 roll get /FMSpotFunction load
} bind def
/getMagentaScreen {
	FMUseHighFrequencyScreens { MHighAngles CMHighFreqs } {MLowAngles CMLowF=
reqs} ifelse
		screenIndex dup 3 1 roll get 3 1 roll get /FMSpotFunction load
} bind def
/getYellowScreen {
	FMUseHighFrequencyScreens { YHighTDot YHighFreqs} { YLowTDot YLowFreqs }=
 ifelse
		screenIndex dup 3 1 roll get 3 1 roll get { 3 div
			{2 { 1 add 2 div 3 mul dup floor sub 2 mul 1 sub exch} repeat
			FMSpotFunction } } {/FMSpotFunction load } ifelse
			0.0 exch
} bind def
/getBlackScreen  {
	FMUseHighFrequencyScreens { KHighFreqs } { KLowFreqs } ifelse
		screenIndex get 45.0 /FMSpotFunction load =

} bind def
/getSpotScreen {
	getBlackScreen
} bind def
/getCompositeScreen {
	getBlackScreen
} bind def
/FMSetScreen =

	FMLevel1 { /setscreen load =

	}{ {
		8 dict begin
		/HalftoneType 1 def
		/SpotFunction exch def
		/Angle exch def
		/Frequency exch def
		/AccurateScreens FMUseAcccurateScreens def
		currentdict end sethalftone
	} bind } ifelse
def
/setDefaultScreen {
	FMPColor {
		orgrxfer cvx orggxfer cvx orgbxfer cvx orgxfer cvx setcolortransfer
	}
	{
		orgxfer cvx settransfer
	} ifelse
	orgfreq organgle orgproc cvx setscreen
} bind def
/setCurrentScreen {
	FrameSepIs FMnone eq {
		FMUseDefaultNoSeparationScreen {
			setDefaultScreen
		} {
			getCompositeScreen FMSetScreen
		} ifelse
	} {
		FrameSepIs FMcustom eq {
			FMUseDefaultSpotSeparationScreen {
				setDefaultScreen
			} {
				getSpotScreen FMSetScreen
			} ifelse
		} {
			FMUseDefaultProcessSeparationScreen {
				setDefaultScreen
			} {
				FrameSepIs FMcyan eq {
					getCyanScreen FMSetScreen
				} {
					FrameSepIs FMmagenta eq {
						getMagentaScreen FMSetScreen
					} {
						FrameSepIs FMyellow eq {
							getYellowScreen FMSetScreen
						} {
							getBlackScreen FMSetScreen
						} ifelse
					} ifelse
				} ifelse
			} ifelse
		} ifelse
	} ifelse =

} bind def
end
	/gstring FMLOCAL
	/gfile FMLOCAL
	/gindex FMLOCAL
	/orgrxfer FMLOCAL
	/orggxfer FMLOCAL
	/orgbxfer FMLOCAL
	/orgxfer FMLOCAL
	/orgproc FMLOCAL
	/orgrproc FMLOCAL
	/orggproc FMLOCAL
	/orgbproc FMLOCAL
	/organgle FMLOCAL
	/orgrangle FMLOCAL
	/orggangle FMLOCAL
	/orgbangle FMLOCAL
	/orgfreq FMLOCAL
	/orgrfreq FMLOCAL
	/orggfreq FMLOCAL
	/orgbfreq FMLOCAL
	/yscale FMLOCAL
	/xscale FMLOCAL
	/edown FMLOCAL
	/manualfeed FMLOCAL
	/paperheight FMLOCAL
	/paperwidth FMLOCAL
/FMDOCUMENT { =

	array /FMfonts exch def =

	/#copies exch def
	FrameDict begin
	0 ne /manualfeed exch def
	/paperheight exch def
	/paperwidth exch def
	0 ne /FrameNegative exch def =

	0 ne /edown exch def =

	/yscale exch def
	/xscale exch def
	FMLevel1 {
		manualfeed {setmanualfeed} if
		/FMdicttop countdictstack 1 add def =

		/FMoptop count def =

		setpapername =

		manualfeed {true} {papersize} ifelse =

		{manualpapersize} {false} ifelse =

		{desperatepapersize} {false} ifelse =

		{ (Can't select requested paper size for Frame print job!) FMFAILURE } =
if
		count -1 FMoptop {pop pop} for
		countdictstack -1 FMdicttop {pop end} for =

		}
		{{1 dict dup /PageSize [paperwidth paperheight]put setpagedevice}stoppe=
d
		{ (Can't select requested paper size for Frame print job!) FMFAILURE } =
if
		 {1 dict dup /ManualFeed manualfeed put setpagedevice } stopped pop }
	ifelse =

	=

	FMPColor {
		currentcolorscreen
			cvlit /orgproc exch def
				  /organgle exch def =

				  /orgfreq exch def
			cvlit /orgbproc exch def
				  /orgbangle exch def =

				  /orgbfreq exch def
			cvlit /orggproc exch def
				  /orggangle exch def =

				  /orggfreq exch def
			cvlit /orgrproc exch def
				  /orgrangle exch def =

				  /orgrfreq exch def
			currentcolortransfer =

			FrameNegative {
				1 1 4 { =

					pop { 1 exch sub } concatprocs 4 1 roll
				} for
				4 copy
				setcolortransfer
			} if
			cvlit /orgxfer exch def
			cvlit /orgbxfer exch def
			cvlit /orggxfer exch def
			cvlit /orgrxfer exch def
	} {
		currentscreen =

			cvlit /orgproc exch def
				  /organgle exch def =

				  /orgfreq exch def
				  =

		currenttransfer =

		FrameNegative {
			{ 1 exch sub } concatprocs
			dup settransfer
		} if =

		cvlit /orgxfer exch def
	} ifelse
	end =

} def =

/pagesave FMLOCAL
/orgmatrix FMLOCAL
/landscape FMLOCAL
/pwid FMLOCAL
/FMBEGINPAGE { =

	FrameDict begin =

	/pagesave save def
	3.86 setmiterlimit
	/landscape exch 0 ne def
	landscape { =

		90 rotate 0 exch dup /pwid exch def neg translate pop =

	}{
		pop /pwid exch def
	} ifelse
	edown { [-1 0 0 1 pwid 0] concat } if
	0 0 moveto paperwidth 0 lineto paperwidth paperheight lineto =

	0 paperheight lineto 0 0 lineto 1 setgray fill
	xscale yscale scale
	/orgmatrix matrix def
	gsave =

} def =

/FMENDPAGE {
	grestore =

	pagesave restore
	end =

	showpage
	} def =

/FMFONTDEFINE { =

	FrameDict begin
	findfont =

	ReEncode =

	1 index exch =

	definefont =

	FMfonts 3 1 roll =

	put
	end =

	} def =

/FMFILLS {
	FrameDict begin dup
	array /fillvals exch def
	dict /patCache exch def
	end =

	} def =

/FMFILL {
	FrameDict begin
	 fillvals 3 1 roll put
	end =

	} def =

/FMNORMALIZEGRAPHICS { =

	newpath
	0.0 0.0 moveto
	1 setlinewidth
	0 setlinecap
	0 0 0 sethsbcolor
	0 setgray =

	} bind def
	/fx FMLOCAL
	/fy FMLOCAL
	/fh FMLOCAL
	/fw FMLOCAL
	/llx FMLOCAL
	/lly FMLOCAL
	/urx FMLOCAL
	/ury FMLOCAL
/FMBEGINEPSF { =

	end =

	/FMEPSF save def =

	/showpage {} def =

% See Adobe's "PostScript Language Reference Manual, 2nd Edition", page 7=
14.
% "...the following operators MUST NOT be used in an EPS file:" (emphasis=
 ours)
	/banddevice {(banddevice) FMBADEPSF} def
	/clear {(clear) FMBADEPSF} def
	/cleardictstack {(cleardictstack) FMBADEPSF} def =

	/copypage {(copypage) FMBADEPSF} def
	/erasepage {(erasepage) FMBADEPSF} def
	/exitserver {(exitserver) FMBADEPSF} def
	/framedevice {(framedevice) FMBADEPSF} def
	/grestoreall {(grestoreall) FMBADEPSF} def
	/initclip {(initclip) FMBADEPSF} def
	/initgraphics {(initgraphics) FMBADEPSF} def
	/initmatrix {(initmatrix) FMBADEPSF} def
	/quit {(quit) FMBADEPSF} def
	/renderbands {(renderbands) FMBADEPSF} def
	/setglobal {(setglobal) FMBADEPSF} def
	/setpagedevice {(setpagedevice) FMBADEPSF} def
	/setshared {(setshared) FMBADEPSF} def
	/startjob {(startjob) FMBADEPSF} def
	/lettertray {(lettertray) FMBADEPSF} def
	/letter {(letter) FMBADEPSF} def
	/lettersmall {(lettersmall) FMBADEPSF} def
	/11x17tray {(11x17tray) FMBADEPSF} def
	/11x17 {(11x17) FMBADEPSF} def
	/ledgertray {(ledgertray) FMBADEPSF} def
	/ledger {(ledger) FMBADEPSF} def
	/legaltray {(legaltray) FMBADEPSF} def
	/legal {(legal) FMBADEPSF} def
	/statementtray {(statementtray) FMBADEPSF} def
	/statement {(statement) FMBADEPSF} def
	/executivetray {(executivetray) FMBADEPSF} def
	/executive {(executive) FMBADEPSF} def
	/a3tray {(a3tray) FMBADEPSF} def
	/a3 {(a3) FMBADEPSF} def
	/a4tray {(a4tray) FMBADEPSF} def
	/a4 {(a4) FMBADEPSF} def
	/a4small {(a4small) FMBADEPSF} def
	/b4tray {(b4tray) FMBADEPSF} def
	/b4 {(b4) FMBADEPSF} def
	/b5tray {(b5tray) FMBADEPSF} def
	/b5 {(b5) FMBADEPSF} def
	FMNORMALIZEGRAPHICS =

	[/fy /fx /fh /fw /ury /urx /lly /llx] {exch def} forall =

	fx fw 2 div add fy fh 2 div add  translate
	rotate
	fw 2 div neg fh 2 div neg translate
	fw urx llx sub div fh ury lly sub div scale =

	llx neg lly neg translate =

	/FMdicttop countdictstack 1 add def =

	/FMoptop count def =

	} bind def
/FMENDEPSF {
	count -1 FMoptop {pop pop} for =

	countdictstack -1 FMdicttop {pop end} for =

	FMEPSF restore
	FrameDict begin =

	} bind def
FrameDict begin =

/setmanualfeed {
%%BeginFeature *ManualFeed True
	 statusdict /manualfeed true put
%%EndFeature
	} bind def
/max {2 copy lt {exch} if pop} bind def
/min {2 copy gt {exch} if pop} bind def
/inch {72 mul} def
/pagedimen { =

	paperheight sub abs 16 lt exch =

	paperwidth sub abs 16 lt and
	{/papername exch def} {pop} ifelse
	} bind def
	/papersizedict FMLOCAL
/setpapername { =

	/papersizedict 14 dict def =

	papersizedict begin
	/papername /unknown def =

		/Letter 8.5 inch 11.0 inch pagedimen
		/LetterSmall 7.68 inch 10.16 inch pagedimen
		/Tabloid 11.0 inch 17.0 inch pagedimen
		/Ledger 17.0 inch 11.0 inch pagedimen
		/Legal 8.5 inch 14.0 inch pagedimen
		/Statement 5.5 inch 8.5 inch pagedimen
		/Executive 7.5 inch 10.0 inch pagedimen
		/A3 11.69 inch 16.5 inch pagedimen
		/A4 8.26 inch 11.69 inch pagedimen
		/A4Small 7.47 inch 10.85 inch pagedimen
		/B4 10.125 inch 14.33 inch pagedimen
		/B5 7.16 inch 10.125 inch pagedimen
	end
	} bind def
/papersize {
	papersizedict begin
		/Letter {lettertray letter} def
		/LetterSmall {lettertray lettersmall} def
		/Tabloid {11x17tray 11x17} def
		/Ledger {ledgertray ledger} def
		/Legal {legaltray legal} def
		/Statement {statementtray statement} def
		/Executive {executivetray executive} def
		/A3 {a3tray a3} def
		/A4 {a4tray a4} def
		/A4Small {a4tray a4small} def
		/B4 {b4tray b4} def
		/B5 {b5tray b5} def
		/unknown {unknown} def
	papersizedict dup papername known {papername} {/unknown} ifelse get
	end
	statusdict begin stopped end =

	} bind def
/manualpapersize {
	papersizedict begin
		/Letter {letter} def
		/LetterSmall {lettersmall} def
		/Tabloid {11x17} def
		/Ledger {ledger} def
		/Legal {legal} def
		/Statement {statement} def
		/Executive {executive} def
		/A3 {a3} def
		/A4 {a4} def
		/A4Small {a4small} def
		/B4 {b4} def
		/B5 {b5} def
		/unknown {unknown} def
	papersizedict dup papername known {papername} {/unknown} ifelse get
	end
	stopped =

	} bind def
/desperatepapersize {
	statusdict /setpageparams known
		{
		paperwidth paperheight 0 1 =

		statusdict begin
		{setpageparams} stopped =

		end
		} {true} ifelse =

	} bind def
/DiacriticEncoding [
/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
/.notdef /.notdef /.notdef /.notdef /space /exclam /quotedbl
/numbersign /dollar /percent /ampersand /quotesingle /parenleft
/parenright /asterisk /plus /comma /hyphen /period /slash /zero /one
/two /three /four /five /six /seven /eight /nine /colon /semicolon
/less /equal /greater /question /at /A /B /C /D /E /F /G /H /I /J /K
/L /M /N /O /P /Q /R /S /T /U /V /W /X /Y /Z /bracketleft /backslash
/bracketright /asciicircum /underscore /grave /a /b /c /d /e /f /g /h
/i /j /k /l /m /n /o /p /q /r /s /t /u /v /w /x /y /z /braceleft /bar
/braceright /asciitilde /.notdef /Adieresis /Aring /Ccedilla /Eacute
/Ntilde /Odieresis /Udieresis /aacute /agrave /acircumflex /adieresis
/atilde /aring /ccedilla /eacute /egrave /ecircumflex /edieresis
/iacute /igrave /icircumflex /idieresis /ntilde /oacute /ograve
/ocircumflex /odieresis /otilde /uacute /ugrave /ucircumflex
/udieresis /dagger /.notdef /cent /sterling /section /bullet
/paragraph /germandbls /registered /copyright /trademark /acute
/dieresis /.notdef /AE /Oslash /.notdef /.notdef /.notdef /.notdef
/yen /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
/ordfeminine /ordmasculine /.notdef /ae /oslash /questiondown
/exclamdown /logicalnot /.notdef /florin /.notdef /.notdef
/guillemotleft /guillemotright /ellipsis /.notdef /Agrave /Atilde
/Otilde /OE /oe /endash /emdash /quotedblleft /quotedblright
/quoteleft /quoteright /.notdef /.notdef /ydieresis /Ydieresis
/fraction /currency /guilsinglleft /guilsinglright /fi /fl /daggerdbl
/periodcentered /quotesinglbase /quotedblbase /perthousand
/Acircumflex /Ecircumflex /Aacute /Edieresis /Egrave /Iacute
/Icircumflex /Idieresis /Igrave /Oacute /Ocircumflex /.notdef /Ograve
/Uacute /Ucircumflex /Ugrave /dotlessi /circumflex /tilde /macron
/breve /dotaccent /ring /cedilla /hungarumlaut /ogonek /caron
] def
/ReEncode { =

	dup =

	length =

	dict begin =

	{
	1 index /FID ne =

		{def} =

		{pop pop} ifelse =

	} forall =

	0 eq {/Encoding DiacriticEncoding def} if =

	currentdict =

	end =

	} bind def
FMPColor =

	=

	{
	/BEGINBITMAPCOLOR { =

		BITMAPCOLOR} def
	/BEGINBITMAPCOLORc { =

		BITMAPCOLORc} def
	/BEGINBITMAPTRUECOLOR { =

		BITMAPTRUECOLOR } def
	/BEGINBITMAPTRUECOLORc { =

		BITMAPTRUECOLORc } def
	}
	=

	{
	/BEGINBITMAPCOLOR { =

		BITMAPGRAY} def
	/BEGINBITMAPCOLORc { =

		BITMAPGRAYc} def
	/BEGINBITMAPTRUECOLOR { =

		BITMAPTRUEGRAY } def
	/BEGINBITMAPTRUECOLORc { =

		BITMAPTRUEGRAYc } def
	}
ifelse
/K { =

	FMPrintAllColorsAsBlack {
		dup 1 eq 2 index 1 eq and 3 index 1 eq and not
			{7 {pop} repeat 0 0 0 1 0 0 0} if
	} if =

	FrameCurColors astore =

	pop combineColor
} bind def
/graymode true def
	/bwidth FMLOCAL
	/bpside FMLOCAL
	/bstring FMLOCAL
	/onbits FMLOCAL
	/offbits FMLOCAL
	/xindex FMLOCAL
	/yindex FMLOCAL
	/x FMLOCAL
	/y FMLOCAL
/setPatternMode {
	FMLevel1 {
		/bwidth  exch def
		/bpside  exch def
		/bstring exch def
		/onbits 0 def  /offbits 0 def
		freq sangle landscape {90 add} if =

			{/y exch def
			 /x exch def
			 /xindex x 1 add 2 div bpside mul cvi def
			 /yindex y 1 add 2 div bpside mul cvi def
			 bstring yindex bwidth mul xindex 8 idiv add get
			 1 7 xindex 8 mod sub bitshift and 0 ne FrameNegative {not} if
			 {/onbits  onbits  1 add def 1}
			 {/offbits offbits 1 add def 0}
			 ifelse
			}
			setscreen
		offbits offbits onbits add div FrameNegative {1.0 exch sub} if
		/FrameCurGray exch def
	} { =

		pop pop
		dup patCache exch known {
			patCache exch get
		} { =

			dup
			patDict /bstring 3 -1 roll put
			patDict =

			9 PatFreq screenIndex get div dup matrix scale
			makepattern
			dup =

			patCache 4 -1 roll 3 -1 roll put
		} ifelse
		/FrameCurGray 0 def
		/FrameCurPat exch def
	} ifelse
	/graymode false def
	combineColor
} bind def
/setGrayScaleMode {
	graymode not {
		/graymode true def
		FMLevel1 {
			setCurrentScreen
		} if
	} if
	/FrameCurGray exch def
	combineColor
} bind def
/normalize {
	transform round exch round exch itransform
	} bind def
/dnormalize {
	dtransform round exch round exch idtransform
	} bind def
/lnormalize { =

	0 dtransform exch cvi 2 idiv 2 mul 1 add exch idtransform pop
	} bind def
/H { =

	lnormalize setlinewidth
	} bind def
/Z {
	setlinecap
	} bind def
	=

/PFill {
	graymode FMLevel1 or not {
		gsave 1 setgray eofill grestore
	} if
} bind def
/PStroke {
	graymode FMLevel1 or not {
		gsave 1 setgray stroke grestore
	} if
	stroke
} bind def
	/fillvals FMLOCAL
/X { =

	fillvals exch get
	dup type /stringtype eq
	{8 1 setPatternMode} =

	{setGrayScaleMode}
	ifelse
	} bind def
/V { =

	PFill gsave eofill grestore
	} bind def
/Vclip {
	clip
	} bind def
/Vstrk {
	currentlinewidth exch setlinewidth PStroke setlinewidth
	} bind def
/N { =

	PStroke
	} bind def
/Nclip {
	strokepath clip newpath
	} bind def
/Nstrk {
	currentlinewidth exch setlinewidth PStroke setlinewidth
	} bind def
/M {newpath moveto} bind def
/E {lineto} bind def
/D {curveto} bind def
/O {closepath} bind def
	/n FMLOCAL
/L { =

 	/n exch def
	newpath
	normalize
	moveto =

	2 1 n {pop normalize lineto} for
	} bind def
/Y { =

	L =

	closepath
	} bind def
	/x1 FMLOCAL
	/x2 FMLOCAL
	/y1 FMLOCAL
	/y2 FMLOCAL
/R { =

	/y2 exch def
	/x2 exch def
	/y1 exch def
	/x1 exch def
	x1 y1
	x2 y1
	x2 y2
	x1 y2
	4 Y =

	} bind def
	/rad FMLOCAL
/rarc =

	{rad =

	 arcto
	} bind def
/RR { =

	/rad exch def
	normalize
	/y2 exch def
	/x2 exch def
	normalize
	/y1 exch def
	/x1 exch def
	mark
	newpath
	{
	x1 y1 rad add moveto
	x1 y2 x2 y2 rarc
	x2 y2 x2 y1 rarc
	x2 y1 x1 y1 rarc
	x1 y1 x1 y2 rarc
	closepath
	} stopped {x1 y1 x2 y2 R} if =

	cleartomark
	} bind def
/RRR { =

	/rad exch def
	normalize /y4 exch def /x4 exch def
	normalize /y3 exch def /x3 exch def
	normalize /y2 exch def /x2 exch def
	normalize /y1 exch def /x1 exch def
	newpath
	normalize moveto =

	mark
	{
	x2 y2 x3 y3 rarc
	x3 y3 x4 y4 rarc
	x4 y4 x1 y1 rarc
	x1 y1 x2 y2 rarc
	closepath
	} stopped
	 {x1 y1 x2 y2 x3 y3 x4 y4 newpath moveto lineto lineto lineto closepath}=
 if
	cleartomark
	} bind def
/C { =

	grestore
	gsave
	R =

	clip
	setCurrentScreen
} bind def
/CP { =

	grestore
	gsave
	Y =

	clip
	setCurrentScreen
} bind def
	/FMpointsize FMLOCAL
/F { =

	FMfonts exch get
	FMpointsize scalefont
	setfont
	} bind def
/Q { =

	/FMpointsize exch def
	F =

	} bind def
/T { =

	moveto show
	} bind def
/RF { =

	rotate
	0 ne {-1 1 scale} if
	} bind def
/TF { =

	gsave
	moveto =

	RF
	show
	grestore
	} bind def
/P { =

	moveto
	0 32 3 2 roll widthshow
	} bind def
/PF { =

	gsave
	moveto =

	RF
	0 32 3 2 roll widthshow
	grestore
	} bind def
/S { =

	moveto
	0 exch ashow
	} bind def
/SF { =

	gsave
	moveto
	RF
	0 exch ashow
	grestore
	} bind def
/B { =

	moveto
	0 32 4 2 roll 0 exch awidthshow
	} bind def
/BF { =

	gsave
	moveto
	RF
	0 32 4 2 roll 0 exch awidthshow
	grestore
	} bind def
/G { =

	gsave
	newpath
	normalize translate 0.0 0.0 moveto =

	dnormalize scale =

	0.0 0.0 1.0 5 3 roll arc =

	closepath =

	PFill fill
	grestore
	} bind def
/Gstrk {
	savematrix
    newpath
    2 index 2 div add exch 3 index 2 div sub exch =

    normalize 2 index 2 div sub exch 3 index 2 div add exch =

    translate
    scale =

    0.0 0.0 1.0 5 3 roll arc =

    restorematrix
    currentlinewidth exch setlinewidth PStroke setlinewidth
    } bind def
/Gclip { =

	newpath
	savematrix
	normalize translate 0.0 0.0 moveto =

	dnormalize scale =

	0.0 0.0 1.0 5 3 roll arc =

	closepath =

	clip newpath
	restorematrix
	} bind def
/GG { =

	gsave
	newpath
	normalize translate 0.0 0.0 moveto =

	rotate =

	dnormalize scale =

	0.0 0.0 1.0 5 3 roll arc =

	closepath
	PFill
	fill
	grestore
	} bind def
/GGclip { =

	savematrix
	newpath
    normalize translate 0.0 0.0 moveto =

    rotate =

    dnormalize scale =

    0.0 0.0 1.0 5 3 roll arc =

    closepath
	clip newpath
	restorematrix
	} bind def
/GGstrk { =

	savematrix
    newpath
    normalize translate 0.0 0.0 moveto =

    rotate =

    dnormalize scale =

    0.0 0.0 1.0 5 3 roll arc =

    closepath =

	restorematrix
    currentlinewidth exch setlinewidth PStroke setlinewidth
	} bind def
/A { =

	gsave
	savematrix
	newpath
	2 index 2 div add exch 3 index 2 div sub exch =

	normalize 2 index 2 div sub exch 3 index 2 div add exch =

	translate =

	scale =

	0.0 0.0 1.0 5 3 roll arc =

	restorematrix
	PStroke
	grestore
	} bind def
/Aclip {
	newpath
	savematrix
	normalize translate 0.0 0.0 moveto =

	dnormalize scale =

	0.0 0.0 1.0 5 3 roll arc =

	closepath =

	strokepath clip newpath
	restorematrix
} bind def
/Astrk {
	Gstrk
} bind def
/AA { =

	gsave
	savematrix
	newpath
	=

	3 index 2 div add exch 4 index 2 div sub exch =

	=

	normalize 3 index 2 div sub exch 4 index 2 div add exch
	translate =

	rotate =

	scale =

	0.0 0.0 1.0 5 3 roll arc =

	restorematrix
	PStroke
	grestore
	} bind def
/AAclip {
	savematrix
	newpath
    normalize translate 0.0 0.0 moveto =

    rotate =

    dnormalize scale =

    0.0 0.0 1.0 5 3 roll arc =

    closepath
	strokepath clip newpath
	restorematrix
} bind def
/AAstrk {
	GGstrk
} bind def
	/x FMLOCAL
	/y FMLOCAL
	/w FMLOCAL
	/h FMLOCAL
	/xx FMLOCAL
	/yy FMLOCAL
	/ww FMLOCAL
	/hh FMLOCAL
	/FMsaveobject FMLOCAL
	/FMoptop FMLOCAL
	/FMdicttop FMLOCAL
/BEGINPRINTCODE { =

	/FMdicttop countdictstack 1 add def =

	/FMoptop count 7 sub def =

	/FMsaveobject save def
	userdict begin =

	/showpage {} def =

	FMNORMALIZEGRAPHICS =

	3 index neg 3 index neg translate
	} bind def
/ENDPRINTCODE {
	count -1 FMoptop {pop pop} for =

	countdictstack -1 FMdicttop {pop end} for =

	FMsaveobject restore =

	} bind def
/gn { =

	0 =

	{	46 mul =

		cf read pop =

		32 sub =

		dup 46 lt {exit} if =

		46 sub add =

		} loop
	add =

	} bind def
	/str FMLOCAL
/cfs { =

	/str sl string def =

	0 1 sl 1 sub {str exch val put} for =

	str def =

	} bind def
/ic [ =

	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0223
	0
	{0 hx} {1 hx} {2 hx} {3 hx} {4 hx} {5 hx} {6 hx} {7 hx} {8 hx} {9 hx}
	{10 hx} {11 hx} {12 hx} {13 hx} {14 hx} {15 hx} {16 hx} {17 hx} {18 hx}
	{19 hx} {gn hx} {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12}
	{13} {14} {15} {16} {17} {18} {19} {gn} {0 wh} {1 wh} {2 wh} {3 wh}
	{4 wh} {5 wh} {6 wh} {7 wh} {8 wh} {9 wh} {10 wh} {11 wh} {12 wh}
	{13 wh} {14 wh} {gn wh} {0 bl} {1 bl} {2 bl} {3 bl} {4 bl} {5 bl} {6 bl}=

	{7 bl} {8 bl} {9 bl} {10 bl} {11 bl} {12 bl} {13 bl} {14 bl} {gn bl}
	{0 fl} {1 fl} {2 fl} {3 fl} {4 fl} {5 fl} {6 fl} {7 fl} {8 fl} {9 fl}
	{10 fl} {11 fl} {12 fl} {13 fl} {14 fl} {gn fl}
	] def
	/sl FMLOCAL
	/val FMLOCAL
	/ws FMLOCAL
	/im FMLOCAL
	/bs FMLOCAL
	/cs FMLOCAL
	/len FMLOCAL
	/pos FMLOCAL
/ms { =

	/sl exch def =

	/val 255 def =

	/ws cfs =

	/im cfs =

	/val 0 def =

	/bs cfs =

	/cs cfs =

	} bind def
400 ms =

/ip { =

	is =

	0 =

	cf cs readline pop =

	{	ic exch get exec =

		add =

		} forall =

	pop =

	=

	} bind def
/rip { =

	   =

	  =

	  bis ris copy pop =

      is
      0
      cf cs readline pop =

      {       ic exch get exec =

              add =

              } forall =

	  pop pop =

	  ris gis copy pop =

	  dup is exch =

	  =

      cf cs readline pop =

      {       ic exch get exec =

              add =

              } forall =

	  pop pop
	  gis bis copy pop =

	  dup add is exch =

	  =

      cf cs readline pop =

      {       ic exch get exec =

              add =

              } forall =

      pop =

      =

      } bind def
/wh { =

	/len exch def =

	/pos exch def =

	ws 0 len getinterval im pos len getinterval copy pop
	pos len =

	} bind def
/bl { =

	/len exch def =

	/pos exch def =

	bs 0 len getinterval im pos len getinterval copy pop
	pos len =

	} bind def
/s1 1 string def
/fl { =

	/len exch def =

	/pos exch def =

	/val cf s1 readhexstring pop 0 get def
	pos 1 pos len add 1 sub {im exch val put} for
	pos len =

	} bind def
/hx { =

	3 copy getinterval =

	cf exch readhexstring pop pop =

	} bind def
	/h FMLOCAL
	/w FMLOCAL
	/d FMLOCAL
	/lb FMLOCAL
	/bitmapsave FMLOCAL
	/is FMLOCAL
	/cf FMLOCAL
/wbytes { =

      dup dup
      24 eq { pop pop 3 mul }
      { 8 eq {pop} {1 eq {7 add 8 idiv} {3 add 4 idiv} ifelse} ifelse } i=
felse
	} bind def
/BEGINBITMAPBWc { =

	1 {} COMMONBITMAPc
	} bind def
/BEGINBITMAPGRAYc { =

	8 {} COMMONBITMAPc
	} bind def
/BEGINBITMAP2BITc { =

	2 {} COMMONBITMAPc
	} bind def
/COMMONBITMAPc { =

		 =

	/r exch def
	/d exch def
	gsave
	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/lb w d wbytes def =

	sl lb lt {lb ms} if =

	/bitmapsave save def =

	r                    =

	/is im 0 lb getinterval def =

	ws 0 lb getinterval is copy pop =

	/cf currentfile def =

	w h d [w 0 0 h neg 0 h] =

	{ip} image =

	bitmapsave restore =

	grestore
	} bind def
/BEGINBITMAPBW { =

	1 {} COMMONBITMAP
	} bind def
/BEGINBITMAPGRAY { =

	8 {} COMMONBITMAP
	} bind def
/BEGINBITMAP2BIT { =

	2 {} COMMONBITMAP
	} bind def
/COMMONBITMAP { =

	/r exch def
	/d exch def
	gsave
	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/bitmapsave save def =

	r                    =

	/is w d wbytes string def
	/cf currentfile def =

	w h d [w 0 0 h neg 0 h] =

	{cf is readhexstring pop} image
	bitmapsave restore =

	grestore
	} bind def
/ngrayt 256 array def
/nredt 256 array def
/nbluet 256 array def
/ngreent 256 array def
	/gryt FMLOCAL
	/blut FMLOCAL
	/grnt FMLOCAL
	/redt FMLOCAL
	/indx FMLOCAL
	/cynu FMLOCAL
	/magu FMLOCAL
	/yelu FMLOCAL
	/k FMLOCAL
	/u FMLOCAL
FMLevel1 {
/colorsetup {
	currentcolortransfer
	/gryt exch def
	/blut exch def
	/grnt exch def
	/redt exch def
	0 1 255 {
		/indx exch def
		/cynu 1 red indx get 255 div sub def
		/magu 1 green indx get 255 div sub def
		/yelu 1 blue indx get 255 div sub def
		/k cynu magu min yelu min def
		/u k currentundercolorremoval exec def
%		/u 0 def
		nredt indx 1 0 cynu u sub max sub redt exec put
		ngreent indx 1 0 magu u sub max sub grnt exec put
		nbluet indx 1 0 yelu u sub max sub blut exec put
		ngrayt indx 1 k currentblackgeneration exec sub gryt exec put
	} for
	{255 mul cvi nredt exch get}
	{255 mul cvi ngreent exch get}
	{255 mul cvi nbluet exch get}
	{255 mul cvi ngrayt exch get}
	setcolortransfer
	{pop 0} setundercolorremoval
	{} setblackgeneration
	} bind def
}
{
/colorSetup2 {
	[ /Indexed /DeviceRGB 255 =

		{dup red exch get 255 div =

		 exch dup green exch get 255 div =

		 exch blue exch get 255 div}
	] setcolorspace
} bind def
} ifelse
	/tran FMLOCAL
/fakecolorsetup {
	/tran 256 string def
	0 1 255 {/indx exch def =

		tran indx
		red indx get 77 mul
		green indx get 151 mul
		blue indx get 28 mul
		add add 256 idiv put} for
	currenttransfer
	{255 mul cvi tran exch get 255.0 div}
	exch concatprocs settransfer
} bind def
/BITMAPCOLOR { =

	/d 8 def
	gsave
	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/bitmapsave save def
	FMLevel1 {	=

		colorsetup
		/is w d wbytes string def
		/cf currentfile def =

		w h d [w 0 0 h neg 0 h] =

		{cf is readhexstring pop} {is} {is} true 3 colorimage =

	} {
		colorSetup2
		/is w d wbytes string def
		/cf currentfile def =

		7 dict dup begin
			/ImageType 1 def
			/Width w def
			/Height h def
			/ImageMatrix [w 0 0 h neg 0 h] def
			/DataSource {cf is readhexstring pop} bind def
			/BitsPerComponent d def
			/Decode [0 255] def
		end image	=

	} ifelse
	bitmapsave restore =

	grestore
	} bind def
/BITMAPCOLORc { =

	/d 8 def
	gsave
	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/lb w d wbytes def =

	sl lb lt {lb ms} if =

	/bitmapsave save def =

	FMLevel1 {	=

		colorsetup
		/is im 0 lb getinterval def =

		ws 0 lb getinterval is copy pop =

		/cf currentfile def =

		w h d [w 0 0 h neg 0 h] =

		{ip} {is} {is} true 3 colorimage
	} {
		colorSetup2
		/is im 0 lb getinterval def =

		ws 0 lb getinterval is copy pop =

		/cf currentfile def =

		7 dict dup begin
			/ImageType 1 def
			/Width w def
			/Height h def
			/ImageMatrix [w 0 0 h neg 0 h] def
			/DataSource {ip} bind def
			/BitsPerComponent d def
			/Decode [0 255] def
		end image	=

	} ifelse
	bitmapsave restore =

	grestore
	} bind def
/BITMAPTRUECOLORc { =

	/d 24 def
        gsave
 	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/lb w d wbytes def =

	sl lb lt {lb ms} if =

        /bitmapsave save def =

        =

	/is im 0 lb getinterval def	=

	/ris im 0 w getinterval def	=

	/gis im w w getinterval def	=

	/bis im w 2 mul w getinterval def =

        =

        ws 0 lb getinterval is copy pop =

        /cf currentfile def =

        w h 8 [w 0 0 h neg 0 h] =

        {w rip pop ris} {gis} {bis} true 3 colorimage
        bitmapsave restore =

        grestore
        } bind def
/BITMAPTRUECOLOR { =

        gsave
		=

		3 index 2 div add exch	=

		4 index 2 div add exch	=

		translate		=

		rotate			=

		1 index 2 div neg	=

		1 index 2 div neg	=

		translate		=

		scale			=

		/h exch def /w exch def
        /bitmapsave save def =

        /is w string def
        /gis w string def
        /bis w string def
        /cf currentfile def =

        w h 8 [w 0 0 h neg 0 h] =

        { cf is readhexstring pop } =

        { cf gis readhexstring pop } =

        { cf bis readhexstring pop } =

        true 3 colorimage =

        bitmapsave restore =

        grestore
        } bind def
/BITMAPTRUEGRAYc { =

	/d 24 def
        gsave
	=

	3 index 2 div add exch	=

	4 index 2 div add exch	=

	translate		=

	rotate			=

	1 index 2 div neg	=

	1 index 2 div neg	=

	translate		=

	scale			=

	/h exch def /w exch def
	/lb w d wbytes def =

	sl lb lt {lb ms} if =

        /bitmapsave save def =

        =

	/is im 0 lb getinterval def	=

	/ris im 0 w getinterval def	=

	/gis im w w getinterval def	=

	/bis im w 2 mul w getinterval def =

        ws 0 lb getinterval is copy pop =

        /cf currentfile def =

        w h 8 [w 0 0 h neg 0 h] =

        {w rip pop ris gis bis w gray} image
        bitmapsave restore =

        grestore
        } bind def
/ww FMLOCAL
/r FMLOCAL
/g FMLOCAL
/b FMLOCAL
/i FMLOCAL
/gray { =

        /ww exch def
        /b exch def
        /g exch def
        /r exch def
        0 1 ww 1 sub { /i exch def r i get .299 mul g i get .587 mul
			b i get .114 mul add add r i 3 -1 roll floor cvi put } for
        r
        } bind def
/BITMAPTRUEGRAY { =

        gsave
		=

		3 index 2 div add exch	=

		4 index 2 div add exch	=

		translate		=

		rotate			=

		1 index 2 div neg	=

		1 index 2 div neg	=

		translate		=

		scale			=

		/h exch def /w exch def
        /bitmapsave save def =

        /is w string def
        /gis w string def
        /bis w string def
        /cf currentfile def =

        w h 8 [w 0 0 h neg 0 h] =

        { cf is readhexstring pop =

          cf gis readhexstring pop =

          cf bis readhexstring pop w gray}  image
        bitmapsave restore =

        grestore
        } bind def
/BITMAPGRAY { =

	8 {fakecolorsetup} COMMONBITMAP
	} bind def
/BITMAPGRAYc { =

	8 {fakecolorsetup} COMMONBITMAPc
	} bind def
/ENDBITMAP {
	} bind def
end =

	/ALDsave FMLOCAL
	/ALDmatrix matrix def ALDmatrix currentmatrix pop
/StartALD {
	/ALDsave save def
	 savematrix
	 ALDmatrix setmatrix
	} bind def
/InALD {
	 restorematrix
	} bind def
/DoneALD {
	 ALDsave restore
	} bind def
/I { setdash } bind def
/J { [] 0 setdash } bind def
%%EndProlog
%%BeginSetup
(4.0) FMVERSION
1 1 0 0 612 792 0 1 12 FMDOCUMENT
0 0 /Times-Bold FMFONTDEFINE
1 0 /Times-Roman FMFONTDEFINE
2 0 /Times-BoldItalic FMFONTDEFINE
3 0 /Times-Italic FMFONTDEFINE
4 0 /Courier FMFONTDEFINE
32 FMFILLS
0 0 FMFILL
1 0.1 FMFILL
2 0.3 FMFILL
3 0.5 FMFILL
4 0.7 FMFILL
5 0.9 FMFILL
6 0.97 FMFILL
7 1 FMFILL
8 <0f1e3c78f0e1c387> FMFILL
9 <0f87c3e1f0783c1e> FMFILL
10 <cccccccccccccccc> FMFILL
11 <ffff0000ffff0000> FMFILL
12 <8142241818244281> FMFILL
13 <03060c183060c081> FMFILL
14 <8040201008040201> FMFILL
16 1 FMFILL
17 0.9 FMFILL
18 0.7 FMFILL
19 0.5 FMFILL
20 0.3 FMFILL
21 0.1 FMFILL
22 0.03 FMFILL
23 0 FMFILL
24 <f0e1c3870f1e3c78> FMFILL
25 <f0783c1e0f87c3e1> FMFILL
26 <3333333333333333> FMFILL
27 <0000ffff0000ffff> FMFILL
28 <7ebddbe7e7dbbd7e> FMFILL
29 <fcf9f3e7cf9f3f7e> FMFILL
30 <7fbfdfeff7fbfdfe> FMFILL
%%EndSetup
%%Page: "1" 1
%%BeginPaperSize: Letter
%%EndPaperSize
612 792 0 FMBEGINPAGE
[0 0 0 1 0 0 0]
[ 0 0 0 0 1 1 1]
[ 0 1 1 0 1 0 0]
[ 1 0 1 0 0 1 0]
[ 1 1 0 0 0 0 1]
[ 1 0 0 0 0 1 1]
[ 0 1 0 0 1 0 1]
[ 0 0 1 0 1 1 0]
 8 FrameSetSepColors
FrameNoSep
0 0 0 1 0 0 0 K
J
0 0 0 1 0 0 0 K
0 12 Q
0 X
0 0 0 1 0 0 0 K
(Abstract) 72 609.8 T
0 0 0 0 1 1 1 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 10 Q
0.74 (How are T) 72 592.13 P
0.74 (cl and Tk taught and used in the classroom?) 115.54 592.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.62 (What lessons can be learned in the trenches to ease the) 72 580.13 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.77 (way to teaching T) 72 568.13 P
0.77 (cl and Tk, and to teaching using Tk?) 145.53 568.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.2 (This panel brings together a diverse group of individuals) 72 556.1=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.74 (who use T) 72 544.13 P
2.74 (cl and Tk in the classroom. One teaches) 118.43 544.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.47 (industrial short courses on T) 72 532.13 P
0.47 (cl, Tk, and extensions. The) 187.07 532.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.68 (others use Tk and STk. in academic classrooms as part) 72 520.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.86 (of teaching user interfaces, software engineering, and) 72 508.13 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.63 (programming. The panelists share the challenges they) 72 496.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.17 (have faced and the lessons they have learned in the pro-) 72 484.13=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(cess.) 72 472.13 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 12 Q
(Intr) 72 449.8 T
(oduction) 92.45 449.8 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 10 Q
1.78 (T) 72 432.13 P
1.78 (cl and Tk have grown rapidly in popularity and are) 77.41 432.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.31 (now being used as prototyping and development tools) 72 420.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.09 (by a lar) 72 408.13 P
0.09 (ge number of users. The earliest users of T) 101.98 408.13 P
0.09 (cl and) 272.75 408.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.1 (Tk were trained through manual pages, trial and error) 72 396.13 P
1.1 (,) 294.5 396.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.12 (and the generous support of other users. T) 72 384.13 P
-0.12 (oday) 238.75 384.13 P
-0.12 (, there are) 257.54 384.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.23 (many more users coming aboard and a much higher) 72 372.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.3 (demand for training. At the same time, T) 72 360.13 P
0.3 (cl and Tk have) 236.96 360.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.27 (become useful tools for teaching other subjects. T) 72 348.13 P
0.27 (cl and) 272.57 348.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Tk are making their mark in the classroom.) 72 336.13 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.21 (This panel brings together dif) 72 319.13 P
0.21 (ferent perspectives on T) 190.99 319.13 P
0.21 (cl,) 287.28 319.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.04 (Tk, and the classroom. Each one uses T) 72 307.13 P
0.04 (cl and Tk as part) 230.18 307.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.47 (of his research and development. And each one has) 72 295.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.82 (brought T) 72 283.13 P
0.82 (cl and Tk into the classroom. One has devel-) 111.84 283.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.1 (oped a pair of industrial short courses to teach program-) 72 271.13=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4.66 (ming in T) 72 259.13 P
4.66 (cl/Tk and [incr T) 120.07 259.13 P
4.66 (cl]. The others teach) 201.38 259.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.09 (university courses using T) 72 247.13 P
2.09 (cl, Tk, and STk as part of) 182.83 247.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.47 (teaching user interfaces, software engineering, and pro-) 72 235.13=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.27 (gramming. The panelists discuss their motivation for) 72 223.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.21 (using T) 72 211.13 P
2.21 (cl and Tk in the classroom, their educational) 103.79 211.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.74 (objectives and techniques, the challenges they faced) 72 199.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.56 (along the way) 72 187.13 P
1.56 (, and the lessons they have learned and) 130.57 187.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.79 (insights they have developed for teaching with T) 72 175.13 P
0.79 (cl and) 272.05 175.13 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Tk.) 72 163.13 T
0 0 0 1 0 0 0 K
72 96.4 297 111.4 C
0 0 0 1 0 0 0 K
81 109.4 225 109.4 2 L
0.5 H
2 Z
0 X
0 0 0 1 0 0 0 K
N
0 0 0 1 0 0 0 K
0 0 612 792 C
0 0 0 1 0 0 0 K
1 7.2 Q
0 X
0 0 0 1 0 0 0 K
(*) 72 91.6 T
1 9 Q
(Panel Contact: Department of Computer Science, University) 75.6 88 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(of Minnesota, Minneapolis, MN 55455) 72 77 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 12 Q
(Panelist Statements) 315 613 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2 10 Q
0.01 (Charles Crowley) 315 595.33 P
3 F
0.01 ( is an Associate Pr) 383.63 595.33 P
0.01 (ofessor of Computer) 458.31 595.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.87 (Science at the University of New Mexico. His teaching) 315 583.33 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.81 (inter) 315 571.33 P
1.81 (ests ar) 333.52 571.33 P
1.81 (e in human computer interfaces, pr) 361.35 571.33 P
1.81 (ogram-) 510.56 571.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.63 (ming languages, softwar) 315 559.33 P
2.63 (e engineering and operating) 418.51 559.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.21 (systems. He has used T) 315 547.33 P
0.21 (cl/Tk in thr) 407.4 547.33 P
0.21 (ee classes: user inter-) 451.9 547.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.1 (face design, softwar) 315 535.33 P
-0.1 (e engineering and Scheme pr) 394.7 535.33 P
-0.1 (ogram-) 510.56 535.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.52 (ming. His r) 315 523.33 P
2.52 (esear) 364.94 523.33 P
2.52 (ch inter) 386.23 523.33 P
2.52 (ests ar) 419.21 523.33 P
2.52 (e in human computer) 447.74 523.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.15 (interfaces and the use of models in design. He uses Tk in) 315 511=
=2E33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.46 (much of his r) 315 499.33 P
0.46 (esear) 368.52 499.33 P
0.46 (ch: a T) 389.81 499.33 P
0.46 (cl/Tk based multiple view edi-) 418.15 499.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.35 (tor) 315 487.33 P
2.35 (, a Tk r) 325.56 487.33 P
2.35 (ecor) 361.12 487.33 P
2.35 (d and r) 378.52 487.33 P
2.35 (eplay system and an interface) 411.74 487.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(builder for non-pr) 315 475.33 T
(ogrammers.) 387.41 475.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 F
(What should CS students learn?) 315 455.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.28 (Students need to acquire the skills necessary to produce) 315 437.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(software products.) 315 425.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(1.) 315 408.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Competence in a programming language, so they) 333 408.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(can produce programs.) 333 396.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(2.) 315 379.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Some competence in a second language with a dif-) 333 379.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.19 (ferent philosophy) 333 367.33 P
-0.19 (, so they will have exposure to dif-) 402.43 367.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(ferent approaches to problem solving.) 333 355.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(3.) 315 338.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Software engineering, so they understand the) 333 338.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(design process and the steps involved.) 333 326.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(4.) 315 309.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Design, so they can develop solutions.) 333 309.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(5.) 315 292.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Speci\336c skills and knowledge \050operating systems,) 333 292.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(database systems, AI, data structures, interpreters) 333 280.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(and compilers, human factors\051, so they can carry) 333 268.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(out the design steps) 333 256.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(6.) 315 239.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Theory) 333 239.33 T
(, so they can formally describe and analyze) 361.23 239.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(their designs.) 333 227.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 F
(Why do we have pr) 315 207.33 T
(ogramming in classes?) 397.6 207.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.08 (Programming supports several of the skills listed above.) 315 189.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.21 (Students need practice to become competent program-) 315 177.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.86 (mers. T) 315 165.33 P
2.86 (eaching software engineering and design are) 347.71 165.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.48 (more fun if students implement their designs. Program-) 315 153.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4.26 (ming helps students understand speci\336c skills and) 315 141.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(knowledge \050in operating systems, for example\051.) 315 129.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 F
(How do T) 315 109.33 T
(cl/Tk \336t into this?) 356.31 109.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.32 (T) 315 91.33 P
0.32 (cl is a good \322second\323 language because it has a dif) 320.41 9=
1.33 P
0.32 (fer-) 525.57 91.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.06 (ent philosophy than languages like FOR) 315 79.33 P
0.06 (TRAN, C, C++,) 475.54 79.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
72 630 540 720 R
7 X
V
0 14 Q
0 X
(T) 166.95 710.67 T
(cl and Tk in the Classr) 175 710.67 T
(oom: Lessons Learned) 310.49 710.67 T
1 12 Q
(Charles Crowley) 112.51 680.8 T
(Joseph A. Konstan) 258.44 680.8 T
1 9.6 Q
(*) 348.76 685.6 T
1 12 Q
(Michael J. McLennan) 415.51 680.8 T
3 F
(University of New Mexico) 90.51 666.8 T
(University of Minnesota) 248 666.8 T
(A) 409.21 666.8 T
(T&T Bell Laboratories) 416.1 666.8 T
(cr) 101.71 652.8 T
(owley@cs.unm.edu) 111.26 652.8 T
(konstan@cs.umn.edu) 254.82 652.8 T
(mmc@mhcnet.att.com) 414.49 652.8 T
0 0 0 1 0 0 0 K
FMENDPAGE
%%EndPage: "1" 1
%%Page: "2" 2
612 792 0 FMBEGINPAGE
[0 0 0 1 0 0 0]
[ 0 0 0 0 1 1 1]
[ 0 1 1 0 1 0 0]
[ 1 0 1 0 0 1 0]
[ 1 1 0 0 0 0 1]
[ 1 0 0 0 0 1 1]
[ 0 1 0 0 1 0 1]
[ 0 0 1 0 1 1 0]
 8 FrameSetSepColors
FrameNoSep
0 0 0 1 0 0 0 K
1 10 Q
0 X
0 0 0 1 0 0 0 K
-0.02 (etc. It is an example of a scripting language intended for) 72 713=
=2E33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.25 (high \322whipupitude\323 \050a term Larry W) 72 701.33 P
0.25 (all used to describe) 219.34 701.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.69 (Perl\051. It shows students a dif) 72 689.33 P
1.69 (ferent way of looking at) 194.15 689.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.38 (programming. It is a more dynamic language than most) 72 677.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2 (students have seen before. It is not really a dif) 72 665.33 P
2 (ferent) 273.68 665.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(\322paradigm\323 but it is very dif) 72 653.33 T
(ferent.) 184.86 653.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.18 (T) 72 636.33 P
-0.18 (cl/Tk together are a fast way to prototype GUIs. This is) 77.41 63=
6.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
3.24 (important because the ability to produce prototypes) 72 624.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.2 (quickly is fun and gives the students a feeling of power) 72 612.33 =
P
0.2 (.) 294.5 612.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.77 (It makes them like computing and draws them into the) 72 600.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.42 (\336eld. Once they see the great things that can be done) 72 588.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.17 (with computers they will be able to see why you may) 72 576.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.56 (want to go to more trouble to get it really right and see) 72 564.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(the point in the discipline of SE.) 72 552.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.12 (Prototyping is also important in learning the importance) 72 535.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.54 (of speci\336cation and design. Using T) 72 523.33 P
0.54 (cl/Tk means that in) 218.44 523.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.95 (a few days you have a working version and you are) 72 511.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.38 (forced to face up the real problem, \322what, exactly) 72 499.33 P
1.38 (, do) 280.62 499.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.76 (you want this program to do?\323 By not bogging them) 72 487.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 (down in programming details, we allow them to see that) 72 475.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.03 (creating a software product is a hard job even if the pro-) 72 463.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.62 (gramming part is easy) 72 451.33 P
2.62 (. It teaches the importance of) 167.81 451.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.31 (design and speci\336cation of the human computer inter-) 72 439.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(face.) 72 427.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.8 (Because T) 72 410.33 P
2.8 (cl lets you get into trouble with program) 116.03 410.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.26 (design, students learn the importance of planning \050or) 72 398.33=
 P
0.26 (, at) 284.52 398.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.24 (least, reor) 72 386.33 P
0.24 (ganizing so it looks like you planned it\051. Hav-) 111.49 386.33 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.64 (ing only two name spaces shows the impact of name) 72 374.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.35 (space pollution. Y) 72 362.33 P
2.35 (ou see the need for modules. And) 148.75 362.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(there is a place to go, itcl, for relief.) 72 350.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.01 (T) 72 333.33 P
-0.01 (cl/Tk shows the value of prototyping because it is such) 77.41 333=
=2E33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.04 (a good prototyping language. Students can see the value) 72 321.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.28 (of iterating over a number of versions of a program and) 72 309.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(see it improve.) 72 297.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 F
(How do we use T) 72 277.33 T
(cl/Tk at the University of New Mex-) 143.86 277.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(ico?) 72 265.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
(1.) 72 247.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(W) 90 247.33 T
(e have used in our software engineering class so) 98.64 247.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(the students can learn about prototyping and to) 90 235.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(reduce the in\337uence of programming in the class.) 90 223.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(2.) 72 206.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(W) 90 206.33 T
(e have used it in our user interface class to allow) 98.64 206.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(students to prototype and experiment with inter-) 90 194.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(faces.) 90 182.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(3.) 72 165.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(W) 90 165.33 T
(e have used STk \050Scheme/Tk\051 in our second) 98.64 165.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(course on programming that covers Scheme. W) 90 153.33 T
(e) 279.44 153.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(used Scheme because we thought it is a better lan-) 90 141.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(guage to learn, especially this early) 90 129.33 T
(, that T) 230.43 129.33 T
(cl.) 258.34 129.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 F
(What have we learned fr) 72 109.33 T
(om this?) 177.36 109.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.68 (Generally T) 72 91.33 P
0.68 (cl and Tk have worked. They motivate stu-) 120.02 91.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 (dents, allow prototyping and are relatively easy to learn.) 72 79.33 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.55 (T) 315 713.33 P
0.55 (cl and Tk have showed us that GUI programming can) 320.41 713.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.01 (be fast and easy) 315 701.33 P
0.01 (. Such a language has many bene\336ts in a) 377.98 701.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(CS curriculum.) 315 689.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(\245) 315 672.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(Learning a prototyping language.) 333 672.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(\245) 315 657.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.17 (Allowing students to \322whip up\323 programs and have) 333 657.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(fun.) 333 645.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(\245) 315 630.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.16 (Reducing programming time so we can concentrate) 333 630.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.3 (on the topic we are actually learning about: AI,) 333 618.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
3.01 (algorithms, design, speci\336cation, SE, user inter-) 333 606.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(faces, etc.) 333 594.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.46 (Despite the ease of T) 315 577.33 P
0.46 (cl and Tk, it takes a while to learn) 400.54 577.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.37 (it, at least a week for a basic competence. Even then,) 315 565.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.06 (students are not con\336dent in the language. This takes up) 315 55=
3.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.42 (class time that could be used for other things. This is) 315 541.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.99 (even true in the Scheme class since it takes a while to) 315 529.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(learn to use the Tk widgets \050even without learning T) 315 517.33 T
(cl\051.) 523.99 517.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.83 (I think that all students should learn a prototyping lan-) 315 500.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.25 (guage early on, maybe in the \336rst programming class.) 315 488.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.1 (This should include Tk which is a nice widget set. If we) 315 476.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.68 (could count on people already knowing T) 315 464.33 P
0.68 (cl/Tk or STk,) 485.03 464.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.74 (then we could use it in more classes where we want to) 315 452.33 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.19 (do some programming but we don\325) 315 440.33 P
0.19 (t want it to take a lot) 456.61 440.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
3.39 (of class \050or home work\051 time. W) 315 428.33 P
3.39 (e gain time, \336rst) 464.82 428.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4.49 (because the students already know it and second) 315 416.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(because T) 315 404.33 T
(cl/Tk is very fast to program in.) 354.56 404.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2 F
2.65 (Joseph A. Konstan) 315 372.33 P
3 F
2.65 ( is an Assistant Pr) 397.81 372.33 P
2.65 (ofessor in the) 480.82 372.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.71 (Department of Computer Science at the University of) 315 360.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.41 (Minnesota. His r) 315 348.33 P
2.41 (esear) 386.95 348.33 P
2.41 (ch and teaching focus on user) 408.24 348.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.33 (interfaces, speci\336cally for distributed multimedia appli-) 315 3=
36.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.04 (cations. He uses Tk in the classr) 315 324.33 P
0.04 (oom as the implementa-) 444.05 324.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
5.25 (tion language for user interface pr) 315 312.33 P
5.25 (ojects and is) 479.5 312.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.69 (intr) 315 300.33 P
0.69 (oducing a new course on UI toolkits that will teach) 329.08 300.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
3.61 (students to write widgets, geometry managers, and) 315 288.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.35 (other toolkit extensions. T) 315 276.33 P
1.35 (cl and Tk also \336gur) 422.03 276.33 P
1.35 (e pr) 504.86 276.33 P
1.35 (omi-) 521.67 276.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.63 (nently in his r) 315 264.33 P
0.63 (esear) 371.79 264.33 P
0.63 (ch including T) 393.08 264.33 P
0.63 (cl str) 451.2 264.33 P
0.63 (eams as a media) 471.74 264.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.53 (type in multimedia, and data pr) 315 252.33 P
0.53 (opagation \050formulas\051 in) 443.38 252.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(T) 315 240.33 T
(cl.) 319.64 240.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.23 (T) 315 223.33 P
0.23 (eaching a user interface design, development, and sys-) 320.41 223.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.17 (tems course at Minnesota has many challenges. The big-) 315 211.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.18 (gest of these are that we have only a 10-week quarter in) 315 199.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.26 (which to teach, and that students generally have not) 315 187.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.2 (been exposed to any toolkits or frameworks for interface) 315 175.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.75 (development. Fortunately) 315 163.33 P
0.75 (, students are generally famil-) 417.87 163.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.17 (iar with programming \050in both C++ and scheme\051 and the) 315 1=
51.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.76 (Unix environment, so it seemed that T) 315 139.33 P
0.76 (cl and Tk would) 472.45 139.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(not be too lar) 315 127.33 T
(ge a stretch for them to learn.) 367.87 127.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.92 (This winter alone, 35 groups of two to four students) 315 110.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.7 (completed projects using T) 315 98.33 P
1.7 (cl and Tk. Along the way) 428 98.33 P
1.7 (,) 537.5 98.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.24 (each group learned enough about T) 315 86.33 P
0.24 (cl and Tk to meet its) 456.86 86.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
FMENDPAGE
%%EndPage: "2" 2
%%Page: "3" 3
612 792 0 FMBEGINPAGE
[0 0 0 1 0 0 0]
[ 0 0 0 0 1 1 1]
[ 0 1 1 0 1 0 0]
[ 1 0 1 0 0 1 0]
[ 1 1 0 0 0 0 1]
[ 1 0 0 0 0 1 1]
[ 0 1 0 0 1 0 1]
[ 0 0 1 0 1 1 0]
 8 FrameSetSepColors
FrameNoSep
0 0 0 1 0 0 0 K
1 10 Q
0 X
0 0 0 1 0 0 0 K
-0.18 (project needs, and many of the groups learned enough to) 72 713.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.13 (use Tk in future course and research projects. In the pro-) 72 701=
=2E33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.02 (cess, I have gained experience in teaching about T) 72 689.33 P
0.02 (cl and) 272.82 689.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.14 (Tk and in supporting students who are learning to use) 72 677.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(them.) 72 665.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.07 (Like most programming, Tk is best learned by example.) 72 648.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.34 (I have found that it is possible to teach the fundamental) 72 636.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.63 (concepts of both the T) 72 624.33 P
0.63 (cl language and the Tk toolkit in) 163.24 624.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.36 (75 minutes by making available a lar) 72 612.33 P
0.36 (ge set of examples) 221.76 612.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.07 (for independent study) 72 600.33 P
0.07 (. Fortunately) 158.7 600.33 P
0.07 (, the FTP archives are) 209.23 600.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.3 (replete with examples. The greatest dif) 72 588.33 P
2.3 (\336cult students) 238.86 588.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.09 (have is identifying appropriate examples, and therein) 72 576.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.92 (lies the real value of an experienced instructor and) 72 564.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(knowledgeable teaching assistants.) 72 552.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.49 (Furthermore, the parts of T) 72 535.33 P
0.49 (cl and Tk that really need to) 181.85 535.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.62 (be taught are very small. The critical concepts that stu-) 72 523.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.47 (dents need assistance with are the basics of UI toolkits:) 72 511.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.86 (the notion of event-driven programming and callback) 72 499.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.61 (functions, and the ideas of a window hierarchy and) 72 487.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.09 (geometry management. Beyond these concepts, the only) 72 475.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.04 (\322teaching\323 needed was to review basic T) 72 463.33 P
-0.04 (cl syntax \050quot-) 234.32 463.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.26 (ing and substitution rules\051, variable scoping, and the) 72 451.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.88 (general T) 72 439.33 P
1.88 (cl/Tk architectural model \050though almost no) 111.22 439.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(students linked in C code within their applications\051.) 72 427.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.86 (The results have been very pleasing. In two years of) 72 410.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.81 (of) 72 398.33 P
2.81 (fering this course, students have produced a wide) 80.15 398.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.32 (range of interesting applications. Several of these are in) 72 386.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.39 (actual day-to-day use \050e.g., a tutorial introduction to) 72 374.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.11 (using X W) 72 362.33 P
2.11 (indows, a mailing list sign-up tool, and a) 119.15 362.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.24 (structured editor and interface to the POV) 72 350.33 P
1.24 (ray graphics) 246.61 350.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.91 (package\051. Students have reported back that they con-) 72 338.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.13 (tinue to use the tools after the class, and several research) 72 3=
26.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.47 (projects now use Tk \050or STk\051 as their primary interface) 72 3=
14.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(development tool.) 72 302.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2 F
-0.21 (Michael J. McLennan) 72 270.33 P
3 F
-0.21 ( is a Member of T) 163.54 270.33 P
-0.21 (echnical Staff at) 232.41 270.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.15 (A) 72 258.33 P
1.15 (T&T Bell Laboratories in Allentown, P) 77.74 258.33 P
1.15 (A. He became) 238.89 258.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.19 (involved with T) 72 246.33 P
1.19 (cl/Tk thr) 135.13 246.33 P
1.19 (ee years ago, while using it to) 170.13 246.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.78 (develop CAD tools for semiconductor pr) 72 234.33 P
1.78 (ocess, device) 243.02 234.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.75 (and cir) 72 222.33 P
2.75 (cuit simulation. Since then he has developed) 102.99 222.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.16 (numer) 72 210.33 P
2.16 (ous extensions, including [incr T) 97.18 210.33 P
2.16 (cl], an object-) 236.3 210.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.38 (oriented extension of the T) 72 198.33 P
0.38 (cl language, and [incr Tk], a) 179.26 198.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
3.28 (facility for building compound \322mega-widgets\323. Dr) 72 186.33 =
P
3.28 (.) 294.5 186.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
6.91 (McLennan has also developed two intr) 72 174.33 P
6.91 (oductory) 261.45 174.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4.62 (courses: \322Building Applications with T) 72 162.33 P
4.62 (cl/Tk\323, and) 246.82 162.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(\322Object-Oriented Pr) 72 150.33 T
(ogramming with [incr T) 155.23 150.33 T
(cl]\323.) 250.71 150.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.06 (I think John Ousterhout\325) 72 133.33 P
0.06 (s book \322T) 169.69 133.33 P
0.06 (cl and the Tk T) 208.56 133.33 P
0.06 (oolkit\323) 269.22 133.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.65 (provides an excellent overview of T) 72 121.33 P
2.65 (cl/Tk. Only one) 228.66 121.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.83 (other source of information is better: the T) 72 109.33 P
1.83 (cl/Tk man) 254.34 109.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.18 (pages. So when I set out to develop my introductory T) 72 97.33 P
-0.18 (cl/) 287 97.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.41 (Tk course, I wanted to include things that were not) 72 85.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.27 (already documented. In my course, I try to show how) 315 713.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.55 (the T) 315 701.33 P
1.55 (cl language stitches the Tk widgets into a lar) 336.68 701.33 P
1.55 (ger) 527.23 701.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.7 (application. Instead of showing each widget by itself, I) 315 689.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.59 (try to show small combinations of widgets, to illustrate) 315 677.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.58 (the interplay between widgets that occurs in normal) 315 665.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.25 (applications. I also try to explain some of the philosophy) 315 65=
3.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.97 (behind the building of applications: Start small and let) 315 641.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.84 (the application grow; avoid hard-wired values for wid-) 315 629.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(get options; use the canvas to create intuitive controls.) 315 617.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.02 (Above all, I try to make the course fun. After all, T) 315 600.33 P=

0.02 (cl/Tk) 518.89 600.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.63 (development is fun, once you get the hang of it. So we) 315 588.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.22 (build a simple \322trek\323 video game. W) 315 576.33 P
-0.22 (e build dialog boxes) 459.82 576.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.03 (to specify landing parties and kill of) 315 564.33 P
2.03 (f the \322red shirt\323) 471.43 564.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.46 (crewmen. Along the way) 315 552.33 P
1.46 (, we \336nd out how to use the) 419.25 552.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.71 (canvas, how to manage grabs, and how to build hyper-) 315 540.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(tools that communicate with one another via \322send\323.) 315 528.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.9 (I encourage students to type along as I speak--to bring) 315 511.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.21 (up a \322wish\323 and test out simple examples as I go along. I) 3=
15 499.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.75 (have found that this gives them hands-on experience,) 315 487.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.13 (while letting the course move forward at a steady pace. I) 315 475=
=2E33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.32 (also give them one or two lab exercises every day) 315 463.33 P
0.32 (, each) 516.36 463.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.98 (lasting about an hour) 315 451.33 P
1.98 (. This gives them time to make) 404.54 451.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.64 (their own mistakes, and to learn how to \336nd them with) 315 439.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(things like \322puts $errorInfo\323.) 315 427.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.93 (New students have the most dif) 315 410.33 P
0.93 (\336culty in T) 445.31 410.33 P
0.93 (cl/Tk by far) 490.92 410.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.36 (with the quoting rules. The rules themselves are quite) 315 398.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.11 (simple; I can explain them on just a few slides. But it) 315 386.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.59 (usually takes the entire course \050and then some!\051 for the) 315=
 374.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.09 (students to understand how to apply them in various sit-) 315 362.3=
3 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.8 (uations. Another source of confusion is the subtle shift) 315 350.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
2.79 (between the pass-by-name semantic that some com-) 315 338.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(mands have, e.g.,) 315 326.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4 F
(    lappend names Fred Joe Larry) 324 309.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
1.63 (and the pass-by-value semantic that many other com-) 315 292.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(mands have, e.g.,) 315 280.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
4 F
(    lrange $names 2 11) 324 263.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1 F
0.36 (Finally) 315 246.33 P
0.36 (, when it comes to mixing C and T) 342.69 246.33 P
0.36 (cl code, many) 483.46 246.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.15 (students have dif) 315 234.33 P
0.15 (\336culty with the notion of programming) 382.88 234.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.06 (on two levels. It is hard for them to visualize how the) 315 222.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
1.3 (control \337ow passes between the two worlds. It is also) 315 210.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.28 (hard for them to appreciate what should be written as C) 315 198.33=
 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(code, and what should be done with T) 315 186.33 T
(cl.) 466.5 186.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.14 (Since we started of) 315 169.33 P
-0.14 (fering these courses, the response has) 390.76 169.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
-0.21 (been phenomenal. Developers with diverse backgrounds) 315 157.33 P=

0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.98 (and even more diverse applications are learning to use) 315 145.33 =
P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.28 (this powerful tool. W) 315 133.33 P
0.28 (e have seen an exponential growth) 400.59 133.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.62 (in the requests for training. I think this speaks volumes) 315 121.=
33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0.21 (for the utility of T) 315 109.33 P
0.21 (cl/Tk, and for its continued success in) 387.36 109.33 P
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
(the future.) 315 97.33 T
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
0 0 0 1 0 0 0 K
FMENDPAGE
%%EndPage: "3" 3
%%Trailer
%%BoundingBox: 0 0 612 792
%%PageOrder: Ascend
%%Pages: 3
%%DocumentFonts: Times-Bold
%%+ Times-Roman
%%+ Times-BoldItalic
%%+ Times-Italic
%%+ Courier
%%EOF

--===_0_Mon_May_22_17:19:29_CDT_1995--


