/*
 * @(#)IconExample.java 1.0 28-SEPT-04
 * 
 * Copyright (c) 2001-2004, Dean Mao All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer. - Redistributions in
 * binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution. - Neither the name of JGraph nor
 * the names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *  
 */

package org.jgraph.example;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.net.URL;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.event.UndoableEditEvent;

import org.jgraph.graph.CellView;
import org.jgraph.graph.CellViewRenderer;
import org.jgraph.graph.DefaultCellViewFactory;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.EdgeView;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphModel;
import org.jgraph.graph.GraphUndoManager;
import org.jgraph.graph.Port;
import org.jgraph.graph.PortRenderer;
import org.jgraph.graph.PortView;

/**
 * @author Dean Mao
 * 
 * Hint: Use <code>ant example</code> to run this example. Make sure you the
 * jgraph source distribution located at <code>../jgraph relative</code> to
 * this project's root directory (see <code>build.xml</code>).
 * 
 * @created Sep 28, 2004
 */
public class IconExample extends GraphEd {

	// Shared icon
	protected ImageIcon jgraphIcon = null;

	/**
	 * 
	 */
	public IconExample() {
		// Use Border Layout
		getContentPane().setLayout(new BorderLayout());
		// Construct the Graph
		graph = new IconGraph(new MyModel());

		// Construct Command History
		//
		// Create a GraphUndoManager which also Updates the ToolBar
		undoManager = new GraphUndoManager() {
			// Override Superclass
			public void undoableEditHappened(UndoableEditEvent e) {
				// First Invoke Superclass
				super.undoableEditHappened(e);
				// Then Update Undo/Redo Buttons
				updateHistoryButtons();
			}
		};

		// Add Listeners to Graph
		//
		// Register UndoManager with the Model
		graph.getModel().addUndoableEditListener(undoManager);
		// Update ToolBar based on Selection Changes
		graph.getSelectionModel().addGraphSelectionListener(this);
		// Listen for Delete Keystroke when the Graph has Focus
		graph.addKeyListener(this);

		// Construct Panel
		//
		// Add a ToolBar
		getContentPane().add(createToolBar(), BorderLayout.NORTH);
		// Add the Graph as Center Component
		getContentPane().add(new JScrollPane(graph), BorderLayout.CENTER);

		//
		// Load Icon
		String iconPath = "org/jgraph/example/resources/jgraph.gif";
		URL jgraphUrl = IconExample.class.getClassLoader()
				.getResource(iconPath);
		// If Valid URL
		if (jgraphUrl != null) {
			// Load Icon
			jgraphIcon = new ImageIcon(jgraphUrl);
		} else {
			throw new RuntimeException(
					"Can't load without the default icon file!  I tried to find: "
							+ iconPath);
		}
	}

	public Map createCellAttributes(Point2D point) {
		Map map = super.createCellAttributes(point);
		GraphConstants.setIcon(map, jgraphIcon);
		return map;
	}

	protected DefaultGraphCell createDefaultGraphCell() {
		return new CustomCell(jgraphIcon, "default\ndescription");
	}

	/**
	 * Define a custom graph that implements the CellViewFactory method,
	 * createView(), so that we can create our custom icon/description vertex.
	 * 
	 * @author Dean Mao
	 * @created Sep 28, 2004
	 */
	public class IconGraph extends GraphEd.MyGraph {
		public IconGraph(GraphModel model) {
			super(model);
			getGraphLayoutCache().setAutoSizeOnValueChange(true);
			getGraphLayoutCache().setFactory(new DefaultCellViewFactory() {
				public CellView createView(GraphModel model, Object c) {
					CellView view = null;
					if (c instanceof CustomCell) {
						return new JGraphIconView(c);
					} else if (c instanceof Port) {
						view = new InvisiblePortView(c);
					} else {
						view = super.createView(model, c);
					}
					return view;
				}
			});
		}
	}

	/**
	 * CustomCell that allows user to define an icon and a description for the
	 * graph vertex.
	 * 
	 * @author Dean Mao
	 * @created Sep 28, 2004
	 */
	public class CustomCell extends DefaultGraphCell {
		private ImageIcon icon;

		private String description;

		public CustomCell(ImageIcon icon, String description) {
			this.icon = icon;
			this.description = description;
		}

		public String getDescription() {
			return description;
		}

		public ImageIcon getIcon() {
			return icon;
		}

		/**
		 * Sets the description on a cell. This is called from the multi-lined
		 * editor.
		 */
		public void setUserObject(Object obj) {
			if (obj != null && obj instanceof String) {
				this.description = obj.toString();
			}
		}

		/**
		 * Return the description of the cell so that it will be the initial
		 * value of the in-graph editor.
		 */
		public String toString() {
			return description;
		}
	}

	/**
	 * This "invisible port" is the same size as the icon on the
	 * icon/description vertex. We do this by navigating up the tree to get the
	 * CellView, then the CustomCell to get the actual icon height/width. Keep
	 * in mind that we are also changing the location of the port such that it
	 * is in the same place as the icon displayed on the screen.
	 * 
	 * The port renderer is designed so that it doesn't paint anything.
	 * 
	 * @author Dean Mao
	 * @created Sep 28, 2004
	 */
	public class InvisiblePortView extends PortView {

		public InvisiblePortView(Object cell) {
			super(cell);
		}

		public Rectangle2D getBounds() {
			Rectangle2D parentBounds = getParentView().getBounds();
			double height = ((CustomCell) getParentView().getCell()).getIcon()
					.getIconHeight();
			double width = ((CustomCell) getParentView().getCell()).getIcon()
					.getIconWidth();
			double x = parentBounds.getX()
					+ ((parentBounds.getWidth() - width) / 2);
			double y = parentBounds.getY() + 5;
			return new Rectangle2D.Double(x, y, width, height);
		}

		public CellViewRenderer getRenderer() {
			return portRenderer;
		}

		public Point2D getLocation(EdgeView edge) {
			if (edge == null)
				return new Point2D.Double(this.getBounds().getCenterX(), this
						.getBounds().getCenterY());
			else
				return super.getLocation(edge);
		}
	}

	protected static InvisiblePortRenderer portRenderer = new InvisiblePortRenderer();

	public static class InvisiblePortRenderer extends PortRenderer {
		public void paint(Graphics g) {
			if (preview)
				super.paint(g);
			// else: null implementation (ie, don't paint anything!!)
		}
	}

	/**
	 * Main method
	 */
	public static void main(String[] args) {
		// Switch off D3D because of Sun XOR painting bug
		// See http://www.jgraph.com/forum/viewtopic.php?t=4066
		System.setProperty("sun.java2d.d3d", "false");
		// Construct Frame
		JFrame frame = new JFrame("IconExample");
		// Set Close Operation to Exit
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// Add an Editor Panel
		frame.getContentPane().add(new IconExample());
		// Fetch URL to Icon Resource
		URL jgraphUrl = IconExample.class.getClassLoader().getResource(
				"org/jgraph/example/resources/jgraph.gif");
		// If Valid URL
		if (jgraphUrl != null) {
			// Load Icon
			ImageIcon jgraphIcon = new ImageIcon(jgraphUrl);
			// Use in Window
			frame.setIconImage(jgraphIcon.getImage());
		}
		// Set Default Size
		frame.setSize(520, 390);
		// Show Frame
		frame.setVisible(true);
	}
}