edu.mit.csail.sdg.alloy4.OurTree Maven / Gradle / Ivy
Show all versions of alloy-core Show documentation
/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package edu.mit.csail.sdg.alloy4;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.util.IdentityHashMap;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
/**
* Graphical tree.
*
* Thread Safety: Can be called only by the AWT event thread.
*/
public abstract class OurTree extends JTree {
/**
* The current list of listeners; whenever a node X is selected we'll send
* (Event.CLICK, X) to each listener.
*/
public final Listeners listeners = new Listeners();
/**
* Custom TreeCellRenderer to print the tree nodes better. (The idea of using
* JLabel is inspired by DefaultTreeCellRenderer)
*/
private final class OurTreeRenderer extends JLabel implements TreeCellRenderer {
/** This ensures the class can be serialized reliably. */
private static final long serialVersionUID = 0;
/** This stores the height of one line of text. */
private int height;
/**
* If preferredHeight > 0, then preferredWidth is the desired width for the
* current object being drawn.
*/
private int preferredWidth = 0;
/**
* If preferredHeight > 0, then preferredHeight is the desired height for the
* current object being drawn.
*/
private int preferredHeight = 0;
/** Whether the current object is selected or not. */
private boolean isSelected;
/** Whether the current object is focused or not. */
private boolean isFocused;
/** Constructs this renderer. */
public OurTreeRenderer(int fontSize) {
super("Anything"); // This ensures that the height is calculated
// properly
setFont(OurUtil.getVizFont().deriveFont((float) fontSize));
setVerticalAlignment(SwingConstants.BOTTOM);
setBorder(new EmptyBorder(0, 3, 0, 3));
setText("Anything"); // So that we can derive the height
height = super.getPreferredSize().height;
}
/**
* This method is called by Swing to return an object to be drawn.
*/
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean isLeaf, int row, boolean isFocused) {
String string = tree.convertValueToText(value, isSelected, expanded, isLeaf, row, isFocused);
this.isFocused = isFocused;
this.isSelected = isSelected;
setText(string);
setForeground(UIManager.getColor(isSelected ? "Tree.selectionForeground" : "Tree.textForeground"));
preferredHeight = 0; // By default, don't override width/height
if (do_isDouble(value)) {
Dimension d = super.getPreferredSize();
preferredWidth = d.width + 3;
preferredHeight = d.height * 2;
}
return this;
}
/**
* We override the getPreferredSize() method to return a custom size if
* do_isDouble() returns true for that value.
*/
@Override
public Dimension getPreferredSize() {
if (preferredHeight > 0)
return new Dimension(preferredWidth, preferredHeight);
Dimension d = super.getPreferredSize();
return new Dimension(d.width + 3, d.height);
}
/**
* We override the paint() method to avoid drawing the box around the "extra
* space" (if height is double height)
*/
@Override
public void paint(Graphics g) {
int w = getWidth(), h = getHeight(), y = h - height;
Color background = isSelected ? UIManager.getColor("Tree.selectionBackground") : Color.WHITE;
Color border = isFocused ? UIManager.getColor("Tree.selectionBorderColor") : null;
if (background != null) {
g.setColor(background);
g.fillRect(0, y, w, h - y);
}
if (border != null && isSelected) {
g.setColor(border);
g.drawRect(0, y, w - 1, h - 1 - y);
}
super.paint(g);
}
}
/** This ensures the class can be serialized reliably. */
private static final long serialVersionUID = 0;
/**
* Subclass should implement this method to return the root of the tree.
*/
public abstract Object do_root();
/**
* Subclass should implement this method to return the list of children nodes
* given a particular node.
*/
public abstract List< ? > do_ask(Object parent);
/**
* Subclass should override this method to return whether a given item should be
* double-height or not (default = no).
*/
protected boolean do_isDouble(Object object) {
return false;
}
/**
* Subclass should call this when all fields are initialized; we won't call
* do_root() and do_ask() until subclass calls this.
*/
protected final void do_start() {
// Create a custom TreeModel that calls do_root() and do_ask() whenever
// the tree needs expansion
setModel(new TreeModel() {
// Cache the parent->child list so that we always get the exact same
// OBJECT REFERENCE when navigating the tree
private final IdentityHashMap