Multi-paradigm programming

Hypermedia programming is by its nature complex, partly because of the variety of content that must be dealt with, and partly because the graphical user interface forms an essential part of the hypermedia application. In this section, we will look at an approach to graphical interface programming that may best be characterized as multi-paradigm programming. Our approach, embodied in the hush library (Eliëns, 1994), combines the use of C++ and the script language Tcl. The advantage of using C++ is its robustness. The advantage of using scripts is, clearly, flexibility. A multi-paradigm approach offers the best of both worlds. Or the worst, for that matter. The hush library offers an interface to the Tcl/Tk (window programming) toolkit, and a number of multimedia devices, including real-time synthesized audio and MPEG (Motion Pictures Experts Group) software video. The principal contribution of the approach embodied in hush is that it offers a type-secure solution for connecting script code with C++ (and vice versa). In particular, it allows one to associate events with actions by means of handler objects. In addition, the hush library allows the programmer to employ inheritance for the development of possibly compound widgets. Hush stands for hyper utility shell. The standard interpreter associated with the hush library is a shell, called hush, including a number of the available extensions of Tcl/Tk and widgets developed by ourselves. \nop{ (such as a filechooser and an MPEG video widget). } The hush library offers a C++ interface to the Tcl/Tk toolkit and its extensions. Moreover, a program created with hush is itself an interpreter extending the hush interpreter. \label{Tcl/Tk}

Tcl/Tk

The language Tcl was first presented in  [Ousterhout90]. Tcl was announced as a flexible cshell-like language, intended to be used for developing an X11-based toolkit. A year later, the Tk toolkit (based on Tcl) was presented in  [Ousterhout91]. From the start Tcl/Tk has received a lot of attention, since it provides a flexible and convenient way in which to develop quite powerful window applications. The Tcl language offers variables, assignment and a procedure construct. Also it provides a number of control constructs, facilities for manipulating strings and built-in primitives giving access to the underlying operating system. The basic Tcl language may easily be extended by associating a function written in C with a command name. Arguments given to the command are passed as strings to the function defining the command. The Tk toolkit is an extension of Tcl with commands to create and configure widgets for displaying text and graphics, and providing facilities for window management. The Tk toolkit, and the wish interpreter based on Tk, provides a convenient way to program X-window based applications.

Example

\zline{\fboxwish} \hspace*{2.5cm} \epsfbox{hello.eps}

Scripts

-- hello world
  button .b -text "Hello, world" -command {
  	puts stdout "hello world"
  	}
  
  pack .b 
  

slide: A Wish example

The wish program is an interpreter for executing Tcl/Tk scripts. As an example of a wish script, look at the hello world program in slide tcl-example. The hello world script defines a button that displays Hello, world, and prints hello world to standard output when it is activated by pressing the left mouse button. The language used to write this script is simply Tcl with the commands defined by Tk, such as the button command (needed to create a button) and the pack command (that is used to map the button to the screen). The wish program actually provides an example of a simple application based on Tcl/Tk. It may easily be extended to include, for example, 3D-graphics by linking the appropriate C libraries and defining the functions making this functionality available as (new) Tcl commands. To define Tcl commands in C, the programmer has to define a command function and declare the function to be a command in Tcl by invoking the {\em Tcl_CreateCommand} function. Creating a command is done with reference to an interpreter, which accounts for the first argument of {\em Tcl_CreateCommand}. The name of the command, as may be used in a Tcl script must be given as a second argument, and the C/C++ function defining the command as a third argument. Finally, when declaring a command, the address of a structure containing client data may be stored, which may be (the address of) the root window, for example. When the function is invoked as the result of executing the Tcl command, the client data stored at declaration time is passed as the first argument to the function. Since the type ClientData is actually defined to be void*, the function must first cast the client data argument to an appropriate type. Evidently, casting is error-prone. Another problem with command functions as used in the Tcl C API is that permanent data are possible only in the form of client data, global variables or static local variables. Both client data and global variables are unsafe by being too visible and static local data are simply inelegant.

Program structure

