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. } .so overview .so kit .so session

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. } .so sli-handler \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. } .so sli-dispatch \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. } .so drawtool .so widget .so button .so menu .so delegation .so new

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. } .so sli-item \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. } .so sli-help \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. }