################################################ # # # ## ## ###### ####### ## ## ## ## ## # # ## ## ## ## ## ### ## ## ## ## # # ## ## ## ## #### ## ## ## ## # # ## ## ###### ###### ## ## ## ## ### # # ## ## ## ## ## #### ## ## ## # # ## ## ## ## ## ## ### ## ## ## # # ####### ###### ####### ## ## ## ## ## # # # ################################################ The following paper was originally published in the Proceedings of the Fourth Annual Tcl/Tk Workshop Monterey, California, July 1996 For more information about USENIX Association contact: 1. Phone: 510 528-8649 2. FAX: 510 548-5738 3. Email: office@usenix.org 4. WWW URL: http://www.usenix.org Tcl/Tk as an OpenDoc Scripting Part Jim Ingham AT&T Bell Laboratories I ) Introduction The OpenDoc component document architecture promises to revolutionize how modern applications are deployed to users. Backed by the Component Integration Laboratories (including Apple, IBM and Novell), it promises to cure the bloat of modern applications, which feel the need to provide every feature possible within one shell. More important, OpenDoc provides unique opportunities for smaller developers, since they can concentrate on solutions in their particular area of expertise, without having to provide the whole environment within which their solutions can function. The basic idea of OpenDoc is to cast the modules that comprise an ordinary application: text editors, spell checkers, picture editors, a differential equation solver, an FTP channel . . ., as separate "parts" that can be embedded in arbitrary combinations into a document shell. The document shell, provided by OpenDoc, controls the event loop and storage, arbitrates access to system resources like the menubar, mouse and keyboard, and provides a channel of communication among the parts for geometry management and data passing. OpenDoc provides a rich language by which the parts communicate, but it does not come with many prebuilt features. While the geometry arbitration mechanism is well defined, the developer is free to choose the particular model of geometry management according to his or her needs. OpenDoc uses the Open Scripting Architecture (OSA) to facilitate data passing among the parts, but does not provide any particular scripting language. Nor does it come with a toolkit for implementing ordinary GUI functions (buttons, listboxes, . . . ). This situation provides an interesting opportunity for deploying Tcl/Tk in a new context, which has many attractive features. If we could make Tk a "part" in the OpenDoc world, then the creators of OpenDoc content would get the ease of development and the rich widget set for their documents that has been such a boon in the X world. At the same time, Tk developers would gain the ability to embed whatever parts are available in the OpenDoc world into their application, without having to build extensions for those parts. This kind of embedding could fill the role that the complex VBX's fill in the Visual Basic world. Also, since OpenDoc is intended to be a cross-platform architecture, the 7.5/4.1 versions of Tcl/Tk are a natural choice for the scripting architecture/widget set. In the rest of this paper, I will give an outline of how Tcl/Tk could be used as a tool in the OpenDoc environment, first providing a brief summary of OpenDoc. Then I will sketch the enhancements that need to be made to Tcl/Tk in order fulfill this role. There are several levels of OpenDoc incorporation, and while the tightest coupling would require some rather low-level changes to the Tk core, there is an intermediate level that could be done fairly surgically. I have started the work in some of these areas, mostly at this point as an assay of the level of difficulty. Although there is much work to be done, there do not seem to be any show-stopping difficulties, and results are promising enough to make the effort worthwhile. This paper is really meant to be a call for an interest group. I intend to show why this is an interesting project and sketch its parts, in the hope that I can find some other developers who are interested in cooperating in its implementation. Note that the following discussion is in the context of the MacOS port of Tcl/Tk. There are also ports of OpenDoc to AIX and OS/2 currently available in Beta, with a Windows port due in the middle of the year, but I have no experience with these ports. II) Summary of OpenDoc The fundamental idea of the OpenDoc model is that the document, rather than the application, should be the central focus of attention. The motivation is to give the content provider control over what elements go into their document: If I don't need pictures, then I don't need the picture editor most word processors now routinely include... And if I need a picture editor, or some other special service embedded in a text document, I should be able to choose one made by someone whose primary interest it was to provide that function, rather than the knock-off required to meet some feature spec sheet. Each OpenDoc document is broken down into a number of "parts", each of which is controlled by its own "part editor". A part editor could be a text or picture editing facility, a spell checking facility, a TCP/IP socket interface... In a given document, there may be several parts that are controlled by the same part editor. By implementation, the part editors are actually class definitions, derived from a base class provided by OpenDoc, and the parts are instances of their part editor class. So although the parts owned by the same part editor share code, they are really independent entities, with their own data and state. The user does not manipulate part editors directly. Rather, the developer of the OpenDoc part also makes a "stationary" document for the part editor. This can be as simple as a template for one part, or that part with some state information included, or can be a whole conglomeration of parts. The user opens the stationary document, which automatically makes a copy of itself for the user to edit. The stationary documents also support a drag and drop mechanism. So if you want to add a picture to a text document you are editing, you drag the stationary document for the picture part editor from the finder onto your text document, and drop it wherever you want the picture to go, then interact with the picture part to load in the desired image. Thus you can make a complicated collections of parts, save it as a stationary document, and it becomes an atomic unit that you can later insert into another document. Each part usually controls some screen area. For instance, a text part would have some area in its document's window to display its contents. Each contiguous bit of screen area in a window that a part occupies is called a "frame". Each part can have more than one frame, and the frames owned by a given part need not be in the same window. So, for instance, if I wanted to put a long bit of text into a newsletter document, I could break the textÕs part up into a number of frames, and spread the frames throughout the newsletter. There is no requirement that the frames be rectangular, though this has little implication for our purposes. More importantly, the frames of a given part can be constructed in such a way as to contain other parts. So one frame of a text part could contain a picture part; for instance to display an in-line image. The containing frame (the text editor in our case) manages the geometry of the embedded part (the picture editor), but has no other responsibility for it. OpenDoc forwards update requests to all the embedded parts, and each part has the responsibility for drawing itself. Thus the coupling between an embedded part and its container is minimal. The embedding can go on to any depth. All user interaction is routed through the OpenDoc shell. For each system service - the mouse, the keyboard, and other ports - there is an associated "focus". Parts negotiate, through the OpenDoc shell, for the focus for a given input stream. In general this is done by "activating" one part, which then requests the keyboard and mouse focus. There are user-interface conventions for how the active part is displayed, and how the activation is switched from one part to another ( this is done by a single mouse click in the part to be activated, for instance). Once a part gets the focus for a given input, the OpenDoc shell will route all such events to that part. Finally, the OpenDoc shell provides the facility for one part to send scripting events to another part in the document, using AppleÕs Open Scripting Architecture (OSA). The OSA provides a well defined way for parts to publish their interface to other parts, and to OSA aware editors. To achieve this communication, the parts build up "Apple Events", which describe a set of steps to be taken using this interface, and pass them back and forth. Any part that is OSA compliant, i.e. that has this interface, can take part in these scripting exchanges. II) Tcl/Tk in OpenDoc Let's start with a scenario of how Tcl/Tk could be used in OpenDoc. Suppose I have written a 2-D differential equation solver, which I have incorporated as a Tcl extension so that I can script it. Then I wrote a nice Tk front-end to control the simulation. Now I need to display the data. Since it is not entirely trivial to write a good 3-D data viewer, particularly one that can do convincing coloring and shading, rotations, etc, and perhaps produce a QuickTime movie of the results, I will look for a commercial package to do the task. All this is possible in the context of ordinary applications, however, the presentation is fragmented. I start up my Wish, do the simulation, and then start up the 3-D viewer, play with it a bit, then go back to the wish to redo the simulation. I have to have a data file somewhere to store the input cards, and the results somewhere else, and my movies in a third file... If both Tk and my 3-D viewer were OpenDoc parts, however, I could make up a single Document that contains as its parts, the Solver engine, the GUI and the viewer window, sort of a poor-man's Mathematica. Later, if I wanted to run a series of simulations, I could add a spreadsheet part to store a table of test cases, write a Tcl script to get the columns one by one from the spreadsheet, run the simulation for each column, and write back some results to the spreadsheet. I could even embed the QuickTime movies of the results as icons in the spreadsheet, if the spreadsheet was written to contain other parts in its cells. Finally, my Tcl script could drive the spreadsheet to do some analysis of the results. All this would be within a single document, so I can hand it off to a colleague to use, without having to give complicated instructions on its use (for instance describing the naming convention that links the input and output data sets). Furthermore, I can make copies of the document, each containing a separate test sequence, and each copy is self-contained, with all its data and results in one place. This picture relies on two separate facilities: the intercommunication of the parts which makes joint operations possible, and the common shell by which a single environment can be constructed around all these functions, some of which I control (the Tcl/Tk and solver engine), and some of which I don't even have libraries for (the spreadsheet and 3-D viewer). I will discuss in the next section what needs to be done to provide these two facilities. The advantage of this integration goes in two directions. Tk brings to the OpenDoc world an extensible (I could incorporate my 2-D solver) scripting language, with a well-designed widget set. In return, Tcl/Tk applications will gain access, in a particularly seamless and attractive way, to custom facilities that are not available as libraries for incorporation into the Tcl shell. The whole discussion is speculative at present, since there are no quality applications currently available as OpenDoc parts. But this will likely change in the near future; OpenDoc won Best Technological Achievement of 1995 at Comdex this year, and has been consistently rated superior to the only real competitor, MicroSoft's OLE. There is an impressive list of commercial developers committed to writing OpenDoc parts (see http://www.cilabs.org for details). And at the same time, Tcl/Tk has an opportunity to make a contribution to this development precisely because there are no well established competitors. III) Changes to Tcl/Tk This section falls under two heads, OpenDoc compliance, and OSA compliance. It is worth pointing out that these two are really independent. In fact, the OSA compliance should really be implemented regardless of whether the OpenDoc work is attempted at all. It is the logical way to implement send and perhaps exec on the Mac. Both the Tcl-based Mac Editor Alpha and the Mac implementation of Perl offer Apple Event support. In the case of Alpha, this has made it a very powerful plug-in editor for the major Mac development environments. Without some kind of Apple Event support, Tcl/Tk will be a mute and isolated participant in the MacOS world. A) OpenDoc: There are two levels of OpenDoc incorporation offered by the Apple OpenDoc development group. One is to make Tcl/Tk a full fledged OpenDoc part, capable of being embedded in other parts, and of containing embedded parts. This is called a container part, in OpenDoc terminology. This is obviously the best way, since it would allow us to embed Tk controls in any other document, and to embed viewers, spreadsheets or whatever parts we wish within the Tk layout of our application. However, it also takes the most work, some of which is not entirely trivial. The second is to make Tcl/Tk be an embeddable part, but not a containing part. This would allow us to put Tk controls and display widgets into a document alongside other parts, and use the Tcl interpreter to run our own extensions, and communicate with the other parts. However, we would not, for example, be able to embed the 3-D viewer into a Tk text widget, or use the Tk scrollbar widget to drive a non-Tk part. i) Necessary Enhancements: The full OpenDoc support requires changes or enhancements in four major areas, the event loop, windows, part activation, and part storage. The following is a bare sketch of each of these four areas. The Event Loop: In any compound architecture like OpenDoc, the shell has to control the event loop. In order to switch control between the various parts, and also allow the parts to share idle time, there has to be a central router. The way this is implemented in OpenDoc is that each part has a HandleEvent method that gets called by the document shell. The document shell takes each event off the event queue, determines which part should respond to that event, and dispatches the event to that partÕs HandleEvent method. The Tk event loop would have to be replaced by an event handler, which can do what it wishes with the events in TkÕs internal event queue, but cannot poll for system events. This is not too difficult to implement. The current model in Mac Tk is that each time through the event loop, the top of TclÕs internal event queue is serviced. If there was an event in the queue, it is handled and the event loop is restarted. If the queue is empty, then the various event sources (file events, as well as UI events,...) are all polled for events. The new events are not processed at this time, but just added to TclÕs internal queue. Then the top event in the queue is serviced. In OpenDoc, the handler would just put the event that woke it up onto the queue, then service the top event. The polling for file events, etc.. would be done when the handler was invoked for an idle event. It is yet to be seen whether this will cause performance problems, but it is the model for all the OpenDoc applications, and none of the currently available parts seem at all sluggish, so it should not be a problem. Windows: One major change in Tk required by OpenDoc is that the main window of the application may not be a top level window. After all, we want to be able to embed a Tk layout into a document. This does not seem a very hard problem, however. We can just make an ODToplevel widget, which is just like a TopLevel, except that its window manager is the OpenDoc shell rather than the MacOS window manager. Tk would draw all its component widgets into this ODToplevel just as it would into an ordinary toplevel. Then it passes its final geometry request to its containing part. There is some work to be done to implement the wm and winfo commands for these ODToplevels, but OpenDoc possesses a full set of calls to get geometry information, and arbitrate for new geometry, so the groundwork is well laid. One requirement for the ODToplevel, and ordinary Toplevels, is that they must be registered with OpenDoc. This is so OpenDoc can do event routing, and dispatch update requests. This registration is accomplished by a fairly simple call, though we have to get access to our parts object to make it. Thus windows in OpenDoc Tk need to have a part pointer added to their data storage. This can just be a ClientData pointer hung off the window structure, so it is not too intrusive. If we want to implement Tk as a containing part, we will also have to provide an ODFrame widget. This will be the containing frame for an embedded OpenDoc part. Each OpenDoc part is responsible for informing its containing part of its geometry requirements, so the ODFrame has all the information it needs to negotiate for position within the rest of the Tk layout. Further, each OpenDoc part has the responsibility to draw itself, so we donÕt need to do any work to present the contained part. There are some User Interface guidelines to indicate which part is the active one in the document, so the ODFrame widget would need some custom border drawing routines to conform to these. Since OpenDoc does all the event passing, the ODFrameÕs responsibilities to itÕs contained part are limited to activating it when the frame is clicked in, and redrawing the borders in accordance with the UI Guidelines when the contained part becomes inactive. Part Activation: When the Tk part is activated (for instance, when it is clicked in, or when it is first created) OpenDoc requires a number of setup routines be run. The part has to request focus for the events it is interested in (mouse events, keyboard events, as well as other system functions, like communications ports). However, this Focus negotiation is separate from the Tk focus commands, just as the window manager focus in X is separate from Tk. Basically, all that is required is that when the Tk part becomes active, it must request the keyboard and mouse focus, done through fairly simple library call. Again, when the part is deactivated, there is a protocol that must be followed to resign the foci which the newly activated part requests. The UI requirements for displaying the active part (there is a little double line border in the current implementation) must be implemented. This frame would just be a special -relief option for the ODToplevel. The general UI standard for OpenDoc is that a part withdraw its floating palettes when it becomes inactive, and redraws them when it is reactivated. This would also have to be implemented. Part Storage: Each part is responsible for reading itself in from storage, and writing itself out to storage. This is so the document can be saved and restored to this state later. In Tk, this is relatively easy, since the Tcl/Tk architecture allows you to query out the state of the interpreter. You know what all the global variables are, and you can just run the Tk tree to find out all the widgets, their contents and their positions. You also need to track the proc definitions, and what files are open, what fileevent handlers have been registered, etc. The state of the application can be "pickled" as this set of Tcl/Tk commands. Doing this quickly and efficiently may take a little work, but all the facilities are already available in Tk. Of course, any extensions that you add to Tcl will have to have this ability to pickle themselves as well, if they are to coexist in OpenDoc. B) Open Scripting Architecture: This is the generic language by which parts (and applications under MacOS) communicate with each other. The idea is that each application specifies the objects and operations that it wants to export to the world. For instance a generic application has objects like documents and windows; and a word processor has, in addition, paragraphs, lines and words of text. There is a "Required suite" of operations that a generic application must perform: open print, quit and run. Then our word processor will add natural functions like get, set, delete and save, whic can act on any of the basic objects. Each of these objects are specified by a four-letter code in a program resource, which identifies the object or function, and by some "Human readable" information on the object. These codes are linked to a handlers within the application at startup. To pass an instruction to another part or application, you construct an Apple Event, which is just a data structure describing the operation and the objects it acts upon using these codes. It is a nested structure, so that objects can be specified as the result of operations, and thus quite complicated instructions can be built up. Then you pass this Apple Event to the Apple Event Manager, which routes it to the other application or part. That part receives the instruction as an entry in its event queue, (of type kHighLevelEvent) and responds the event, parsing up into its parts, and calling out the handlers associated with the codes, and finally returning a reply Apple event if the incoming instruction requested a result. Our job, however, is simpler than the above discussion makes it sound. We do not need to provide an OSA interface to the objects within Tk, since it already has its own scripting language. If another part wants to get at a widget in a Tk layout, they can just send a bit of Tcl code. All we need is 1) to be able to accept a Tcl script from another application, and respond to it, and 2) some facility to build up an Apple Event and dispatch it to another application. This capability already exists in the Tcl-based Mac Editor Alpha. There is a doScript Apple Event handler, and a Tcl extension that allows you to build up arbitrary Apple Events from their codes, and send them. Although the Alpha code is not in the public domain, so we cannot use it directly, the Apple Event builder is a simple wrapping of a library (AEGizmos) provided by Apple, and in the public domain, so this functionality is easy to replicate. Adding the single Apple Event handler (doScript) is quite straightforward. Of course, this is a very low-level solution, and probably not how you would really use the inter-part communication. In keeping with the Tcl philosophy, you would identify a part you were interested in communicating with, and then extend the basic Apple Event builder to provide a higher level of abstraction appropriate to that part. There is a Core suite of Apple Events that all applications are expected to support (when the events and objects make sense for the application), and it would be worth the effort, at least, to wrap the process of building these events. One example of the use of this Apple Event builder is to implement send for Tk on the Mac. After all, send is no more than the doScript Apple event with the script as its data, sent between two Wish applications. Another use for the Apple Event builder, is to implement exec. There is no concept of "Command line arguments" on the Mac, so you canÕt really make an exact parallel to the Unix exec. The equivalent is to send a run Apple event to the application, and the extra initialization data can be provided in the Apple Event. So the implementation of this inter-part communication would close two open items in the Mac Port of Tk. IV) Conclusions: First of all, I hope I have communicated some of the promise of the "document centered" User Interface philosophy. It allows the user to tailor "mini-applications", which contain the specific functionality they need. Moreover, each copy of the document contains both the description of the "application" that the user has assembled, and its state when he or she last left it. So, once constructed, the document can be replicated, reused, edited, and embedded as an atomic element in another document. It transfers many of the "application designer" functions to the user of the application, and offers a flexibility and level of control that is quite promising. However, at this stage the OpenDoc world is lacking a good solid toolkit. This, by the way, is not some oversight on the part of the OpenDoc development group. The idea was to create an open architecture that developers could tailor to their own needs, rather than forcing a whole package down everyoneÕs throats. This situation offers great possibilities, however, for someone who can deploy a solid toolkit in OpenDoc. There are several reasons to think Tcl/Tk would fill this role elegantly. Its extensibility would be a great asset, just as it is for stand-alone applications. The widget set has proved popular because it is easy to use, and yet very flexible. Currently OpenDoc only has a solid implementation under MacOS. However, as it is deployed to more platforms, we can repeat the steps we took to embed Tcl/Tk into MacOS OpenDoc, with the confidence that the engine is well tested on those platforms. This would not be true of any widget set that was developed first of all on the Mac. I will conclude by laying out a proposal for the incorporation of Tcl/Tk into OpenDoc. First of all, we need to refit the event loop part of Tcl, converting it into a handler based architecture. Substantial progress has been made in this area already. Then, we should implement the scripting engine into MacTk, and port that to OpenDoc. This is of sufficient utility to MacTk in general, that it should be done as soon as possible. The next step is to embed Tk. The simplest way to do this is to start by allowing only separate, Tk-controlled toplevels. These only need to be registered with OpenDoc, so they receive events, other than that they do not differ from their Wish counterparts. This has also been done. Then we need to write an ODTopLevel widget, and finally the ODFrame widget. I have not made any progress on these items yet; they are fairly independent, and would make interesting projects for anyone interested.