\nop{ The hush library is intended to provide a convenient way to program window-based applications in C++. As a first example of using hush, we will look at a simple program written in C++ that uses a graphical interface defined by a Tcl/Tk script. } \c{ The hush library is intended to provide a convenient way to program window-based applications in C++. Basically, there are two considerations that may lead you to employ the hush library. When you are familiar with Tcl/Tk and you need to combine Tcl scripts with C++ code, you may use handler classes to do so in a relatively type-secure way. On the other hand, when you want to program graphical user interfaces in C++, you may employ the hush widget classes. In the latter case, you may choose to remain ignorant of the underlying Tcl/Tk implementation or exploit the Tcl script facility to the extent you wish. \nop{ Whether you choose to use Tcl scripts or not, the structure of the program is to a large extent the same. } } \nop{ As an illustration of the structure of a program using hush, we will look at a simple program written in C++ that uses a graphical interface defined by a Tcl/Tk script. After discussing the example, we will look at a brief overview of the classes that constitute the hush library. A more detailed description will be given of the kit class, that encapsulates the embedded Tcl interpreter, and the session class, that shields of the details of the window environment. } \c{ \nop{ The example given in the previous section showed what kind of components are typically used when developing a program with the hush library. However, instead of employing a Tcl script, the window interface may also be developed entirely by employing hush C++ widgets. } In this section, a brief overview will be given of the classes offered by the hush library. Further, it will be shown how to construct the hush interpreter referred to in the introduction. In addition, we will take a closer look at the classes kit and session, which are needed to communicate with the embedded Tcl interpreter and to initialize the main event loop. } \c{

The hush class library

The hush C++ library consists of three kinds of classes, namely (a) the widget classes, which mimic the functionality of Tk, (b) the handler classes, which are involved in the handling of events and the binding of C++ code to Tcl commands, and (c) the classes kit and session, which encapsulate the embedded interpreter and the window management system, } .so f_ov \c{ In the widget class hierarchy depicted in slide hush-overview (a), the widget class represents an abstract widget, defining the commands that are valid for each of the descendant concrete widget classes. The widget class, however, is not an abstract class in C++ terms. It may be used for creating pointers to widgets defined in Tcl. In contrast, employing the constructor of one of the concrete widget classes results in actually creating a widget. A more detailed example showing the functionality offered by the widget classes will be given in section user. A description of the individual widget classes is included in appendix hush-man. } \c{ The class hierarchy depicted in slide hush-overview (b) depicts the handler class as a subclass of client. The reason for this will become clear in section Binding. The handler class may also be considered an abstract class, in the sense that it is intended to be used as the ancestor of a user-defined handler class. The handler class has two pre-defined descendant classes, namely the widget class and the item class. This implies, indeed, that both the widget and the item classes (the latter of which is discussed in section hush-item) may also be used as ancestor handler classes. The reason for this is that any descendant of a widget or item class may declare itself to be its own handler and define the actions that are invoked in response to particular events. This will be illustrated and discussed in sections Binding and user. } \c{

The hush interpreter

In the introduction, hush was announced as both a C++ library and as an interpreter extending the wish interpreter. The program shown in slide hush-interpreter substantiates this claim, albeit in a perhaps disappointingly simple way. } \slide{hush-interpreter}{The hush interpreter}{ .ds lib/sli-hush.c }{ .ds lib/hush.c } } \c{ The structure of the program is characteristic for hush-based applications. \nop{ similar to the C++ example of section hush-example. } Part [1] consists merely of including the {\tt hush.h} header file and may possibly define additional functionality. \nop{ Part [2] is empty. } Part [2] consists of an application class, derived from session, defining how the hush interpreter deals with command-line arguments (a and d) and the initialization that takes place when the main event loop is started (c). To understand (a) and (d) it is enough to know that the hush library provides a hypertext widget and that the -x option treats the next argument as the name of a hypertext file. In section hyper, an example will be given that involves the hypertext widget. In (b) the {\tt hush.tcl} file is declared to be the initialization file. It contains the Tcl code for installing the extensions loaded in (c). In (e), a predefined button {\tt .quit} is packed to the root widget. Part [3] is needed to initialize the application and start the main event loop. } \c{ The hush interpreter defined by the program extends the wish interpreter by loading some extensions to Tcl \nop{discussed in the introduction} and by allowing for the display of a hypertext file. The interpreter accepts any command-line argument accepted by the wish interpreter, in addition to the -x hypertext option. } \nop{ The Tcl interface script given in slide int-script, for example, may be executed using the hush interpreter. } \c{

The {\em kit} class

Hush is meant to provide a parsimonious C++ interface to Tcl/Tk. Nevertheless, as with many a toolkit, some kind of API shock seems to be unavoidable. This is especially true for the widget class (covered in section hush-widget) and the class kit defining the C++ interface with the embedded Tcl interpreter. The functionality of kit can only be completely understood after reading this section. However, since an instance of kit is used in almost any other object (class), it is presented here first. See slide class-kit. The reader will undoubtly gradually learn the functionality of kit by studying the examples. } \c{ To understand why a kit class is needed, recall that each hush program contains an embedded Tcl interpreter. The kit class encapsulates this interpreter and provides a collection of member functions to interact with the embedded interpreter. } \nop{ The kit class is derived from the class hcl which provides access to the lower level details of (an extended version of) Tcl. \nop{Consult the reference manual.} } \c{ The first group of functions (eval, result, evaluate and source) may be used to execute commands in the Tcl scripting language directly. A Tcl command is simply a string conforming to certain syntactic requirements. The function eval evaluates a Tcl command. The function result() may be used to fetch the result of the last Tcl command. In contrast, the function result(char*) may be used to set the result of a Tcl command, when this command is defined in C++ (as may be done with kit::action). The function evaluate provides a shorthand for combining eval and result(). The function source may be used to read in a file containing a Tcl script. } \c{ Also, we have the kit::action function that may be used to associate a Tcl command with a handler object. In section action, alternative ways of defining an action are discussed. } \nop{ The function after may be used to have a Tcl command evaluated after n milliseconds. (It makes use of X timer callbacks). The function send may be used to evaluate a command in a different application. An application may be given a name when creating the session object. The function selection delivers the current X selection. The function update may be used to process any pending events. These functions are only for experienced users. See section tcl. } \c{ The next group of functions is related to widgets and events that may occur to widgets. The event function delivers the latest event. It may only be used in a command that is bound to some particular event. See section hush-events. When other events occur before accessing the event object, the information it contains may be obsolete. } \slide{class-kit}{The kit class}{
  interface kit { 
\fbox{kit}
int eval(char* cmd); char* result(); void result(char* s); char* evaluate(char* cmd); int source(char* f); action& action(char* name, handler* h); class event event(); widget* root(); widget* pack(widget* w, char* opts = ""); widget* pack(char* wp, char* opts = ""; char* selection(char* opts=""); void after(int msecs, char* cmd); void after(int n, handler* h); void update(char* opts=""); char* send(char* it, char* cmd); void trace(int level = 1); void notrace(); void quit(); };
}{
  interface kit { 
\fbox{kit}
int eval(char* cmd);
to evaluate script command

char* result();
to fetch the result of eval

void result(char* s);
to set the result of eval

char* evaluate(char* cmd)
combines eval and result

int source(char* f);
to load a script from file

action& action(char* name, handler* h); class event event();
returns the last event

widget* root();
returns toplevel (root) widget

widget* pack(widget* w, char* options = ""); widget* pack(char* wp, char* options = ""; char* selection(char* options="");
X environment

void after(int msecs, char* cmd); void after(int n, handler* h); void update(char* options=""); char* send(char* it, char* cmd); void trace(int level = 1); void notrace(); void quit()
to terminate the session

};
} } \c{ The function root gives access to the toplevel root widget associated with that particular instance of the kit. The function pack may be used to append widgets to the root widget, in order to map them to the screen. Widgets may be identified either by a pointer to a widget object or by their path name, which is a string. See section widget. } \c{ Next, we have a group of functions related to the X environment. The function selection delivers the current X selection. The function after may be used to set a timer callback for a handler. Setting a timer callback means that the handler object will be invoked after the number of milliseconds given as the first argument to after. The function update may be used to ensure that all pending events are processed. For example, when moving items on a canvas, an update may be needed to make the changes visible. Also, we have a send function that may be used to communicate with other Tcl/Tk applications. The first argument of send must be the name of an application, which may be set when creating a session. } \nop{ The function action may be used to associate a Tcl command to a command function or handler written in C++. See section action for details. } \nop{ A kit object, which may be only created by session(), provides an interface to the tcl interpreter and the global properties of the system. The function eval() may be used to execute commands in tcl and to retrieve the result. The function event delivers the latest event. When other events occur before accessing a particular event object, the information gotten will be obsolete. } \c{ Further, we have the functions trace() and notrace(), which may be used to turn on, respectively off, tracing. The level indicates at what detail information will be given. Trace level zero is equivalent to notrace(). Finally, the function quit may be used to terminate the main event loop. } \c{

The {\em session} class

Each program written with hush may contain only one embedded interpreter. To initialize the kit object wrapping the interpreter and to start the main event loop, an instance of the class session must be created. See slide class-session. The preferred way of doing this is by defining a descendant class of the session class, redefining the virtual function session::main to specify what needs to be done before starting the main loop. In addition, the constructor of the newly defined class may be used to check command line arguments and to initialize application specific data, as illustrated in slide hush-interpreter. } \slide{class-session}{The session class}{
  interface session  { 
\fbox{session}
session(int argc, char** argv, char* name = 0); void init(char* fname); virtual void prelude(kit* tk, int argc, char* argv[] ); virtual void main(kit* tk, int argc, char* argv[] ); int run( ); };
} \c{ When creating a session object, the name of the application may be given as the last parameter. Under this name, the application is known to other Tk applications, which may communicate with each other by means of the send command. The function init may be used to specify a different initialization script. This script must include the default hush initialization script, which is an adapted version of the original wish initialization script. } \c{ Apart from the function main, a function prelude may also be defined. When the program is used as an interpreter (by giving {\tt -f file} as command line arguments) only the prelude function will be executed, otherwise prelude will be executed before main. In interpreter mode, the main function will also be executed when the script contains the command go-back. Finally, the function run must be called to actually initialize the program and start the main loop. }

Binding actions to events

Handler objects may be bound to Tcl commands or events. Events are generated by the (X) window environment in response to actions of the user. These actions include pressing a mouse button, releasing a mouse button, moving the mouse, etc. Instead of explicitly dealing with all incoming events, applications delegate control to the environment by associating a callback function with each event that is relevant for a particular widget. This mechanism frees the programmer from the responsibility of deciding to which widget the event belongs and what action to take. Nevertheless, from the perspective of program design, the proper organization of the callback functions is not a trivial matter. Common practice is to write only a limited number of callback functions and perform explicit dispatching according to the type of event. An object-oriented approach may be advantageous as a means to organize a collection of callback functions as member functions of a single class. One way of doing this is to define an abstract event handler class which provides a virtual member function for each of the most commonly occurring events. In effect, such a handler class hides the dispatching according to the type of the event. A concrete handler class may then be defined simply by overriding the member functions corresponding to the events of interest. Before studying the abstract handler class in more detail, we will briefly look at the definition of the event class.

Events

Events always belong to a particular widget. To which widget events are actually directed depends upon whether the programmer has defined a binding for the event type. When such a binding exists for a widget and the (toolkit) environment decides that the event belongs to that widget, then the callback associated with that event is executed. Information concerning the event may be retrieved by asking the kit for the latest event. \slide{class-event}{The event class}{
  interface event  {  
\fbox{event}
int type();
\c{ X event type }
char* name();
\c{type as string}
int x(); int y(); ... void* rawevent();
\c{delivers} raw X event

};
}{
  interface event  {  
\fbox{event}
int type();
\c{// X event type }
char* name();
\c{// type as string}
int x(); int y(); int button(int i = 0);
\c{// ButtonPress}
int buttonup(int i = 0);
\c{// ButtonRelease}
int motion();
\c{// MotionNotify}
int keyevent();
\c{// KeyPress or KeyRelease}
int buttonevent(int i = 0);
\c{// ButtonPress or Release}
int keycode(); void trace();
\c{// prints event information}
void* rawevent();
// \c{delivers} raw X event
};
} } Event objects represent the events generated by the X-window system. Each event has a type. The type of the event can be inspected with type() which returns an integer value or name() which returns a string representation of the type. For some of the common event types, such as ButtonPress, ButtonRelease and MotionNotify, member functions are provided to facilitate testing. If an integer argument (1, 2 or 3) is given to button, buttonup or buttonevent, it is checked whether the event has occurred for the corresponding button. The functions x and y deliver the widget coordinates of the event, if appropriate. Calling trace for the event results in printing the type and coordinate information for the event. When setting the kit::trace level to 2 this information is automatically printed. Programmers not satisfied with this interface can check the type and access the underlying XEvent at their own risk. \c{

Handlers

Handler objects provide a type secure way in which to deal with local data and global resources needed when responding to an event. A handler object may store the information it needs in instance variables, when its constructor is called. See slide class-handler. } \slide{class-handler}{The handler class}{
  interface handler : client { 
\c{\fbox{handler}}
virtual int dispatch(kit* _tk, int _argc, char* _argv[]); virtual int operator()(); virtual void press( event& ) { } virtual void release( event& ) { } virtual void keypress( event& ) { } virtual void keyrelease( event& ) { } virtual void motion( event& ) { } virtual void enter( event& ) { } virtual void leave( event& ) { } virtual void other( event& ) { } protected: int argc; char** argv; kit* tk; };
} \c{ Activating a handler object in response to an event or a Tcl command occurs by calling the dispatch function of the handler. The system takes care of this, provided that the user has bound the handler object to a Tcl command or event. } \slide{dispatching}{The dispatch and operator() function}{
  int handler::dispatch(kit* _tk, int _argc, char* _argv[]) {
  tk = _tk; argc = _argc; argv = _argv;
  return this->operator()();
  }
  
  int handler::operator()() { 
\c{\fbox{operator()}}
event e = tk->event(); if (e.type()==ButtonPress) press(e); else if (e.type()==ButtonRelease) release(e); else if (e.type()==KeyPress) keypress(e); else if (e.type()==KeyRelease) keyrelease(e); else if (e.type()==MotionNotify) motion(e); else if (e.type()==EnterNotify) enter(e); else if (e.type()==LeaveNotify) leave(e); else other(e); return OK; }
}{
  int handler::dispatch(kit* _tk, int _argc, char* _argv[]) {
  	tk = _tk; argc = _argc; argv = _argv;
  	return this->operator()();
  }
  
  int handler::operator()() { 
\fbox{operator()}
event e = tk->event(); if ( e.type() == ButtonPress ) press(e); else if ( e.type() == ButtonRelease ) release(e); else if ( e.type() == KeyPress ) keypress(e); else if ( e.type() == KeyRelease ) keyrelease(e); else if ( e.type() == MotionNotify ) motion(e); else if ( e.type() == EnterNotify ) enter(e); else if ( e.type() == LeaveNotify ) leave(e); else other(e); return OK; }
} } \c{ The dispatch function, in its turn, calls the operator() function, after storing the kit, argc and argv arguments in the corresponding instance variables. See slide dispatching. The handler::operator() function fetches the latest event from the kit object and selects one of the predefined member functions (press, motion, release, etc.) according to the type of the event. The original handler class knows only virtual functions. Each of these function, including the dispatch and operator() function, may be redefined. The two-step indirection, via the dispatch and operator() functions, is introduced to facilitate derivation by inheritance, directly from the handler or from classes that are themselves derived from the handler class, such as the widget classes. }

Actions

Handler objects will be activated only when a binding has been defined, by using kit::action or implicitly by employing widget::bind or widget::handler. Such bindings may also be defined by kit::after or item::bind and item::handler. Implicit binding results in the creation of an anonymous action. Actions, as characterized by the class action, provide the actual means with which to bind C++ code to Tcl commands. Actions may be defined either trivially by a Tcl command, by a C/C++ command function or by handler objects. In effect, defining an action by means of a handler object amounts, implicitly, to defining a command function with a privileged client for which, by convention, the dispatch function is invoked. } \slide{class-client}{The client class}{

Client

client

  class client {  };  
empty class

Command

command

  typedef command(client*, kit*, int argc, char* argv[]);
  
} Data passed to a command function must be of the type client, which is defined by an empty class introduced only to please the compiler. In slide class-client the type definition of command is given. Apart from the client* parameter, a command function must also declare a kit* parameter and an argc and argv parameter, similar to that for main. The client* data of a command (and similarly for the clientdata parameter of a tclcommand) can be any kind of class. However, it is to be preferred that such classes are made a subclass of client. The client data pointer is declared when creating an action and passed to the command function when the actual call is made. The other parameters (argc and argv) depend on the actual call. The use of argc and argv comes from the original C interface of Tcl. It proves to be a very flexible way of communicating data, especially in string-oriented applications.
  interface action { 
\fbox{action}
action(char* name); action(char* name, handler* h); action(char* name, command f, client* data = 0); action(char* name, tcl_command f, ClientData data = 0); action( handler* h);
\ifsli{}{//} anonymous
action( command f, client* data = 0); action( tcl_command f, ClientData data = 0); char* name();
\c{// returns the name of an action}
};

slide: The action class

The class action offers no less than seven constructors. The first constructor, which takes a (char*) string as a parameter, is merely for convenience. It may be used to convert the name of a Tcl script command into an action. The following three constructors differ from the last three constructors only by their first string parameter, which serves to define the name under which the action will be known by the Tcl script interpreter. The last three constructors, in contrast, create anonymous actions, of which the user, however, can ask the name by invoking the function name. The preferred form of creating an action is by giving it (apart from a name) a handler as a parameter. As already noted, handler objects offer a type-secure way of dealing with client information. In contrast, the second constructor of this group, which takes a command function and possibly a pointer to client data as parameters, may make (type-insecure) conversions of the client data necessary. The constructor taking a {\em tcl_command} and ClientData as parameters is incorporated for compatibility reasons only. When using any of the last six constructors, as a side-effect an association is created between the name of the action and a Tcl command. If such a Tcl command already exists, the previous association will be overwritten. This is also the case if it has been defined as a Tcl script command.

User interface widgets

\c{ The Tk toolkit offers numerous built-in widgets. The Tk widgets conform to the look-and-feel of the OSF/Motif standard. The hush C++ interface for Tk provides for each Tk widget a class of the same name which supports the creation of a widget and allows the user to access and modify it. In addition to the standard Tk widgets, the hush library includes a number of other widgets, such as a barchart, hypertext and photo widget (created by other Tk adepts). Also, widgets of our own making are offered, such as a filechooser and MPEG video widget. } \fslide{eps-drawtool}{ The drawtool interface}{ \epsfbox{drawtool.eps} } In this section we will look at the realization of simple drawing tool. The example illustrates how to use the hush library widgets, and serves to illustrate in particular how handlers may be attached to widgets, either by declaration or by inheritance, and how to construct compound widgets. Our approach may be considered object-oriented, in the sense that each component of the user interface is defined by a class derived from a widget class. It must be pointed out beforehand that the main difficulty in defining compound or mega widgets is not the construction of the components themselves, but delegating the configuration and binding instructions to the appropriate components. Section new shows how a compound widget defined in C++ may be made to correspond to a widget command that may be used in a Tcl script. Ideally, defining a new widget includes both the definition of a C++ class interface and the definition of the corresponding Tcl command. Our drawing tool consists of a tablet, which is a canvas with scrollbars to allow for a large size canvas of which only a part is displayed, a {\em menu_bar}, having a File and an Edit menu, and a toolbox, which is a collection of buttons for selecting from among the drawing facilities. In addition, a help facility is offered. See slide eps-drawtool.
  class application : public session { 
\c{\fbox{application}}
public: application(int argc, char* argv[]) : session(argc,argv,"drawtool") {} void main(kit* tk, int argc, char* argv[]) { widget* root = tk->root(); frame* f = new frame(root,".frame"); tablet* c = new tablet(f); toolbox* b = new toolbox(root,c); menubar* m = new menu_bar(f,c,b); tk->pack(m)->pack(c); tk->pack(b,"-side left")->pack(f,"-side right"); } };

slide: The drawing tool

In slide draw-tool-x the application class for drawtool is depicted. Before the main event loop is started, the components of the drawing tool are created and packed to the root widget. In addition to the tablet, {\em menu_bar} and toolbox, a frame widget is created to pack the menubar and tablet together. This is needed to ensure that the geometrical layout of the widget comes out right. Each of the component widgets is given a pointer to the root widget. In addition, a pointer to the tablet is given to the toolbox and a pointer to the toolbox is given to the {\em menu_bar}. The reason for this will become clear when discussing the toolbox and {\em menu_bar} in sections buttons and menus, respectively. In the example, no attention will be paid to memory management. \c{

Configuring widgets

Widgets are the elements from which a GUI is made. They appear as windows on the screen to display text or graphics and may respond to events such as motioning the mouse or pressing a key by calling an action associated with that event. Usually, the various widgets constituting the user interface are (hierarchically) related to each other, such as in the drawtool application which contains a canvas to display graphic elements, a button toolbox for selecting the graphic items and a menubar offering various options such as saving the drawing in a file. } \c{ Widgets in Tk are identified by a path name. The path name of a widget reflects its possible subordination to another widget. See slide widget-hierarchy. } .so f_lay \c{ Pathnames consist of strings separated by dots. The first character of a path must be a dot. The first letter of a path must be lower case. The format of a path name may be expressed in BNF form as - ::= '.' | '.' | '.' For example `.' is the path name of the root widget, whereas `.quit' is the path name of a widget subordinate to the root widget. A widget subordinate to another widget must have the path name of that widget as part of its own path name. For example, the widget `.f.m' may have a widget `.f.m.h' as a subordinate widget. Note that the widget hierarchy induced by the path names is completely orthogonal to the widget class inheritance hierarchy depicted in slide hush-overview (a) and slide widget-classes. With respect to the path name hierarchy, when speaking of ancestors we simply mean superordinate widgets. } \c{ Pathnames are treated somewhat more liberally in hush. For example, widget path names may simply be defined or extended by a string. The missing dot is then automatically inserted. } \slide{class-widget}{The widget class}{
  interface widget : handler { 
\c{\fbox{widget}}
widget(char* p); widget(widget& w, char* p); char* type();
\c{returns type of the widget}
char* path();
\c{returns path of the widget}
int eval(char* cmd);
\c{invokes "thepath() cmd"}
char* result();
\c{returns the result of eval}
char* evaluate(char* cmd)
\c{combines eval and result()}
virtual configure(char* cmd);
\c{uses 'path()' }
virtual geometry(int xs, int ys);
\c{width x height}
widget* pack(char* opts = "" ); widget* pack(widget* w, char* = "" ); bind(char *b, handler* h, char* = "" ); bind(char *b, action& ac, char* = "" ); handler(class handler* h, char* = "" ); handler(action& ac, char* = "" ); ... void destroy();
\c{removes widget from the screen}
void* tkwin();
\c{gives access to Tk window}
widget* self();
\c{for constructing} mega widgets

void redirect(widget* w); protected: char* thepath();
\c{delivers the} virtual path

virtual install(action&,char* =""); };
}{
  interface widget : handler { 
\fbox{widget}
widget(char* p); widget(widget& w, char* p); char* type();
\c{// returns type of the widget}
char* path();
\c{// returns path of the widget}
int eval(char* cmd);
\c{// invokes "thepath() cmd"}
char* result();
\c{// returns the result of eval}
char* evaluate(char* cmd)
\c{// combines eval and result()}
virtual void configure(char* cmd);
\c{// uses 'path()' }
virtual void geometry(int xs, int ys);
\c{// width x height}
widget* pack(char* options = "" ); widget* pack(widget* w, char* options = "" ); bind(char *b, handler* h, char* args = "" ); bind(char *b, action& ac, char* args = "" ); handler(class handler* h, char* args = "" ); handler(action& ac, char* args = "" ); void xscroll(scrollbar* s);
\c{// to attach scrollbars}
void yscroll(scrollbar* s); void focus(char* options=""); void grab(char* options=""); void destroy();
\c{// removes widget from the screen}
void* tkwin();
\c{// gives access to Tk window}
widget* self();
\c{// for constructing} mega widgets
void redirect(widget* w); protected: char* thepath();
\c{// delivers} the virtual path
virtual install(action&,char* args=""); };
} } \c{ Calling the constructor widget as in widget* w = new widget(".awry"); does not result in creating an actual widget but only defines a pointer to the widget with that particular name. (It is not an abstract class, technically, since it does not contain any pure virtual functions, see section abstract-class.) If a widget with that name exists, it may be treated as an ordinary widget object, otherwise an error will occur. The constructor widget(widget* w,char* path) creates a widget by appending the path name path to the path name of the argument widget w. The function path delivers the path name of a widget object. Each widget created by Tk actually defines a Tcl command associated with the path name of the widget. In other words, an actual widget may be regarded as an object which can be asked to evaluate commands. For example a widget `.b' may be asked to change its background color by a Tcl command like .b configure -background blue The functions eval, result and evaluate enable the programmer to apply Tcl commands to the widget directly, as does the configure command. The function geometry sets the width and height of the widget. } \c{ Naming widgets in a hierarchical fashion does not imply that the widgets behave accordingly. The widget class interface offers two pack functions. The function widget::pack(char*) applies to individual widgets. As options one may specify, for example, {\tt -side X}, where {\tt X} is either top, bottom, left or right, to pack the widget to the appropriate side of the cavity specified by the ancestor widget. Other options are -fill x or -fill y, to fill up the space in the appropriate dimensions, or {\tt -padx N} or {\tt -pady N}, for some integer {\tt N}, to surround the widget with some extra space. Alternatively, the function widget::pack(widget*,char*) may be used, which allows for the same options but applies packing to the widget parameter. This function is convenient when packing widgets in a frame or toplevel widget. As a remark, the kit::pack function may only be used to pack widgets to the root window. } \c{

Binding events

Widgets may respond to events. To associate an event with an action, an explicit binding must be specified for that particular widget. Some widgets provide default bindings. These may, however, be overruled. The function bind is used to associate handlers or actions with events. The first string parameter of bind may be used to specify the event type. Common event types are, for example, {\tt ButtonPress}, {\tt ButtonRelease} and {\tt Motion}, which are the default events for canvas widgets. Also keystrokes may be defined as events, for example {\tt Return}, which is the default event for the entry widget. The function widget::handler may be used to associate a handler object or action with the default bindings for the widget. Concrete widgets may not override the handler function itself, but must define the protected virtual function install. Typically, the install function consists of calls to bind for each of the event types that is relevant to the widget. For both the bind and handler functions, the optional args parameter may be used to specify the arguments that will be passed to the handler or action when it is invoked. For the button widget, for example, the default install function supplies the text of the button as an additional argument for its handler. } \c{ In addition, the widget class offers four functions that may be used when defining compound or mega widgets. The function redirect(w) must by used to delegate the invocation of the eval, configure, bind and handler functions to the widget w. The function self() gives access to the widget to which the commands are redirected. After invoking redirect, the function thepath will deliver the path that is determined by self()->path(). In contrast, the function path will still deliver the path name of the outer widget. Calling redirect when creating the compound widget class suffices for most situations. However, when the default events must be changed or the declaration of a handler must take effect for several component widgets, the virtual function install must be redefined to handle the delegation explicitly. How redirect and install actually work will become clear in the examples. } \c{

Handlers

A note on terminology is in place here. The reader may be a little astonished by the fact that we have both a handler class and a handler function, which is more properly written as widget::handler. The situation may even seem more confusing when realizing that the widget class itself is a descendant of the handler class. Schematically, we have the situation as depicted in slide h-disc. } \slide{h-disc}{Handler classes and functions}{
   class widget : public handler {
   public:
   ...
   void handler(class handler* h) { ... }
   ...
   };
  
} \c{ Note that there is no ambiguity here. A handler object is an object that may be invoked in response to a Tcl command or an event. The handler function declares a handler object to be responsible for dealing with the events that are of interest to the widget. Note that since a widget is a handler instance, it may declare itself as a handler for the incoming events. }

Buttons

As the first component of the drawing tool, we will look at the toolbox. The toolbox is a collection of buttons packed in a frame. See slide draw-toolbox.
  class toolbutton : public button { 
\fbox{toolbutton}
public: toolbutton(widget* w, char* name) : button(w,name) {
(a)

text(name); handler(w,name); pack();
(b)

} }; class toolbox : public frame {
\fbox{toolbox}
public: toolbox(widget* w, tablet* t) : c(t), frame(w,"toolbox") {
(c)

button* b1 = new toolbutton(this,"move"); button* b2 = new toolbutton(this,"box"); button* b3 = new toolbutton(this,"circle"); button* b4 = new toolbutton(this,"arrow"); } int operator()() {
(d)

c->mode(argv[1]); return OK; } private: tablet* c; };

slide: The toolbox

Each button is an instance of the class toolbutton. When a toolbutton is created (a), the actual button is given the name of the button as its path. Next, (b) the button is given the name as its text, the ancestor widget w is declared to be the handler for the button and the button is packed. The function text is a member function of the class button, whereas both handler and pack are common widget functions. Note that the parameter name is used as a path name, as the text to display, and as an argument for the handler, that will be passed as a parameter when invoking the handler object. The toolbox class inherits from the frame widget class, and creates a frame widget with a path relative to the widget parameter provided by the constructor (c). The constructor further creates the four toolbuttons. The toolbox is both the superordinate widget and handler for each toolbutton. When the operator() function of the toolbox is invoked in response to pressing a button, the call is delegated to the mode function of the tablet (d). The argument given to mode corresponds to the name of the button pressed. The definition of the toolbutton and toolbox illustrates that a widget need not necessarily be its own handler. The decision, whether to define a subclass which is made its own handler or to install an external handler depends upon what is considered the most convenient way in which to access the resources needed. As a guideline, exploit the regularity of the application.
  class menu_bar : public menubar { 
\fbox{menu_bar}
public: menu_bar(widget* w, tablet* t, toolbox* b) : menubar(w,"bar") { configure("-relief sunken"); menubutton* b1 = new file_menu(this,t); menubutton* b2 = new edit_menu(this,b); button* b3 = new help_button(this); } };

slide: The {\em menu\_bar}

\c{

Menus

The second component of our drawing tool is the menubar. The class {\em menu_bar}, depicted in slide draw-menubar is derived from the hush widget menubar. Its constructor requires an ancestor widget, a tablet and a toolbox. The tablet is passed as a parameter to the {\em file_menu}, and the toolbox to the {\em edit_menu}. In addition, a {\em help_button} is created, which provides online help in a hypertext format when pressed. The help facility will be discussed in section hyper. }
  class file_menu : public menubutton { 
\fbox{file_menu}
public: file_menu(widget* w, tablet* t) : c(t), menubutton(w,"file") {
(a)

configure("-relief sunken"); text("File"); pack("-side left"); f = new file_handler(c);
(b)

(c)

class menu* m = new class menu(this,"menu"); this->menu(m);
declares it for the menubutton

m->handler(this);
(d)

m->entry("Open"); m->entry("Save"); m->entry("Quit"); } int operator()() { if (!strcmp(argv[1],"Quit")) tk->quit();
(e)

else f->dispatch(tk,argc,argv); return OK; } protected: tablet* c; file_handler* f; };

slide: The {\em file\_menu}

\c{ A menubar consists of menubuttons to which actual menus are attached. Each menu consists of a number of entries, which may possibly lead to cascaded menus. The {\em file_menu} class, depicted in slide draw-file, defines a menu, but is derived from menubutton in order to attach the menu to its menubar ancestor (a). Its constructor defines the appearance of the button and creates a {\em file_handler} (b). It then defines the actual menu (c). The menu must explicitly be attached to the menubutton by invoking the menubar member function menu. For creating the menu, the keyword class is needed to disambiguate between the creation of an instance of the class menu and the call of the menubar::menu function. Before defining the various entries of the menu, the {\em file_menu} instance is declared as the handler for the menu entries (d). However, except for the entry Quit, which is handled by calling the kit::quit function (e), the calls are delegated to the previously created {\em file_handler}. } \c{ The second button of the {\em menu_bar} is defined by the {\em edit_menu}. The {\em edit_menu} requires a toolbox and creates a menubutton. It configures the button and defines a menu containing two entries, one of which is a cascaded menu. Both the main menu and the cascaded menu are given the toolbox as a handler. This makes sense only because for our simple application, the functionality offered by the toolbox and {\it edit_menu} coincide. }
  class drawmode { 
\fbox{drawmode}
public: enum { move, box, circle, arrow, lastmode }; }; class tablet : public canvas {
\fbox{tablet}
public: tablet(widget* w, char* options=""); int operator()() {
\fbox{operator()}
return handlers [mode]->dispatch(tk,argc,argv); } void mode(char* m); protected: void init(char* options); int _mode; class handler* handlers[drawmode::lastmode]; canvas* c; };

slide: The {\em tablet}

Defining actions

The most important component of our drawtool application is defined by the tablet widget class depicted in slide draw-tablet. The various modes supported by the drawing tool are enumerated in a separate class drawmode. The tablet class itself inherits from the canvas widget class. This has the advantage that it offers the full functionality of a canvas. In addition to the constructor and operator() function, which delegates the incoming event to the appropriate handler according to the {\em _mode} variable, it offers a function mode, which sets the mode of the canvas as indicated by its string argument, and a function init that determines the creation and geometrical layout of the component widgets. As instance variables, it contains an integer {\em _mode} variable and an array of handlers that contains the handlers corresponding to the modes supported. See section canvas for an example of a typical canvas handler.

Dispatching

Although the tablet must act as a canvas, the actual tablet widget is nothing but a frame that contains a canvas widget as one of its components. See slide tablet-op.
  tablet::tablet(widget* w, char* options)  
\fbox{tablet}
: canvas(w,"tablet",0) {
(a)

widget* top = new frame(path()); init(options); redirect(c);
(b)

handler(this);
(c)

handlers[drawmode::move] = new move_handler(this); handlers[drawmode::box] = new box_handler(this); handlers[drawmode::circle] = new circle_handler(this); handlers[drawmode::arrow]=new arrow_handler(this); _mode = drawmode::move; }

slide: Installing the handlers

This is reflected in the invocation of the canvas constructor (a). By convention, when the options parameter is 0 instead of the empty string, no actual widget is created but only an abstract widget, as happens when calling the widget class constructor. Instead of creating a canvas right away, the tablet constructor creates a top frame, initializes the actual component widgets, and redirects the eval, configure, bind and handler invocations to the subordinate canvas widget (b). It then declares itself to be its own handler, which results in declaring itself to be the handler for the canvas component (c). Note that reversing the order of calling redirect and handler would be disastrous. After that it creates the handlers for the various modes and sets the initial mode to move. The operator() function takes care of dispatching calls to the appropriate handler. The dispatch function must be called to pass the tk, argc and argv parameters.
  class drawtool : public canvas { 
\fbox{drawtool}
public: drawtool() : canvas() { }
(I)

drawtool(char* p, char* opts="") : canvas(p,0) { init(opts); redirect(c); } int operator()(){
(II)

if ( !strcmp( "drawtool" ,*argv) ) create(--argc,++argv); else if (!strcmp("self",argv[1]) ) tk->result(self()->path()); else self()->eval( flatten(--argc,++argv) ); return OK; } protected: tablet* c; void init(char* options); void create(int argc, char* argv[]) {
(III)

char* name = *argv; char* args = flatten(--argc,++argv); handler* h = new drawtool(name, args ); tk->action(name,h); } };

slide: The {\em drawtool} widget command

\c{

Creating new widgets

Having taken care of the basic components of the drawing tool, that is the toolbox, {\em menu_bar} and tablet widgets, all that remains to be done is to define a suitable {\em file_handler}, appropriate handlers for the various drawing modes and a {\em help_handler}. We will skip the {\em file_handler}, but look at the latter two issues in sections canvas and hyper, respectively. However, before that it will be shown how we may grant the drawtool the status of a veritable Tk widget, by defining a drawtool handler class and a corresponding drawtool widget command. See slide draw-new. } \c{ Defining a widget command involves three steps: (I) the declaration of the binding between a command and a handler, (II) the definition of the operator() action function, which actually defines a mini-interpreter, and (III) the definition of the actual creation of the widget and its declaration as a Tcl/Tk command. Step (I) is straightforward. We need to define an empty handler, which will be associated with the drawtool command when starting the application. See slide new-command (a). The functionality offered by the interpreter defined by the operator() function in (II) is kept quite simple, but may easily be extended. When the first argument of the call is drawtool, a new drawtool widget is created as specified in (III), except when the second argument is self. In that case, the virtual path of the widget is returned, which is actually the path of the tablet's canvas. It is the responsibility of the writer of the script that the self command is not addressed to the empty handler. If neither of these cases apply, the function widget::eval is invoked for self(), with the remaining arguments flattened to a string. This allows for using the drawtool almost as an ordinary canvas. See the example hypertext script shown in section hyper. } \c{ The creation of the actual widget and declaration of the corresponding Tcl command, according to the Tk convention, is somewhat more involved (III). Recall that each Tk widget is identified by its path, which simultaneously defines a command that may be used to configure the widget or, as for a canvas, to draw figures on the screen. Hence, the function create must create a new widget and declare the widget to be the handler of the command corresponding to its path name. } \slide{new-command}{The drawtool application}{ .ds draw/sl-app.s }{ .ds draw/applic.s } } \c{ The application class depicted in slide new-command will by now look familiar, except for the function prelude. In the body of the prelude function, the Tcl command drawtool is declared, with an instance of drawtool as its handler (a). In this way, the drawtool widget is made available as a command when the program is used as an interpreter. However, in the main function this declaration is overridden (b). Instead, the actual drawtool widget is made the handler of the command, to allow for a script to address the drawtool by calling drawtool self. }

Delegation

You may by now have lost track of how delegation within a compound widget takes place. Perhaps a brief look at the implementation will clarify this. Each eval, configure or bind function call for a widget results in a command addressed to the path of the widget. By redirecting the command to a different path, the instructions may be delegated to the appropriate (component) widget. Delegation occurs, in other words, by directing the commands to the widget's virtual path, which is obtained by the protected function thepath(). In contrast, the function path() delivers the path of the widget's outer component. Indirection takes place by invoking the function self(), which relies on an instance variable {\em _self} that may be set by the redirect function. } \fslide{f-self}{Dereferencing self()}{
  • \hspace*{-2cm} \begin{mfpic}[25][12]{-2}{10}{0}{10} \rect{(1,1),(9,9)} \rect{(3,1.4),(8.8,6)} \rect{(4.5,1.8),(8.6,4)} \cdisk{(1.5,2.3),0.2} \cdisk{(3.5,2.3),0.2} \cdisk{(5.0,2.3),0.2} \curvedarrow{(1.7,2.3),(2.3,2.0), (3.0,2.3)} \curvedarrow{(3.7,2.3),(4.0,2.0),(4.5,2.3)} \curvedarrow{(5.2,2.3),(5.5,2.0), (5.7,2.3), (5.5,2.6), (5.3,2.3)} \label[tl]{1.2}{8.5}{drawtool} \label[tl]{3.2}{5.5}{tablet} \label[tl]{4.7}{3.5}{canvas} \end{mfpic} }{ \begin{mfpic}[25][8]{0}{10}{0}{10} \rect{(1,1),(9,9)} \rect{(3,1.4),(8.8,6)} \rect{(4.5,1.8),(8.6,4)} \cdisk{(1.5,2.3),0.2} \cdisk{(3.5,2.3),0.2} \cdisk{(5.0,2.3),0.2} \curvedarrow{(1.7,2.3),(2.3,2.0), (3.0,2.3)} \curvedarrow{(3.7,2.3),(4.0,2.0),(4.5,2.3)} \curvedarrow{(5.2,2.3),(5.5,2.0), (5.7,2.3), (5.5,2.6), (5.3,2.3)} \label[tl]{1.2}{8.5}{drawtool} \label[tl]{3.2}{5.5}{tablet} \label[tl]{4.7}{3.5}{canvas} \end{mfpic} }
} The implementation of thepath() and self() is simply:
  char* thepath() { return self()->path(); }
  widget* self() { return _self?_self->self():this; }
  
Hence, resolving a compound widget's primary inner component relies on simple pointer chasing, which may be applied recursively to an arbitrary depth at acceptable costs.

Graphics and hypertext

\c{ The Tk toolkit offers powerful facilities for graphics and (hyper)text (see Ousterhout, 1993).\index{Ousterhout (1993)} In this section we will discuss only the canvas widget offered by Tk. Instead of looking at the text widget provided by Tk, we will (briefly) look at the hypertext widget, which presents an alternative approach to defining hyperstructures. } \slide{class-item}{The item class}{
  interface item { 
\c{\fbox{item}}
operator int();
\ifsli{}{returns} item index

void configure(char* cmd);
\c{uses canvas}
void tag(char* s);
sets tag \c{for item}

char* tags();
delivers tags \c{set for the item}

void move(int x, int y); bind(char *b, handler* h, char* = "" ); bind(char *b, action& ac, char* = "" ); handler(class handler* h, char* = "" ); handler(action& ac, char* = "" ); protected: virtual install(action&,char* =""); };
}{
  interface item { 
\fbox{item}
operator int();
\ifsli{}{returns} item index

void configure(char* cmd);
\c{uses canvas}

void tag(char* s);
sets tag \c{for item}

char* tags();
delivers tags \c{set for the item}

void move(int x, int y); bind(char *b, handler* h, char* args = "" ); bind(char *b, action& ac, char* args = "" ); handler(class handler* h, char* args = "" ); handler(action& ac, char* args = "" ); protected: virtual install(action&,char* args=""); };
} } \c{

The $item$ class

The canvas widget allows the programmer to create a number of built-in graphic items. Items are given a numerical index when created and, in addition, they may be given a (string) tag. Tags allow items to be manipulated in a group-wise fashion. To deal with items in a C++ context, the hush library contains a class item of which the functionality is shown in slide class-item. } \c{ Instances of item may not be created directly by the user, but instead are created by the canvas widget. For an item, its index may be obtained by casting the item to int. If the index does not identify an existing item, it will be zero. Existing items may be moved, in a relative way, by the move function. } \c{ In a similar way as for widgets, items may be associated with events, either explicitly by using item::bind, or implicitly by using item::handler. The default bindings for items are identical to the default bindings for the canvas widget, but these may be overridden by descendant classes. Similar to the widget class, the item class is derived from the handler class. This allows the user to define possibly compound shapes defining their own handler. }

The $canvas$ widget

The Tk canvas widget offers a powerful means for creating structured graphics. The hush class canvas provides merely a simplified interface to the corresponding Tk widget. }
  class move_handler : public handler { 
\fbox{move_handler}
public: move_handler( canvas* cv ) : c(cv) { dragging = 0; } void press( event& e ) { x = e.x(); y = e.y(); id = c->overlapping(x, y); if (id) dragging = 1; } void motion( event& e ) { if (dragging) { id.move( e.x() - x, e.y() - y ); x = e.x(); y = e.y(); } } void release( event& ) { dragging = 0; } protected: canvas* c; int dragging; item id; int x,y; };

slide: The {\em move\_handler} class

As an example of the use of a canvas, consider the definition of the move_handler class in slide draw-move. The move_handler class is derived from the class handler. It makes use of the dispatch and operator() function defined for handler, but redefines the (virtual) functions press, motion and release. When creating an instance of move_handler, a pointer to the canvas must be given to the constructor. In addition, the class has data members to record position coordinates and whether a particular item is being moved. Actually, moving an item occurs by pressing the (left) mouse button on an item and dragging the item along. When the mouse button is released, moving stops. To identify the item, the function overlapping is used. The movement is determined by the distance between the last recorded position and the current position of the cursor. \fslide{hyper}{Hypertext help}{ \epsfbox{help.eps} } In an analogous manner, a box_handler may be defined. The box_handler sets dragging to true when the button is pressed and creates a rectangle of zero width and height. Each time the function motion is called, the item created in the previous round is deleted and a new rectangle is created by calling
   c->rectangle(x,y,e.x(),e.y());
  
where c is a pointer to the canvas and x and y the button pointer coordinates stored when dragging began. For circles and lines, it suffices to replace the call to rectangle with a call to the appropriate figure creation function. \c{

The $hypertext$ widget

Both the Tk canvas and text widget allow the binding of actions to particular items and hence defining dynamically what we may call hyperstructures. A different, in a way more static, approach is offered by the hypertext widget originally developed by George Howlett. %%george.howlett@att.com. } \slide{help-text}{ A hypertext help file }{
  Rubber banding: press the left mouse button
  and release when the rectangle is of appropriate
  size
      %%
  drawtool $this.draw 
  $this append $this.draw 
  $this.draw create rectangle 20 20 80 80
  $this.draw create rectangle 10 30 70 90
  $this.draw create oval 40 40 90 90
  $this append $this.draw 
  %%
  For additional information click on the %% 
  button $this.goto -text instruction -command end-of-text
  $this append $this.goto
  %%
  button.  Press %%
  button $this.quit -command { destroy . } -text quit 
  $this append quit
  %% to remove the window.
  
}{
  Rubber banding: press the left mouse button
  and release when the rectangle is of appropriate
  size
      %%
  drawtool $this.draw 
  $this append $this.draw 
  $this.draw create rectangle 20 20 80 80
  $this.draw create rectangle 10 30 70 90
  $this.draw create oval 40 40 90 90
  $this append $this.draw 
  %%
  For additional information click on the %% 
  button $this.goto -text instruction -command end-of-text
  $this append $this.goto
  %%
  button.  Press %%
  button $this.quit -command { destroy . } -text quit -bg pink
  $this append quit
  %% to remove the window.
  
} } \c{ The hypertext widget may be used to display text files containing embedded Tcl code. The Tcl code must be placed between escapes, that take the form of {\tt %%} for both the start and end of the code. A screen shot of a fragment of the online help for drawtool is given in slide hyper. Notice that the online help provides a replica of the drawtool application, surrounded by text. When looking at (again a fragment of) the hypertext file specifying the contents of the online help, given in slide help-text, you see that the drawtool command defined in section new is employed to create the embedded widget. } \c{ When specifying the hypertext file, widgets may be given a pathname relative to the pathname of the hypertext widget by using the variable this. In addition, the hypertext widget offers the variables thisline and thisfile to identify the current line number and current file name. } \c{ Any of the widgets and commands offered by Tcl/Tk or supported by hush may be included in a hypertext file, including the ones defined by the program itself. }