Figure 4.1 shows the relationships between the built-in X, Motif and Unix libraries and an arbitrary X/Motif application. All X, Motif and Unix libraries have a C interface (functions, defines, typedefs, structs, enums, unions and variables). When a library is depicted on top of another library, this means that a library is implemented by means of the interface provided by the underlying layer. You are referred to the X and Motif books and manuals in the list of references at the end of this text.
The TCM X/Motif user interface is restricted to that part of the source (classes and functions) that directly uses the Motif, Xt and Xlib libraries. That part consists of most of the classes in the src/ui directory and some of the classes in the other editor libraries. The names of the editor specific user interface classes all end on Window or Stubs, such as ERWindow, DFWindow and DiagramStubs, TableStubs etc. The window classes are all specializations of the abstract class ui/MainWindow. The stub classes contain functions that are called from the user interface widgets (menus, push buttons, text fields etc.) and these functions in turn call application specific functions of non-GUI objects.
The ed/DrawingArea class is not part of libgui but of libeditor because it also has application specific behavior (albeit at a high level). The drawing area needs a viewer class (a descendent of class ed/Viewer). The viewer class should provide functions that are called when mouse buttons are pressed or dragged in the drawing area, when the mouse pointer is moved or when a characters are typed in while the mouse pointer is in the drawing area.
When asked what code contains X/Motif calls (and forms the GUI) then the answer is: almost all code of src/ui, all classes that end on Window or end on Stubs and the DrawingArea class.
The entire TCM distribution of the current version consists of about 450 classes and about 83000 lines of code (source and headers files, including white spaces and comments, but excluding the 20-line header part of the GNU General Public License). The X/Motif dependent part is about 19000 lines of code. This is the part that should be rewritten when TCM is translated to a non-X Windows environment.
The TCM user interface is more or less insulated into a restricted part of the source code, put in a number of C++ classes that are clearly distinguishable from the rest. But the user interface code is spread over all the TCM libraries (except libglobal). The reason for this organization is that factoring out the commonalities between all different tools was for us more important than a complete separation of the entire application code on one hand and the entire user interface code on the other. This means that each diagram editor has its own specific node and edge buttons. The code for creating tiled buttons in general is part of libgui but the specific initialization is done in the code of a specific diagram editor.
The approach of factoring out commonalities was a design for evolution and that has proved to work for TCM. It makes it relatively easy to add new tools or modify individual existing tools while maintaining a stable core of libraries and unrelated tools. When features are added to some library or to some tool, all tools that are included in the area of that library or tool (see figure 3.1) are also updated minimizing the risk of code redundancy or inconsistencies.
Like all X11 applications, the TCM tools are event driven. This means they have the following basic structure:
When a TCM tool is started the widgets that form the main window are created. The class ed/EditWindow and its specializations contain the functions to create the constituent parts of the main window. These widgets have one or more so-called call-back functions: the widget reacts to a certain set of X events (that set is built-in) and when such an event occurs a user-supplied action, in the form of ordinary C functions, will be called. In our implementation we do not use ordinary C functions but instead we provide C++ static member functions.
An exception to this use of callbacks, is the drawing area widget which, by default, does not react on the events that are needed to draw pictures as we like to do in TCM. Therefore, so-called translations are used, an X-Toolkit data structure to define some specified mapping of X events to user supplied actions. All details of translations and drawing area events are hidden in the ed/DrawingArea class.
Most pop-up window dialog widgets are created during initialization. When they are popped up they do not have to be created (which is much faster) and when these dialogs are dismissed they are not destroyed, but they are simply kept unmanaged (invisible). An exception to this are so-called message dialogs, for error and warning messages etc. These are created on the fly because we can not determine in advance how many of which kind will be necessary.
A special kind of message dialog is the question dialog which is application modal. This means that the user can only respond to this dialog and other user actions in the applications are prohibited. This is necessary when the application has to have an answer before it can proceed (e.g. the `save before quit?' dialog).
The main event loop is built-in in Motif, as well as the calling of the event handlers (via callback or translation).
In contrast with an ordinary C program the editors don't start by directly entering the main() function. Instead, each editor is compiled with a distinct file ??editor.c, e.g. gdeditor.c for TGD. In that file two global objects are created (but the two instance variables are not visible outside this file). For example:
Application *app = new Application("Tcm"); MainWindow *mw = new GDWindow("Tcm");This means that the very first thing that is done on start-up is calling the constructor
There should be just one instance of class Application per editor instance, which can be accessed via the global variable named theApplication. The application opens the X display, sets the color map and does some other things that are applicable for the entire application. Furthermore, the application class keeps track of a list of the application (main) windows. The current TCM implementation is limited to a single main window (the other windows are made as pop-up dialog windows) but in principle the framework of libgui can be utilized in programs that have multiple main windows.
After the creation of both class instances, the function main(argc, argv) is entered in which theApplication->Initialize(argc, argv) is called. The main function is generic and is part of libgui. The application then creates an object config of type Config, which reads in the configuration file and keeps tracks of various editor defaults. Then the application initializes the Xt application context and subsequently calls the main window initialization functions.
The main window initialization consists of the creation of the main window widgets. After the main window widgets are created, some other important objects are created (in the DiagramWindow or TableWindow class), notably:
These objects (of which exactly one instance exist per main window), will be supplied as parameter to the Motif callback functions 4.1. The callback function are static member functions of one of the stub classes. When the stub is called then the object is casted to the right type (because the parameters to stubs are simply pointers) and the appropriate class member of this object is called possibly with some other parameters that are supplied to the stub.
And finally, after the creation of the main window, the application object and the other main editor objects, the X main event loop is entered, giving X the control.
See figure 4.2 for the basic main window's widget structure. The boxes are Motif widgets (widget type and name between parentheses). The arrows connect widgets with their parents. The root is the top-level shell (TCM). To be more exact: the figure shows the main window of the generic diagram editor, TGD. The other diagram editors look almost the same, the main difference are their node and edge buttons. The table editors are almost similar too but they do not have node and edge buttons of course.
When libeditor.so is compiled with the option -DDUMPWIDGETTREE, a textual representation of the widget structure will be written to standard output when the editor is started (its output is also used for making this figure).
The details about creation of the widgets can be found in the classes ui/MainWindow, ed/EditWindow, ed/DrawWindow, dg/DiagramWindow, tb/TableWindow etc. See the class hierarchy of chapter 5 for the relationships and all the members of these classes.
Menus are created by the constructor function of class ui/Menu. For an example for how to create a menu see dg/DiagramWindow::CreateMenuBar.
The items of a menu are specified by means of the ui/MenuItem class. A menu contains a list of menu items, which can be supplied to the menu constructor function. The menus common to all drawing editors are made in ed/DrawWindow, the menus common to all diagram editors are made in dg/DiagramWindow and the menus common to all table editors are made in tb/TableWindow. Likewise, menus that are specific for a single editor, are made in the corresponding main window class.
One of the fields of a MenuItem is the function that will be called when the menu item is selected. These functions are called stubs and have to be static C++ class members. The functions that are supplied are all part of a ...Stubs class which consist entirely of these static functions. Menu items also have a so-called callback data. The callback data is always used to pass a pointer to the object whose class member should be called. For example, the Print menu item has as callback function the Print function of ed/Editstubs and as callback data a pointer to a printer of class ed/Printer. In the stubs function Printer::Print is called.
You can also call functions via the other main window widgets, for instance the diagram name text field or the node and edge toggle buttons. This works similar as with menus. You have to supply a callback function (XtCallBack) after the widget creation. These callbacks are also listed in the stub classes.
This is a very short introduction to drawing lines etc. in Xlib. For the rest you are referred to the documentation mentioned in the references.
X has 16 drawing modes (so called raster operations). A technique for simulating graphical objects moving or stretching (like resizing a box or a ``rubber band'' line), is to set the Graphics Context to the eXclusive-OR raster operation mode. The Graphics Context is an Xlib data structure which determines how an object will be drawn when a drawing routine is called (XDrawLine, XDrawRectangle etc.). In XOR mode, the new destination pixel is produced by the exclusive or of the old destination pixel with the source pixel. In this mode, you can easily draw and erase a figure. You draw a figure to let it appear, and when you redraw it, you erase it. By sequencing drawing and redrawing you can simulating an object being moved or dragged. Furthermore, in XOR mode, objects can overlap each other without damaging each other. The overlapping part is then white, and when one of the objects is moved or removed it will be redrawn, so that the overlapping part will appear black again. All details of drawing graphics with Xlib is hidden in the XGrafport class. An XGrafport has as attributes some GCs.
Note that each Xlib drawing operation is performed twice: one on the drawing area (window) and one on a Pixmap that serves as a backup store. This is needed because X does not automatically save the window contents when the window is overlapped or resized.
All the entire Xlib drawing functionality is hidden in the ui/XGrafport class which is a descendant of the abstract ui/Grafport class. Because XGrafport only uses a reference to the X Display and an X Window it can be used rather easily in any 2D drawing program under X (it uses Xlib but it does not use Motif nor the other classes of libgui that use Motif).