2. The Basics

a. What is a reference frame?

The Cool Modes plug-ins are called reference frames. So what exactly is a reference frame in the Cool Modes context?
Speaking of a reference frame, the plug-in as a whole, it comprises various Java classes of nodes and edges that logically belong together. All these classes must be part of a Java package that can be located anywhere in the file system, since Cool Modes can scan the file system for new reference frames. In our example reference frame we will develop in the course of this HOWTO, all the Java class files of our nodes and edges (including their models and views) belong to the package myplugins.calc.

Apart from the package containing the nodes' and edges' classes, a Cool Modes plug-in needs a ReferenceFrame and may have a Palette class that implements the ReferenceFrame and the Palette interface. In this tutorial, we will develop the classes myplugins.calc.CalcRefFrame mypalettes.calc.CalcPalette.

The ReferenceFrame class is meant as a controller for the plug-in. It provides initialization, updating and disposing, as well as handling of EdgeRules, FordiddenCycleRules and PackagePrefixes, to complement the classpath. The palette object is a property of this class.

The palette class should basically provide the graphical user interface. It normally consists of a JGraph including one instance of each type of node that belongs to the plug-in's package. Via drag-and-drop, a new instance of such a node can be created in a workspace.
The edges, on the other hand, are not part of the JGraph in the palette. Instead, for each type of edge belonging to a plug-in there might be a toggle button to make Cool Modes change into edge mode so that the selected type of edge can be drawn between two nodes in a workspace. You will learn more about Cool Modes' various kinds of modes below.
Note that it is not necessary to build a palette class, because a standarized user interface can be generated by xml. How this can be done is for now not part of this how-to, but will soon be implemented. Moreover it is possible to develop reference frames with various functions that not need to have a graphical user-interface, and therefor no palette.

b. The modes

A couple of methods in the AbstractPalette class and also in the palette classes you will derive from it deal with changing or getting information on the mode Cool Modes is currently in. Because of this, a short excursus is necessary.

Basically, Cool Modes knows exactly three different kinds of modes:

Moreover, it is possible to set once mode. More about this below where the edge mode is explained.

Generally, switching between modes depends on the currently active reference frame. How this can be done is explained in the Step by step chapter.

Node mode

In node mode the user can drag and drop nodes or manipulate them in some other ways. Usually, a palette sets Cool Modes to node mode when it is selected. This is done in the method enterPalette() and is actually only one line. Below is the enterPalette() method taken from our example palette CalcPalette which we will develop later in this tutorial:

public void enterPalette() {
    toNodeMode();
}
 					 

Edge mode

Actually, there is more than just one edge mode. Cool Modes changes to edge mode if the user activates one of the toggle buttons corresponding to one type of edge or to the special case of deleting an edge. So, while switching to this mode, information on the selected edge type is given as a parameter of the appropriate method. A special case is the delete edge mode, enabling the user to delete edges between two nodes in the workspace by drawing from one node to the other as if creating a new edge.
If the user clicks on an activated toggle button, Cool Modes leaves edge mode and usually switches to node mode. This depends on what the active palette's actionPerformed() method looks like.

If the once checkbox above the toggle buttons is selected, an activated toggle button is automatically deselected after the user has drawn respectively deleted an edge between two nodes. However, it is only reasonable to select or deselect once mode if Cool Modes is not in edge mode.

Draw mode

In draw mode it is possible to freely write or draw on the workspace as with a pen. The Handwriting plug-in, for instance, has no nodes or edges and only makes use of this mode.

c. The AbstractReferenceFrame class

To get a working plug-in we need to build the controlling class, a ReferenceFrame. We usually do not directly implement the ReferenceFrame interface. Instead, all ReferenceFrames are derived from an abstract class info.collide.coolmodes.core.AbstractReferenceFrame which implements the info.collide.coolmodes.core.ReferenceFrame interface.
In order to be able to write your own reference frame, you first need a clear understanding of what the methods provided by the AbstractReferenceFrame class are doing. Here you will also learn which of those methods you should override in your own reference frame class. The complete source code of the AbstractReferenceFrame class can be viewed here.

protected AbstractPalette palette;
					 

This is the only object known by AbstractReferenceFrame. It is the graphical user-interface, the palette class. This property should be instantiated in the constructor.

