Recursive layout
Describes Recursive Layout.
Describes classes associated with recursive layout with a diagram.
Describes the features of the layout.
Describes the generic features and parameters of the layout.
Describes the modes available in this layout.
Describes how to access all sublayouts through recursive layout.
Describes how to access all sublayouts through recursive layout.
Explains some mechanisms that are available for advanced users.
Describes the way to use the default layout provider.
Overview of recursive layout
This layout feature is implemented by the class
IlvRecursiveLayout from the package
ilog.views.graphlayout.recursive. The
IlvRecursiveLayout class is internally used by the graph layout renderer of a diagram component; in Rogue Wave®
JViews Diagrammer, the rendering mechanism is transparent so that you never need to deal with this class.
Important The Recursive Layout can be used only in Java™ code. No CSS syntax is available for this layout.
The Recursive Layout class is not a layout algorithm but rather a facility to apply another layout algorithm recursively on a nested graph. It traverses the nesting structure starting from the graph that is attached to the Recursive Layout itself and recursively applies a layout on all subgraphs. You can tailor which sublayout must be applied to which subgraph.
There are basically two scenarios:
The same layout style must be applied to all subgraphs.
An individual layout style must be applied to each subgraph.
The class IlvRecursiveLayout which manages sublayouts for nested graphs
Code sample: same layout style everywhere
This sample assumes that you want to apply a Tree Layout to a nested graph and that each subgraph should be laid out with the same global layout parameters.
The Tree Layout algorithm handles only flat graphs, that is, if applied to an attached graph, it lays out only the nodes and links of the attached graph, but not the nodes and links of the subgraphs that are nested inside the attached graph. Hence the Tree Layout must be encapsulated in a Recursive Layout.
The Recursive Layout traverses the entire nesting hierarchy of the attached graph, while the encapsulated Tree Layout lays out each (flat) subgraph of the nesting hierarchy during the traversal.
Java code sample
...
import ilog.views.*;
import ilog.views.graphlayout;
import ilog.views.graphlayout.recursive.*;
import ilog.views.graphlayout.tree.*;
IlvRecursiveLayout layout = new IlvRecursiveLayout(IlvTreeLayout());
IlvGrapher topLevelGrapher = ...
layout.attach(topLevelGrapher);
try {
IlvRecursiveLayoutReport layoutReport =
(IlvRecursiveLayoutReport)layout.performLayout();
int code = layoutReport.getCode();
System.out.println("Layout completed (" +
layoutReport.codeToString(code) + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// detach the Recursive Layout when it is no longer needed
layout.detach();
...
This mode of the Recursive Layout is called reference layout mode. In this case, a Tree Layout is performed recursively on the top-level graph and on each subgraph. All layouts are performed with the same global layout parameters.
NOTE The term “global layout parameter” applies to the parameters that do not depend on a specific node or link. For example, Tree Layout has a global layout parameter set by setGlobalLinkStyle, as well as a layout parameter set by setLinkStyle(link, style) which is local to a link.
You can change the global layout parameters by accessing the reference layout of the Recursive Layout:
IlvTreeLayout treeLayout = (IlvTreeLayout)layout.getReferenceLayout();
treeLayout.setFlowDirection(IlvDirection.Left);
Technically, the reference layout instance is not applied to each subgraph because each subgraph needs an individual layout instance. The reference layout instance is only applied to the top-level graph. Furthermore, a clone of the reference instance is created for each subgraph. This clone remains attached to the subgraph as long as the Recursive Layout is attached to the top-level graph. Before layout is performed, the global layout parameters are copied from the reference layout instance to each cloned layout instance.
Sometimes, you want to specify local layout parameters for individual nodes and links. In this case, you need to access the cloned layout instance that is attached to the subgraph that owns the node or link. For example, for the link style of an individual link, use:
IlvTreeLayout treeLayout =
(IlvTreeLayout)layout.getLayout(link.getGraphicBag());
treeLayout.setLinkStyle(link, IlvTreeLayout.ORTHOGONAL_STYLE);
You cannot use the reference layout mode in the following cases:
The layout algorithm to be applied on subgraphs is not the same as the algorithm needed for the topmost graph (the reference layout).
The same layout algorithm, but using different global parameter settings, needs to be applied on different subgraphs.
In these cases, you can use one of the other modes.
Java code sample: Mixing different layout styles
The following example shows the second scenario: Each subgraph should be laid out by a different layout style or with individual global layout parameters. In this case, you use the internal provider mode of the Recursive Layout.
Assume that you have a graph with three subgraphs. The top-level graph and the first subgraph should be processed with Tree Layout, the second subgraph with Bus Layout, and the third subgraph with Grid Layout. You have to specify which layout must be used for which subgraph, and then you can perform the layout.
...
import ilog.views.*;
import ilog.views.graphlayout.*;
import ilog.views.graphlayout.recursive.*;
import ilog.views.graphlayout.tree.*;
import ilog.views.graphlayout.bus.*;
import ilog.views.graphlayout.grid.*;
IlvRecursiveLayout layout = new IlvRecursiveLayout();
IlvGrapher topLevelGrapher = ...
layout.attach(topLevelGrapher);
// specify the layout of the top level graph
layout.setLayout(null, new IlvTreeLayout());
// specify the layout of subgraphs
layout.setLayout(subgraph1, new IlvTreeLayout());
layout.setLayout(subgraph2, new IlvBusLayout());
layout.setLayout(subgraph3, new IlvGridLayout());
// perform layout
try {
IlvRecursiveLayoutReport layoutReport =
(IlvRecursiveLayoutReport)layout.performLayout();
int code = layoutReport.getCode();
System.out.println("Layout completed (" +
layoutReport.codeToString(code) + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// detach the Recursive Layout when it is no longer needed
layout.detach();
...
In this scenario, there is no reference layout. All layout parameters of different subgraphs are independent. You need to specify new, independent layout instances for each subgraph; otherwise no layout will be performed for the corresponding subgraph. The layout instances are attached to the subgraph as long as the Recursive Layout is attached to the top-level graph. You can specify in this example different global layout parameters for the Tree Layout of the top-level graph and the Tree Layout of subgraph1. You access the layout instance of each individual subgraph to change global layout parameters for this subgraph as well as parameters of nodes and links of the subgraph. For instance if node1 belongs to subgraph1 and node2 belongs to subgraph2, you can set individual global and local layout parameters in this way:
// access the layout of the top level graph
IlvTreeLayout treeLayout1 = (IlvTreeLayout)layout.getLayout(null);
treeLayout1.setFlowDirection(IlvDirection.Bottom);
// access the layouts of the subgraphs
IlvTreeLayout treeLayout2 = (IlvTreeLayout)layout.getLayout(subgraph1);
treeLayout2.setFlowDirection(IlvDirection.Left);
treeLayout2.setAlignment(node1, IlvTreeLayout.TIP_OVER);
IlvBusLayout busLayout = (IlvBusLayout)layout.getLayout(subgraph2);
busLayout.setOrdering(IlvBusLayout.ORDER_BY_HEIGHT);
busLayout.setBus(node2);
IlvGridLayout gridLayout = (IlvGridLayout)layout.getLayout(subgraph3);
gridLayout.setLayoutMode(IlvGridLayout.TILE_TO_COLUMNS);
Java code sample: Using a specified layout provider
The Rogue Wave JViews Diagrammer Graph Layout library provides a flexible mechanism for the choice of the layout instance to be applied to each subgraph in a nested graph: the layout provider. In the previous example, a layout provider was used internally. For simplicity, the details of the mechanism are hidden, and you select the choice of layout by using the method setLayout on the Recursive Layout instance. Therefore, this layout mode is called internal provider mode.
You can also design your own layout provider and use it inside the Recursive Layout. This is the specified provider mode of the Recursive Layout.
A layout provider is a class that implements the interface IlvLayoutProvider. The interface has a single method:
getGraphLayout(IlvGraphModel graphModel)
This method must return the layout instance to be used for the graph model passed as the argument, or null if no layout is required for this graph. When performing the Recursive Layout, these methods get the layout instance to be used for each graph from the specified layout provider.
To implement the interface IlvLayoutProvider, you must decide how the choice of the layout instance is done. This can be based on some criteria such as the type of graph (eventually known in advance), or a choice already made by the user and recorded, for example, in a property of the graph. A possible implementation of the getGraphLayout method is:
public IlvGraphLayout getGraphLayout(IlvGraphModel graphModel)
{
Object prop = graphModel.getProperty("layout type");
// if none, return null (no layout needed for this graph)
if (!(prop instanceof String))
return null;
IlvGraphLayout layout = null;
String name = (String)prop;
if (name.equals("tree"))
layout = new IlvTreeLayout();
else if (name.equals("flow"))
layout = new IlvHierarchicalLayout();
else
throw new RuntimeException("unsupported layout choice: " + name);
layout.attach(graphModel);
return layout;
}
This implementation is only an example among many possible implementations. The implementation may decide to store the newly allocated layout instance to avoid allocating a new one when the method is again called for the same graph.
If you have implemented a layout provider, you can use it in the Recursive Layout in the following way:
..
import ilog.views.*;
import ilog.views.graphlayout.*;
import ilog.views.graphlayout.recursive.*;
IlvLayoutProvider layoutProvider = ...
IlvRecursiveLayout layout = new IlvRecursiveLayout(layoutProvider);
IlvGrapher topLevelGrapher = ...
layout.attach(topLevelGrapher);
// Perform the layout
try {
IlvRecursiveLayoutReport layoutReport =
(IlvRecursiveLayoutReport)layout.performLayout();
int code = layoutReport.getCode();
System.out.println("Layout completed (" +
layoutReport.codeToString(code) + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// detach the Recursive Layout when it is no longer needed
layout.detach();
...
Features
This subclass of
IlvGraphLayout is not a usual layout algorithm but rather a facility to manage the layout of a nested grapher.
Three layout modes: reference layout mode, internal provider mode, and specified provider mode.
By using this class you can perform a layout algorithm recursively in a nested grapher.
By using this class you can perform a recursive layout on a nested grapher while each subgrapher uses an individual layout style.
Layout features, speed, and quality depend on the features, speed, and quality of the sublayouts.
Generic features and parameters
Depending on the support of its sublayouts, Recursive Layout may support the following generic parameters defined in the
IlvGraphLayout class (see
Generic parameters and features):
The following paragraphs describe the particular way in which these parameters are used by this subclass.
Allowed time
The Recursive Layout can stop the entire layout of a nested graph after a certain amount of time. If the allowed time setting has elapsed, the Recursive Layout stops; that means it stops the currently running layout of a subgraph and skips the subsequent layouts of subgraphs that have not yet been started. If at the stop time point a sublayout is running on a subgraph that does not support the “allowed time” feature, then this sublayout first runs to completion before the Recursive Layout is stopped. If the Recursive Layout stops early because the allowed time has elapsed, the result code in the layout report is:
IlvGraphLayoutReport.STOPPED_AND_INVALID
Percentage completion calculation
The Recursive Layout calculates the percentage of completion. This value can be obtained from the layout report during the run of the layout. The value is a very rough estimation. If the layouts on the subgraphs do not support the calculation of the percentage completion, the Recursive Layout can report the percentage based only on the information of how many layouts of subgraphs are already finished. For example, if the entire nesting structure contains five nested graphs, the mechanism reports 20% after the layout of the first subgraph has finished, 40% after the layout of the second subgraph has finished, and so on. If the layouts of the subgraphs support the calculation of the percentage completion, the Recursive Layout calculates a more detailed percentage. In most cases, the calculated percentage is only a very rough estimation that does not always grow linearly over time.
For a detailed description of this feature, see
Percentage of completion calculation and
Listener layout.
Save parameters to named properties
The Recursive Layout instance can save its layout parameters into named properties if all its sublayouts support this feature. This can be used to save layout parameters to
.ivl files. (For a detailed description of this feature, see
Percentage of completion calculationand
Saving layout parameters and preferred layouts.)
Stop immediately
The Recursive Layout can be stopped at any time.
It stops the currently running layout of a subgraph after cleanup if the method
stopImmediately is called and skips the subsequent layouts of subgraphs that have not yet been started.
If at the stop time point a sublayout is running on a subgraph that does not support the “stop immediately” feature, then this sublayout first runs to completion before the Recursive Layout is stopped.
For a description of this method in the IlvGraphLayout class, see
Stop immediately. If the layout stops before completion, the result code in the layout report is
IlvGraphLayoutReport.STOPPED_AND_INVALID.
Recursive layout modes
Describes the modes available in this layout.
Describes the modes available in this layout.
Describes the reference layout mode for a nested graph.
Describes the internal provider mode for a nested graph.
Describes the specified provider mode for a nested graph.
Overview of recursive layout modes
The Recursive Layout has three different layout modes:
The layout mode is determined by the constructor that you use. The way to set global layout parameters of the sublayouts that are applied to the subgraphs is slightly different for each layout mode. You can query the current layout mode by using:
int getLayoutMode()
The possible return values are:
IlvRecursiveLayout.REFERENCE_LAYOUT_MODE: The same layout style with the same global layout parameters is applied to all subgraphs of the nested graph.
IlvRecursiveLayout.INTERNAL_PROVIDER_MODE: The layout is applied using an internal recursive layout provider. The layout styles of individual subgraphs can be specified by using the method
setLayout.
IlvRecursiveLayout.SPECIFIED_PROVIDER_MODE: The layout is applied using an explicitly specified layout provider.
Reference layout mode
Use this mode if you want to apply the same layout style with the same global layout parameters to the entire nested graph.
You first need to allocate the reference layout that is a new instance of any graph layout algorithm (except IlvRecursiveLayout ) that should be applied to all subgraphs of the nested graph.
Then you allocate the Recursive Layout using the constructor with the reference layout as argument
IlvRecursiveLayout(IlvGraphLayout referenceLayout)
The reference layout is internally used only for the top-level graph of the nested graph. Clones of the reference layout are used for the subgraphs. Therefore, all subgraphs are laid out with the same global layout parameters.
To change the global layout parameters, you can access the reference layout by:
IlvGraphLayout getReferenceLayout()
Global layout parameters are those parameters that are independent from specific nodes or links. Other layout parameters are local to specific nodes or links.
For example, in IlvHierarchicalLayout, the method setGlobalLinkStyle(style) is a global layout parameter, while the method setLinkStyle(link,style) is a local layout parameter.
If you want to set layout parameters that are local to an individual node or link, you must access the particular clone of the reference layout that is responsible for the subgraph that owns the node or link.
After you attach the Recursive Layout to the top-level grapher or graph model, you can retrieve the layout instance of a specific subgraph by:
IlvGraphLayout getLayout(Object subgraph)
In reference layout mode, it makes no sense to modify any global layout parameter on the returned instance. The global layout parameters are always taken from the reference layout only. If you pass null as subgraph, you get the layout instance of the top-level graph. This is actually the same layout instance as the reference layout.
The reference layout and its clones used during recursive layout remain attached to the subgraphs (or the graph models of the subgraphs, respectively) as long as the Recursive Layout itself is attached. When detaching the Recursive Layout, all layouts of subgraphs are automatically detached as well.
Internal provider mode
Use this mode if you want to perform graph layout on a nested graph and either you need individual global layout parameters for specific subgraphs or you want to lay out different subgraphs with different styles. In this case, there is no reference layout. You allocate the Recursive Layout by calling the constructor with no arguments:
IlvRecursiveLayout()
Before you can perform a layout, you need to specify which layout is used for which subgraph. First, you should attach the Recursive Layout to a graph. Then, to specify the layout of the top-level graph, call:
recursiveLayout.setLayout(null, sublayout);
To specify the layout of a specific subgraph, call
recursiveLayout.setLayout(subgraph, sublayout);
It is important that you assign a different layout instance for each subgraph. You cannot share the same layout instance among different subgraphs. You should allocate a new, fresh layout instance for each subgraph. If you pass null as sublayout, then no layout is performed for this particular subgraph.
To set the layout for a subgraph and recursively for all its subgraphs, you can use:
setLayout(Object subgraph, IlvGraphLayout layout, boolean traverse)
and pass the true argument for the traverse flag. This sets the layouts to a clone of the input layout for each subgraph starting at the input subgraph.
Internally, the Recursive Layout uses a layout provider of type IlvRecursiveLayoutProvider. You can access the current layout provider by:
IlvLayoutProvider getLayoutProvider()
In internal provider mode, it is mostly not necessary to manipulate the layout provider directly.
Because there is no reference layout, global layout parameters are independent for each subgraph. Global and local layout parameters can be set by accessing the particular layout instance that is assigned to a specific subgraph.
After you attach the Recursive Layout to the top-level grapher or graph model, you can retrieve the layout instance of a specific subgraph by:
IlvGraphLayout getLayout(Object subgraph)
If you pass null as subgraph, you get the layout instance of the top-level graph.
The layout instances of the subgraphs used during recursive layout remain attached to the subgraphs (or the graph models of the subgraphs, respectively) as long as the Recursive Layout itself is attached. When you detach the Recursive Layout, all layouts of subgraphs are automatically detached as well.
Specified provider mode
The specified provider mode can be used if you want to perform graph layout on a nested graph and either you want individual global layout parameters for specific subgraphs or you want to lay out different subgraphs with different styles. It is your responsibility to manage the specified layout provider (unlike the case with the internal provider mode), but this is probably only necessary in very advanced applications.
Specified provider mode has no reference layout. You allocate the Recursive Layout by using the constructor with your layout provider as argument:
IlvRecursiveLayout(IlvLayoutProvider specifiedProvider)
You can access the current layout provider by:
IlvLayoutProvider getLayoutProvider()
You should implement your layout provider in such a way that it delivers a different layout instance for each subgraph. The delivered layout instance must be attached to the graph model of the corresponding subgraph.
Because there is no reference layout, global layout parameters are independent for each subgraph. It is recommended that the implementation of the layout provider takes care of the setting of global and local layout parameters. Theoretically, you can use the method:
IlvGraphLayout getLayout(Object subgraph)
which will return the layout instance that the specified layout provider delivers for the graph model of the input subgraph. If you pass null as subgraph, you get the layout instance of the top-level graph. The effect of this method depends on the implementation of the layout provider that was passed to the constructor of Recursive Layout.
The layout instances of the subgraphs used during recursive layout should be attached by the layout provider. They are usually not automatically detached when the Recursive Layout is detached.
Unless you use one of the predefined providers of class IlvDefaultLayoutProvider or IlvRecursiveLayoutProvider, you should traverse all layouts and detach them explicitly.
Accessing all sublayouts
When the Recursive Layout is attached, you can conveniently access all layouts that will be used during layout. This works in all layout modes:
Enumeration getLayouts(boolean preOrder)
As explained in
Internal mechanism, the
getLayouts method returns an enumeration of instances of
IlvGraphLayout. If the
preOrder flag is
true, the layout of the parent graph occurs before the layout of its child nodes in the enumeration.
If the preorder flag is false, the layout of the parent graph occurs after the layout of its child nodes. For example, in the graph in the following figure, the call getLayouts(true) returns the layouts for the subgraphs in this order: L1, L1.1, L1.1.1, L1.1.2, L1.2; the call getLayouts(false) returns the layouts for the subgraphs in this order: L1.1.1, L1.1.2, L1.1, L1.2, L1.
Nesting structure in a graph
NOTE In specified provider mode, the enumeration returned by getLayouts contains the instances that are delivered by the specified provider. If the specified provider returns a different instance in each call of getGraphLayout(IlvGraphModel), then the enumeration does not contain the instances that are later used during layout. Hence it is recommended to use layout providers that store the layout instances internally and return the same instance for the same graph model. The predefines IlvDefaultLayoutProvider and IlvRecursiveLayoutProvider store the layout instances internally.
Convenience method for setting reference layout mode
The class IlvGraphLayout contains a convenience method. To perform a recursive layout recursively, you can use:
int performLayout(boolean force, boolean redraw, boolean traverse)
If the traverse flag is true, it traverses the nested graph and performs the layout on each subgraph. This is a shortcut for the reference mode of Recursive Layout. The statement:
flatLayout.performLayout(force, redraw, true);
is equivalent to creating a Recursive Layout in reference mode that uses the flatLayout as reference layout:
IlvRecursiveLayout recursiveLayout = new IlvRecursiveLayout(flatLayout);
recursiveLayout.performLayout(force, redraw);
Specific parameters
Besides some expert parameters, Recursive Layout does not provide any specific layout parameters. You can set specific layout parameters of the sublayouts individually by accessing them via getLayout(Object):
IlvGraphLayout sublayout = recursiveLayout.getLayout(subgraph);
sublayout.setParameter(parameter);
Recursive Layout has some convenient methods that automatically traverse the nested graph recursively and set the corresponding parameter at each sublayout of a subgraph that supports this parameter. This works well particularly in reference layout mode. In internal or specified provider mode, it takes only the current nesting structure into account. If you change the specific layout of a subgraph or the nesting structure (for example, by adding a new subgraph) after using such a convenience method, the new layout of the new subgraph usually has a different setting, so you may need to apply the convenience method again.
The following methods traverse the nested graph recursively and set the corresponding parameter on all sublayouts (see
Generic parameters and features and
Using advanced features for details):
setCoordinatesMode(int mode) void setUseDefaultParameters(boolean option) void setMinBusyTime(long time) void setInputCheckEnabled(boolean enable) void propagateLayoutOfConnectedComponentsEnabled(boolean enable) void propagateLayoutOfConnectedComponents(IlvGraphLayout layout) void propagateLinkConnectionBoxInterface(IlvLinkConnectionBoxInterface linkConnectionBoxInterface) propagateLinkClipInterface(IlvLinkClipInterface linkClipInterface) void checkAppropriateLinks() void setLinkCheckEnabled(boolean enable) void setConnectionPointCheckEnabled(boolean enable) There is a generic propagation mechanism for setting any parameter which is implemented by reflection. For example, the following call traverses the nested graph recursively, checks for each sublayout using introspection whether a method called setFlowDirection exists, and passes the input value direction to this method. As a result, all sublayouts that have a flow direction parameter will use the same flow direction, while the layout parameters of those layouts that do not have a flow direction remain unchanged:
int code = recursiveLayout.propagateLayoutParameter("flowDirection",
null, direction);
The second argument of propagateLayoutParameter can be used to select only specific layout classes. The call
int code = recursiveLayout.propagateLayoutParameter("flowDirection",
IlvHierarchicalLayout.class, direction);
propagates the flow direction only to all those sublayouts that are instances of IlvHierarchicalLayout. For example if a subgraph uses a Tree Layout, its flow direction remains unchanged in this case, even though IlvTreeLayout has a method setFlowDirection.
The return code of the propagation method indicates whether setting the parameter has been successful. It is a bitwise-Or combination of the following bit masks:
IlvRecursiveLayout.PROPAGATE_PARAMETER_SET - the parameter was successfully applied at some layout instance of a subgraph.
IlvRecursiveLayout.PROPAGATE_PARAMETER_AMBIGUOUS - the method to set the parameter could not uniquely be determined at some layout instance, because there were many methods with the same name, which creates an unresolvable ambiguity. In this case, an arbitrary method is chosen among the ambiguous methods.
IlvRecursiveLayout.PROPAGATE_CLASS_MISMATCH - the parameter was not applied at some layout instance of a subgraph because the layout instance did not match the specified layout class. This can happen only when a non-null layout class is specified as the second parameter of the method
propagateLayoutParameter.
IlvRecursiveLayout.PROPAGATE_PARAMETER_MISMATCH - the parameter was not applied at some layout instance of a subgraph because no matching method with appropriate argument types was found via reflection, or because the security manager of the Java™ Virtual Machine did not allow reflection.
IlvRecursiveLayout.PROPAGATE_EXCEPTION - the method to set the parameter was applied but threw an exception at some layout instance of a subgraph.
For further details about the propagation mechanism, see the class
IlvRecursiveLayout IlvRecursiveLayout in the
Java API Reference Manual.
For experts: mechanisms for advanced users
The mechanisms available for advanced users are:
Subgraph correction
In Rogue Wave JViews, the position of a subgrapher (instance of
IlvGrapher) is always calculated from the positions of the contents of the subgrapher. The position of the subgrapher is simply the position of the bounding box around its contents. This mechanism has side effects when performing layout: a subgraph will never appear fixed if its contents is laid out, because when a layout is applied to the contents of the subgraph, the bounding box, hence the position, of the subgraph changes. Depending on the applications, this may be an unwanted effect.
Normally, after the subgraph is laid out, its parent graph gets laid out, which eventually moves the entire subgraph to its final position. Therefore, in most cases, the unwanted position of the subgraph is only temporary and you can ignore the entire problem. However, in a few situations, you need to be aware of the effect, namely:
if the subgraph is specified as fixed (for instance, by a call to the method
setFixed ) and hence should not move;
if the parent graph is never laid out;
if the parent graph is laid out in a layout style with incremental mode on, which analyzes the positions of the nodes (for instance in Tree Layout and Hierarchical Layout).
In all these situations, it is important that the subgraph remains at the old position even after its contents has been laid out. The implementation of
IlvGrapher does not behave like this.
The solution is simply to move the subgraph back to its old position immediately after the subgraph has been laid out, just before the layout of its parent graph is started. The Recursive Layout allows you to install a subgraph correction interface that contains a correct method which is called exactly at this point. You install a subgraph correction interface in the following way:
layout.setSubgraphCorrectionInterface(
new IlvSubgraphCorrectionBarycenterFixed());
Effect of subgraph correction illustrates the effect of subgraph correction.
Two default implementations of
IlvSubgraphCorrectionInterface are available:
IlvSubgraphCorrectionBarycenterFixed corrects the subgraph so that after its contents has been laid out, the center of the subgraph remains the same. However, the size of the subgraph bounding box may change due to the contents layout.
IlvSubgraphCorrectionBoundsFixed corrects the subgraph so that after its contents has been laid out, the bounding box of the subgraph remains the same. However, to achieve this, the zoom level of the subgraph is changed.
These implementations of the subgraph correction interface do not correct the top-level graph, but only the nested subgraphs The instances can be shared between different instances of
IlvRecursiveLayout.
Effect of subgraph correction
Listener layout
Event listener layout is an advanced feature documented in
Using event listeners. You need to understand that general description and the concept of layout listeners before you read this section. This section describes some specific details of the Recursive Layout related to layout listeners.
The application can listen for layout events sent by the Recursive Layout or by each sublayout individually. For example, a progress bar that displays the progress of the entire nested layout should listen for the layout events fired by the Recursive Layout itself, while an application that wants to detect when a specific sublayout of a subgraph is started or stopped should listen for the layout events sent by that particular sublayout.
To install a layout event listener at the Recursive Layout, call usually:
recursiveLayout.addGraphLayoutEventListener(listener);
To install a layout listener that receives the layout events of all sublayouts of the Recursive Layout, you can call:
recursiveLayout.addSubLayoutEventListener(listener);
Note that in this case, the listener is installed at the Recursive Layout instance (not at the sublayout instances) but receives the events from the sublayouts (not from the Recursive Layout). An internal mechanism makes sure that the events are forwarded to the listener.
Alternatively, you could traverse the nesting structure and install the same listener at all subgraph layouts. However, this would have two disadvantages: it requires more memory and you need to reinstall or update the listener whenever you change the layout of a subgraph or the nesting structure by adding or removing subgraphs. When you use addSubLayoutEventListener, updating the listener is not necessary in this case.
For experts: more on layout providers
For information on using the Recursive Layout with a specified layout provider, see
Specified provider mode.
The library provides a default implementation of the interface
IlvLayoutProvider, named
IlvDefaultLayoutProvider. In many cases, it is simpler either to use this class as is, or to subclass it, rather than directly implementing the interface.
The class
IlvDefaultLayoutProvider allows you to set the layout instance to be used for each graph (called the
preferred layout) with the method:
setPreferredLayout(IlvGraphModel graphModel, IlvGraphLayout layout, boolean
detachPrevious)
The layout instance specified as the preferred layout is stored in a property of the graph. The current preferred layout is returned by the method:
getPreferredLayout(IlvGraphModel graphModel)
The method returns null if no layout has been specified for this graph.
When the method getGraphLayout is called on the default provider, the previously specified preferred layout is returned, if any. Otherwise, a new layout instance is allocated by a call to the method:
createGraphLayout(IlvGraphModel graphModel)
This newly created layout is recorded as the preferred layout of this graph, which is attached to the layout instance.
When a preferred layout has been specified for a graph, the default implementation of the method createGraphLayout copies the layout instance that is the preferred layout of the nearest parent graph. Therefore, if a preferred layout L is specified for a graph G and no preferred layout is set for its subgraphs, then the graph G and all its subgraphs are laid out using the same layout algorithm L (copies of it are used for the subgraphs).
NOTE You must call the method
detachLayouts when you no longer need the layout provider instance; otherwise, the garbage collector may fail to remove some objects.
The settings of the preferred layout made using the class
IlvDefaultLayoutProvider can be saved in
.ivl files. For details, see
Saving layout parameters and preferred layouts.
Java code sample
The following Java™ code sample illustrates the use of the class
IlvDefaultLayoutProvider.
...
IlvGrapher grapherA = new IlvGrapher();
IlvGrapher grapherB = new IlvGrapher();
// Fill the graphers with nodes and links;
// grapherB is added as a subgraph of grapherA
grapherA.addNode(grapherB, false);
// Create a grapher adapter for the topmost graph
IlvGrapherAdapter adapterA = new IlvGrapherAdapter(grapherA);
// Get a grapher adapter for the subgraph
IlvGraphModel adapterB = adapterA.getGraphModel(grapherB);
// Create the layout provider
IlvDefaultLayoutProvider provider = new IlvDefaultLayoutProvider();
// Specify the preferred layouts for each grapher
// (this automatically attaches the layouts)
provider.setPreferredLayout(adapterA, new IlvTreeLayout());
provider.setPreferredLayout(adapterB, new IlvGridLayout());
// Create a recursive layout in specified provider mode
IlvRecursiveLayout layout = new IlvRecursiveLayout(provider);
// Perform the layout
try {
IlvRecursiveLayoutReport layoutReport =
(IlvRecursiveLayoutReport)layout.performLayout();
int code = layoutReport.getCode();
System.out.println("Layout completed (" +
layoutReport.codeToString(code) + ")");
}
catch (IlvGraphLayoutException e) {
System.err.println(e.getMessage());
}
...
// detach the layouts when the provider is no longer needed
provider.detachLayouts(adapterA, true);
// dispose the topmost adapter when no longer needed
adapterA.dispose();
...
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.