A major design point for VMD is to make it relatively easy to add completely different user interface (UI) methods, and allow for each interface to provide a means for accomplishing tasks that may also be accomplished by using the other interfaces as well. Figure 5 illustrates the objects which are used to realize this design. There are four main or base-class level objects used in this category:
Since there are to be several different UI components, there needs to be a way to avoid duplication of the code required to carry out the tasks requested by the user manipulating the user interface. This is the purpose of the Command object: each subclass of Command represents a single operation or tasks which the user may request to be done via a user interface of some form. These Command objects may take parameters to tell them exactly how to perform the task, but are designed to be rather specific about exactly what they should do. For example, CmdMolNew is the Command subclass which contains all the code necessary to create a new molecule via the algorithm described earlier, while the CmdRotate object knows how to apply a specified rotation to the current Scene. Each Command has a unique code, defined in the file Command.h, and requires derived classes to do the following things:
*cmdText « "rot " « axis;
*cmdText « ( byOrTo == CmdRotate::BY ? " by " : " to ");
*cmdText « deg;
*cmdText « ends;
There are many many actions which need to be done each time through the main execution loop of VMD (section 20). The CommandQueue object is used to queue and execute all the actions that need to be done. This is essentially a FIFO queue, and there is just one instance of this in VMD (stored in the global variable commandQueue). This object also contains routines for logging a VMD session. New Command objects are added to the queue via the routine void CommandQueue::append(Command * ), and are appended to the end of the queue; the routine void CommandQueue::execute( ) then executes the top Command in the queue, and then deletes the Command instance. After the Command is executed, but before it is deleted, CommandQueue informs the UIObject's (described later) that the action was done (why this is so is also described later). Since the Command is deleted after it is executed, an instance of the Command must be created via new, and then left to CommandQueue to be deleted. This is done because due to the asynchronous nature of this method of executing commands, it is not known exactly when the data in the Command will be needed, and thus it is unknown when the storage space may be freed up for other use. The only object which knows this is CommandQueue, and so it must be given new copies of these Command objects which it must delete when done.
The objects which create these Command objects are derived from the UIObject base class. This base class forms the heart of all the different types of UI components which VMD provides. For example, the text console UI (UIText), the mouse interface (Mouse), and all the GUI forms (FormsObj and derivations thereof) are derived from UIObject. All the UIObjects, when they are initialized, register with a CommandQueue object, which maintains the list of all UIObjects and can work with them as a group. The UIObject is given a unique ID code when it registers, which it uses to identify later if any actions being done were a result of a request from itself.
Each UIObject basically works with a subset of all the possible Command objects which VMD contains. Typically a UI component displays some graphical feedback or status of the current state of the program, such as displaying via a slider or lighted button what the value of some variable is. When an action is performed the UI components must be informed because this graphical status must be updated to reflect any changes. Any number of different UI components may require such an update, but since the number of Commands which can result in a change to the particular graphical display of each UIObject is much smaller than the total number of available actions, it would be very inefficient to have every UI component notified when each action is performed. Instead, the UIObjects each maintain a list of the integer codes for the Commands in which they are interested. When a Command is executed, the Commandqueue notifies only those UIObjects which have indicated they are interested in the Command. However, a UIObject can create any available Command instance, and give it to the CommandQueue to be executed. When a new Command is created, the ID of the UI which is creating it is also given to the Command, so that later when the UI components are notified of the action they can tell who requested the activity.
The purpose of each UIObject is to provide a means for the user to input commands, and to display to the user the current status of the program. The virtual routine int UIObject::check_event() is called once for each UI during the main execution loop to allow the UI component to check for user events (such as keyboard entries, mouse button presses, or manipulations of GUI components such as buttons or sliders). If such an event is found, a new Command is created for the event (events are simply derived from Command, and contain the data specifying the type of event) and put on the CommandQueue. After all UIObjects are checked for events, the CommandQueue is told to start executing its queued actions, continuing until the queue is empty. When an event action is processed, typically it results in some other form of Command to be requested, which is done by creating the proper special derivationof Command for the action and giving it to the CommandQueue. Eventually all events are processed, and the actions requested by them are then processed, and finally the queue is empty. As each Command is processed the requested action is done and all the UIObjects which expressed an interest in the action are notified, which allows them to update their display. When the queue is empty, VMD proceeds to then redraw the Scene. This execution loop is summarized in section 20.
It is relatively simple to create a new UIObject; each on-screen menu is a separate UIObject as is the mouse, the text console (which almost never needs to be updated due to a command being executed), and the 3D UI. Each UIObject can contain the ability to execute as many or as few actions as is desired. New UIObjects should be new'ed in the VMDinitUI routine, after the and CommandQueue global instance are created.