public void init(GraphApplication manager) {
    palette.setHandler(manager);
    // used to allow the palette loading of xml documents without specifying a package
    palette.setPackagePrefixes(getPackagePrefixes());
}

public void update() {}

public void dispose() {}
 					

The init(GraphApplication manager) method is always called when the ReferenceFrame is initialized. It is used basically to give the ReferenceFrame the opportunity to check currently available graphs and draw-areas. update(), on the other hand, is called each time a graph or a draw-area has been added or removed. The ReferenceFrame can consult the GraphApplication to keep track of this. dispose() is called whenever the plug-in is removed, to provide a clean disposal.

public Palette getPalette() { return palette; }

This simply returns the graphical user-interface, the palette.

public String[] getPackagePrefixes() {
    String[] s = new String[0];
    return s;
}
 				 

Returns just an empty String array. Override this method and return an array with the prefixes of your own reference frame's package. Important: If not done, the XML parser will not find your nodes and edges! An example of this can be found in the Step by step chapter of this HOWTO.

public Association[] getNodeAssociations() {
    Association[] list = new Association[0];
    return list;
}

public Association[] getEdgeAssociations() {
    Association[] list = new Association[0];
    return list;
}
					 

Since AbstractReferenceFrame does not have any nodes or edges belonging to it, both of the methods noted above return an empty Association array. For each node and edge of a reference frame, however, it is necessary to associate it with their corresponding model in order to make drag-and-drop work. An array of all node-model associations then has to returned by getNodeAssociations(), while getEdgeAssociation() must return an array of all edge-model associations. See Step by Step for an example.

public abstract String getIdentifier();
					 

Because this is an abstract method you not have to override it to provide your plug-in's identifier. Important: all the identifiers must be unique, i.e. no two reference frames may return the same identifier with this method.

public Rule[] getRules() {
    Rule[] list = new Rule[0];
    return list;
}
					 

Basically, an EdgeRule enables you to allow or forbid edges between certain kind of nodes. Everything you have to know about edge rules is discussed in the JGraph HOWTO. Since there are no nodes or edges, here just an empty Rule array is returned. If edge rules are logically necessary for your nodes and edges, it is recommended to override this method and return an array with one entry for each rule.
In addition, with some kinds of edges and nodes, it does not make any sense to allow the user to construct a cycle. If this is the case with the edges and nodes of your reference frame, the array this method returns may also consist of objects of type ForbiddenCycleRule, because this class is derived from Rule, too. Forbidden cycle rules are discussed in the JGraph HOWTO.

public String getLocation() {
    return getClass().getName();
}
					 

This method simply returns the exact location and name of this ReferenceFrame, e.g. "info.collide.coolmodes.palettes.calc.CalcRefFrame".

d. The AbstractPalette class

When writing a new palette for Cool Modes we usually do not directly implement the Palette interface. Instead, all palettes are derived from an abstract class info.collide.coolmodes.core.AbstractPalette, the parent class of which is JFrame and which implements the info.collide.coolmodes.core.Palette interface.
In order to be able to write your own palette, you first need a clear understanding of what the methods provided by the AbstractPalette class are doing. Here you will also learn which of those methods you should override in your own palette class. The complete source code of the AbstractPalette class can be viewed here.

protected GraphApplication manager;
					 

This is the only object known by AbstractPalette and can be accessed by the inherited palette classes. It is necessary for changing between the various modes of Cool Modes as well as for getting access to the workspaces or their graphs in some of the methods explained below.
GraphApplication is an interface, the class that implements it in Cool Modes is WorkspaceManager.

private String[] prefixes = new String[0];
					 

Holds just an empty String array. Use setPackagePrefixes to set an array with the prefixes of your own reference frame's package. Important: If not done, the XML parser will not find your nodes and edges! An example of this can be found in the Step by step chapter of this HOWTO.

protected String getIconLocation() {
    return "info/collide/coolmodes/resources/ui/default_palette.gif";
}
		 			 

getIconLocation() returns the relative file path of the icon that is displayed in the Add Plug-In dialogue as well as in the buttons in the palette window that enable the user to activate any of the loaded plug-ins.
The standard icon of AbstractPalette is a question mark. It is recommended that this method be overridden in your own palette class, returning the path to a custom icon that should be located in the resources directory of your reference frame's package, for example in myplugins/calc/resources/calc.gif. This method is called by getIcon() in order to return the actual ImageIcon.

