################################################ # # # ## ## ###### ####### ## ## ## ## ## # # ## ## ## ## ## ### ## ## ## ## # # ## ## ## ## #### ## ## ## ## # # ## ## ###### ###### ## ## ## ## ### # # ## ## ## ## ## #### ## ## ## # # ## ## ## ## ## ## ### ## ## ## # # ####### ###### ####### ## ## ## ## ## # # # ################################################ 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: https://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 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 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 FMFILL 11 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 FMFILL 25 FMFILL 26 <3333333333333333> FMFILL 27 <0000ffff0000ffff> FMFILL 28 <7ebddbe7e7dbbd7e> FMFILL 29 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--