/*
* Copyright (C) 2012 Jason Gedge <http://www.gedge.ca>
*
* This file is part of the OpGraph project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.gedge.opgraph.app.components.canvas;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.UndoableEditListener;
import javax.swing.undo.UndoableEditSupport;
import ca.gedge.opgraph.OpNode;
import ca.gedge.opgraph.OpNodeAdapter;
import ca.gedge.opgraph.app.components.DoubleClickableTextField;
import ca.gedge.opgraph.app.edits.node.ChangeNodeNameEdit;
import ca.gedge.opgraph.app.util.GUIHelper;
/**
* A text field that displays the name of a node. Supports undo/redo.
*/
public class CanvasNodeName extends JTextField {
/** Double-click support */
private DoubleClickableTextField doubleClickSupport;
/** The node whose name this component is showing */
private OpNode node;
/** The style used for this component */
private NodeStyle style;
/** Undo support for node name edits */
private UndoableEditSupport undoSupport;
/**
* Constructs a name component that references the given node.
*
* @param node the node
* @param style the node style
*
* @throws NullPointerException if the specified node is <code>null</code>
*/
public CanvasNodeName(OpNode node, NodeStyle style) {
this.doubleClickSupport = new DoubleClickableTextField(this);
this.undoSupport = new UndoableEditSupport();
setHorizontalAlignment(CENTER);
setFont(getFont().deriveFont(Font.BOLD));
setNode(node);
setStyle(style);
this.doubleClickSupport.addPropertyChangeListener(DoubleClickableTextField.TEXT_PROPERTY, textListener);
}
/**
* Gets the node whose name is displayed by this component.
*
* @return the node
*/
public OpNode getNode() {
return node;
}
/**
* Sets the node whose name is displayed by this component.
*
* @param node the node
*
* @throws NullPointerException if the specified node is <code>null</code>
*/
public void setNode(OpNode node) {
if(node == null) throw new NullPointerException("node cannot be null");
if(this.node != null)
this.node.removeNodeListener(nodeListener);
this.node = node;
this.node.addNodeListener(nodeListener);
// Update component
setBorder(new EmptyBorder(2, 5, 2, 5));
setText(node.getName());
setToolTipText(node.getDescription());
}
/**
* Sets the style used for this node.
*
* @param style the node style
*/
public void setStyle(NodeStyle style) {
this.style = (style == null ? new NodeStyle() : style);
setBackground(this.style.NodeNameTopColor);
setForeground(this.style.NodeNameTextColor);
revalidate();
repaint();
}
//
// Overrides
//
@Override
protected void paintComponent(Graphics gfx) {
if(doubleClickSupport.isEditing()) {
super.paintComponent(gfx);
} else {
final Graphics2D g = (Graphics2D)gfx;
final Rectangle rect = GUIHelper.getInterior(this);
final int halign = getHorizontalAlignment();
final int valign = SwingConstants.CENTER;
final Point p = GUIHelper.placeTextInRectangle(g, getText(), rect, halign, valign);
// Draw shadow under text, if necessary
if(style.NodeNameTextShadowColor != null) {
g.setColor(style.NodeNameTextShadowColor);
g.drawString(getText(), p.x + 1, p.y + 1);
}
g.setColor(getForeground());
g.drawString(getText(), p.x, p.y);
}
}
//
// OpNodeListener
//
final OpNodeAdapter nodeListener = new OpNodeAdapter() {
@Override
public void nodePropertyChanged(String propertyName, Object oldValue, Object newValue) {
if(propertyName.equals(OpNode.NAME_PROPERTY))
setText((String)newValue);
}
};
//
// DoubleClickableTextField property listener
//
private final PropertyChangeListener textListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
if(node != null) {
String t = (String)e.getNewValue();
if(t == null || t.trim().length() == 0)
t = node.getName();
undoSupport.postEdit(new ChangeNodeNameEdit(node, t));
}
}
};
//
// UndoableEdit support
//
/**
* Adds an undoable edit listener to this component.
*
* @param listener the listener to add
*/
public void addUndoableEditListener(UndoableEditListener listener) {
undoSupport.addUndoableEditListener(listener);
}
/**
* Removes an undoable edit listener from this component.
*
* @param listener the listener to remove
*/
public void removeUndoableEditListener(UndoableEditListener listener) {
undoSupport.removeUndoableEditListener(listener);
}
}