protected void toNodeMode() {
    manager.setNodeMode();
}

protected void toDrawMode() {
    manager.setDrawMode();
}

protected void toEdgeMode(Edge e) {
    manager.setEdgeMode(e);
}

protected void toEdgeMode(Edge e, boolean once) {
    if (once) manager.setEdgeMode(e,true,this);
    else manager.setEdgeMode(e);
}
 				 

With the information from the part The modes the methods above should be more or less self-explanatory. You will have to call these methods (in your palette's actionPerformed() method or elsewhere) to switch to a certain mode.

public void setNodeModeUI() {}
			 			

When in once mode, Cool Modes automatically switches back to node mode after a single edge has been drawn by the user. Since a palette is responsible for setting up the graphical user interface that is appropriate for each mode, Cool Modes, apart from switching the mode, also automatically calls the palette's setNodeModeUI() method in such a case. So everything that must be done to prepare the user interface of node mode is to be placed in here.
Usually, if you override this method in your palette class, you will provide for an enabled once checkbox and deselect all the edges' toggle buttons, which is the visual representation of the fact that Cool Modes is not in any kind of edge mode any more.

protected void activateLastWorkspace() {
    manager.activateLastWorkspace();
}
 					

activateLastWorkspace() simply calls the corresponding method in the GraphApplication in order to activate the workspace that was last active before the user deactivated it by clicking somewhere into the palette window (e.g. to select a node or to click on a toggle button to change the mode).
Sometimes, it is of importance that after using the control elements in the palette window, the previously active workspace immediately gets the focus again. So, this method should be called at the end of the actionPerformed() method implemented in the palette class.

protected JGraph loadJGraph(String location) {
    JGraph jgraph = JGraphLoader.loadJGraph(
    ClassLoader.getSystemClassLoader().getResource(location), prefixes);
    jgraph.setPopupEnabledRecursively(false);
    return jgraph;
}
 					

One way to build the JGraph that - as a part of the plug-in's graphical user interface - contains one instance of each type of node belonging to the reference frame is to load it from a valid XML file with JGraph as the top-level tag (an example is shown in the Step by step chapter. This can be done in the constructor of your palette class by calling loadJGraph(String location) where location is the path to your XML file, located in your plug-in package, e.g. myplugins/calc/calc.xml.

public void enterPalette() {}

public void leavePalette() {}
			 			

A palette's enterPalette() method is always called if this reference frame is selected by the user. Usually, plug-ins switch Cool Modes to node mode if they are selected and thus override this method.
A palette's leavePalette() method, on the other hand, is always called if this plug-in is deselected by the user. Most reference frames, not having any special tasks to do if deselected, do not override this empty method, but if there is anything your plug-in must do in such a case, override it.

public abstract Component getUI();
			 		 

This is the only abstract method of the AbstractPalette class. When overriding it, you build the graphical user interface (usually consisting of a JGraph containing the nodes of our reference frame and a JPanel holding the buttons used to activate or deactivate the edges) and then return it. Usually this GUI is also an instance of JPanel. We will construct a GUI for our example plug-in in full detail in the Step by step chapter.

Since AbstractPalette does not have any nodes or edges belonging to it, both of the methods noted above return an empty Association array. For each node and edge of a plug-in, however, it is necessary to associate it with their corresponding model in order to make drag-and-drop work. An array of all node-model associations then has to returned by getNodeAssociations(), while getEdgeAssociation() must return an array of all edge-model associations. See Step by Step for an example.

public String getToolTipText() {
    return "my hint";
}
 					 

The tooltip text will appear if the cursor is positioned over one of the buttons that allow the user to switch between loaded plug-ins. Simply override getToolTipText() so that an appropriate tooltip text is returned by this method.

public Icon getIcon() {
    return new ImageIcon(getToolkit().getImage(
    ClassLoader.getSystemClassLoader().getResource(getIconLocation())));
}
 					

getIcon() returns the ImageIcon object the file path of which is declared in the getIconLocation() method which you should override so that your custom reference frame icon will be loaded.