This section describes how to build and manipulate graphs using the VkGraph class.
Minimally, you must perform the following actions to build and display an ObjectPak graph:
The VkGraph constructor is simple with few arguments. You must provide a name and the parent widget for the graph:
VkGraph(char *name, Widget parent)
The VkGraph destructor destroys the graph. It does not destroy any VkNode objects that are part of the graph.
After you create nodes, you must add them to the graph object you created. Also, if you didn't specify the parent-child relationship for the nodes when you created them, you should supply the remaining connectivity information when adding the nodes to the graph. (See "ViewKit Node Class" for information on creating nodes.)
The VkGraph::add() function adds nodes to a graph object:
virtual int add(VkNode *node)
virtual void add(VkNode *parent, VkNode *child,
char *attribute = NULL)
If you supply only one node pointer as an argument, add() simply adds the node to the graph. If you have already added the node to the graph, add() does nothing.
If you supply two node pointers as arguments, add() adds both nodes to the graph and establishes the first node as the parent of the second node. If you have already added either node to the graph, add() does not add the node again, but it does establish the parent-child relationship between the nodes.
Note: The second form of add() establishes the parent-child relationship between nodes even if one already exists. Thus, it is possible to have more than one connection between nodes. By default, the graph displays only a single arc between connected nodes, even if you define multiple connections between the nodes. However, as described in "Interactive Viewing Features Provided by VkGraph" on page 256, by clicking on the graph's Multiple Arcs button the user can force the graph to an arc for each connection you defined. To turn off multiple-arc display, the user can click again on the Multiple Arcs button.
When specifying a parent/child connection using add(), you can specify an attribute for that connection. An attribute is an arbitrary name that you can use to control the appearance of the arc widget that connects the two nodes. For example, assume that you add two nodes to a graph as follows:
graph->add(parent, child, "primary");
graph->add(parent, child, "secondary");
The resulting graph displays two connecting arcs between the two nodes.
You can now specify X resources to control various aspects of the arc. For example:
*primary*foreground: red
*primary*arcDirection: bidirected
*secondary*foreground: blue
*secondary*arcDirection: undirected
*secondary*style: LineOnOffDash
You can use this method to set many of the resources supported by the SgArc widget. The resources you can specify are: XmNforeground, XmNtoSide, XmNfromSide, XmNfromPosition, XmNtoPosition, XmNarcDirection, XmNfontList, XmNarcWidth, XmNstyle, and XmNdashes. See the SgArc(3) man page for details on these resources.
The following code fragment creates a graph, creates two nodes, establishes a parent-child relationship between the nodes, and adds the nodes to the graph:
graph = new VkGraph("graph", parent);
p_node = new VkNode("parentNode", "Parent");
c1_node = new VkNode("childNode1", p_node, "Child 1");
graph->add(p_node);
graph->add(c1_node);
Note: In this example, the connection between the two nodes is established when you create c1_node. Therefore, you must add the nodes to the graph using separate calls to add().
If, instead of the two separate calls, you execute:
graph->add(p_node, c1_node);
then you not only add the two nodes to the graph, but you establish a second connection between the nodes.
You can accomplish the same result as above by creating the nodes without providing the parent-child relationship, and then specifying the connection when you add the nodes to the graph. The following code fragment is functionally equivalent to that shown above:
graph = new VkGraph("graph", parent);
p_node = new VkNode("parentNode", "Parent");
c1_node = new VkNode("childNode1", "Child 1");
graph->add(p_node, c1_node);
You can remove nodes from a graph using VkGraph::remove():
virtual void remove(VkNode *node, Boolean deleteNode = FALSE)
By default, remove() removes the node from the graph but does not delete it. If you set the deleteNode argument to TRUE, remove() deletes the node when it removes it.
Once you have added all nodes to a graph and specified their connectivity, you must indicate which nodes the graph should display. VkGraph provides many functions that allow you to display or hide all of the graph, individual nodes, and portions of node subtrees.
After displaying nodes, you should call one of the graph layout member functions as described in "Laying Out the Graph" . Otherwise, the nodes might not display in desired locations.
The basic display functions are VkGraph::displayAll() and VkGraph::clearAll():
displayAll() displays all nodes and clearAll() hides all nodes. Typically, after creating your graph, you execute displayAll() to display all of the nodes. For example:
graph->displayAll();
Sometimes you might want to display only portions of your graph. VkGraph provides functions to operate on either single nodes or subtrees of nodes.
The VkGraph::display() function displays a single node:
virtual void display(VkNode *child)
virtual VkNode *display(char *name)
You can provide display() with either a pointer to the node or the component name of the node. If you provide the node's name, this function returns a pointer to the node.
VkGraph::undisplay() hides a single node:
virtual void undisplay(VkNode *node)
virtual void hideNode(VkNode *node)
VkGraph::hideNode() is equivalent to undisplay().
VkGraph also provides a large number of functions that display or hide portions of the graph:
virtual void displayWithParents(VkNode *node)
virtual VkNode *displayWithParents(char *name)
virtual void displayWithAllParents(VkNode *node)
virtual VkNode *displayWithAllParents(char *name)
virtual void hideParents(VkNode *node)
virtual void displayParentsAndChildren(VkNode *node)
virtual VkNode *displayParentsAndChildren(char *name)
virtual void hideParentsAndChildren(VkNode *node)
You can also create your own functions for determining whether or not nodes are displayed and then use the VkGraph::displayIf() function to apply those functions:
virtual void displayIf(VkGraphFilterProc)
The type definition of VkGraphFilterProc is:
typedef Boolean (*VkGraphFilterProc) (VkNode *)
The function you provide must be a static function that accepts a node as an arguments and returns TRUE if the node should be displayed.
Note: displayIf() does not hide (that is, call undisplay()) if the filter function returns FALSE for a node. Therefore, to display only those nodes for which the filter function returns TRUE, you must first call clearAll().
For example, the following function displays only those nodes whose names begin with the string "state":
The final step in displaying a graph is to lay it out. Laying out the graph arranges the widgets in a logical manner and then manages the widgets.
To lay out the entire graph, call the VkGraph::doLayout() function, which applies the layout algorithm to the entire graph and then manages all widgets associated with the graph:
void doLayout()
If you modify the graph after displaying it, or if you allow the user to edit the graph interactively, the graph might become cluttered and you might want to lay out the graph again. To do so you can call doLayout() again to force the graph to reapply the layout algorithm to the graph to clean up the display. As an example, the Realign button provided on the graph command panel simply calls doLayout() whenever the user clicks on the button.
If, after displaying the graph, you display any additional nodes (for example, using the VkGraph::display() function), you must force a layout of the graph to manage all the widgets you created. You can call doLayout() again to do so, but this applies the layout algorithm to the entire graph. Doing so could produce major changes in the layout of the entire graph, which could be disruptive and undesired if the user has previously moved nodes. Also, it could take considerable time if the graph is large. In this case, you can instead call the VkGraph::doSubtreeLayout() function which, given a root node, applies the layout algorithm to just a subtree of the graph:
void doSubtreeLayout(VkNode *node)
For example, the following code fragment illustrates displaying a graph, graph, and then displaying another node, newNode:
VkGraph::doSparseLayout() is a special-purpose build and layout function that displays the relationship between a node and its grandparent nodes even if the node's parents are not displayed:
void doSparseLayout()
doSparseLayout() performs a special build of the graph and whenever it finds a node with an undisplayed parent node, it checks to see whether there are any displayed grandparent nodes. If doSparseLayout() finds such grandparent nodes, it creates a dashed-line arc (instead of a solid-line arc) to connect the node and its grandparent nodes. After finishing the build process, doSparseLayout() performs a layout of the entire graph and manages all widgets associated with the graph.
So far, this chapter discussed creating tree graphs using the VkGraph class. However, VkGraph also supports butterfly graphs, which display only a central node and its immediate parent and child nodes. The central node of a butterfly graph is called the butterfly node.
VkGraph can construct a butterfly graph from any graph specification. Call VkGraph::displayButterfly() to specify one node as the butterfly node; VkGraph automatically determines which nodes to display:
Then call VkGraph::doLayout() to lay out the graph as you normally would. For example, assuming that you have already defined a graph specification for a graph called graph, the following code fragment would instruct the graph object to display a butterfly graph centered on the node centerNode:
After displaying a butterfly graph, you can use displayButterfly() to specify a new butterfly node and display a different butterfly graph given the same graph specification. For example, the following code fragment illustrates setting a new butterfly node, newCenter, after displaying the butterfly graph in the example above:
After displaying a butterfly graph, you can return to displaying a normal tree graph by setting the layout style to XmGRAPH using the VkGraph::setLayoutStyle() function:
virtual void setLayoutStyle(char type)
For example, the following code fragment illustrates displaying the entire graph specified by graph after displaying the butterfly graphs above:
As discussed in "Interactive Viewing Features Provided by VkGraph" , by clicking on the Graph Overview button in the graph command panel, a user can display an overview of all a graph's visible nodes.
You can also display the overview window programmatically using VkGraph::showOverview():
void showOverview()
Call VkGraph::hideOverview() to programmatically hide the overview window:
void hideOverview()
You can obtain a pointer to the overview window's VkWindow object using VkGraph::overviewWindow():
VkWindow *overviewWindow()
VkGraph provides the following utility functions:
virtual void setZoomOption(int index)
void sortAll()
typedef void (*VkGraphNodeProc) (VkNode *)
The function you provide must be a static function that accepts a node as an arguments and has a void return value.
virtual void forAllNodesDo(VkGraphNodeProc function)
void makeNodeVisible(VkNode *node)
void saveToFile()
void setSize(int entries)
VkGraph provides the following access functions for obtaining values associated with the graph:
int numNodes()
VkNode *find(char *name)
Widget graphWidget()
Widget workArea()
Widget twinsButton()
Widget relayButton()
Widget reorientButton()
Occasionally, after displaying one graph, you might want to display an entirely different graph. The simplest method of accomplishing this is to create another VkGraph object for the new graph.
However, creating a new graph object entails the overhead of creating many new widgets and data structures. Sometimes it is simpler, faster, and more appropriate to re-use the existing graph object. For example, consider a window in which you are displaying a graph of C++ class hierarchies associated with a program. The window might contain controls that allow the user to select other programs to examine. If the user selects a new program to examine, the most convenient thing to do would be to keep the existing graph object but "clear it" of all existing information.
The VkGraph::tearDownGraph() function provides this ability:
virtual void tearDownGraph()
It tears down the graph by destroying all arc and node widgets and deleting all VkNode objects associated with the graph. This function is equivalent to deleting all VkNode objects associated with the graph, deleting the graph object, and creating a new graph object with the same name, but entails less overhead processing than if you were to explicitly perform these actions separately.
The VkGraph class declares two ObjectPak member function callbacks and activates the VkGraph::arcCreatedCallback whenever the graph creates a SgArc widget to connect two nodes. The arcCreatedCallback callback includes as call data the newly created SgArc widget. See the SgArc(3) reference pages for information on the SgArc widget.
VkGraph activates the VkGraph::arcDestroyedCallback whenever the graph destroys all arc widgets as a result of a call to VkGraph::clearAll() (see "Indicating Display Nodes" ). VkGraph activates the arcDestroyedCallback callback once for every arc destroyed, including as call data the SgArc widget destroyed. See the SgArc(3) reference pages for information on the SgArc widget.
VkGraph sets several X resources that specify the labels of its popup menus. You can override these values in an app-defaults file if you want to provide your own labels. The resources and their default values are:
VkGraph provides much of the functionality required for displaying and manipulating graphs. In most other cases, you can use the graphWidget() access function to obtain a pointer to the SgGraph widget for direct operations on the widget.
However, you might want to perform additional processing when certain actions occur. You can create a subclass of VkGraph which provides several hook" functions that you can override to implement additional functionality:
virtual void buildCmdPanel(Widget parent)
virtual void buildZoomMenu(Widget parent)
virtual void addMenuItems(VkPopupMenu *menu)
You can override this function if you want to change its behavior or support any additional menu items that you added by overriding addMenuItems().
virtual void popupMenu(VkNode *node, XEvent *event)
virtual void addDesktopMenuItems(VkPopupMenu *menu)
virtual void twinsVisibleHook(Boolean state)