/*
 * @(#)EditorGraph.java 3.3 23-APR-04
 * 
 * Copyright (c) 2001-2004, Gaudenz Alder 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.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.Hashtable;
import java.util.Map;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.event.CellEditorListener;

import org.jgraph.JGraph;
import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.CellView;
import org.jgraph.graph.GraphCellEditor;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphModel;
import org.jgraph.plaf.basic.BasicGraphUI;

/** 
* An example that demonstrates how to use a JDialog 
* as a CellEditor in JGraph. 
* 
* @version 1.1 23/12/02 
* @author Gaudenz Alder 
*/

public class EditorGraph extends JGraph {

	/** 
	* Constructs a EditorGraph with a sample model. 
	*/
	public EditorGraph() {
	}

	/** 
	* Constructs a EditorGraph for <code>model</code>. 
	*/
	public EditorGraph(GraphModel model) {
		super(model);
	}

	/** 
	* Override parent method with custom GraphUI. 
	*/
	public void updateUI() {
		setUI(new EditorGraphUI());
		invalidate();
	}

	public static void main(String[] args) {
		JFrame frame = new JFrame("EditorGraph");
		frame.getContentPane().add(new EditorGraph());
		frame.pack();
		//frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}

	/** 
	* Definition of the custom GraphUI. 
	*/
	public class EditorGraphUI extends BasicGraphUI {

		protected CellEditorListener cellEditorListener;

		protected JFrame editDialog = null;

		/** 
		* Create the dialog using the cell's editing component. 
		*/
		protected void createEditDialog(Object cell, MouseEvent event) {
			Dimension editorSize = editingComponent.getPreferredSize();
			editDialog = new JFrame("Edit " + graph.convertValueToString(cell));
			editDialog.setSize(editorSize.width, editorSize.height);
			editDialog.getContentPane().add(editingComponent);
			editingComponent.validate();
			editDialog.pack();
			editDialog.show();
		}

		/** 
		* Stops the editing session. If messageStop is true the editor 
		* is messaged with stopEditing, if messageCancel is true the 
		* editor is messaged with cancelEditing. If messageGraph is true 
		* the graphModel is messaged with valueForCellChanged. 
		*/
		protected void completeEditing(
			boolean messageStop,
			boolean messageCancel,
			boolean messageGraph) {
			if (stopEditingInCompleteEditing
				&& editingComponent != null
				&& editDialog != null) {
				Component oldComponent = editingComponent;
				Object oldCell = editingCell;
				GraphCellEditor oldEditor = cellEditor;
				Object newValue = oldEditor.getCellEditorValue();
				Rectangle2D editingBounds = graph.getCellBounds(editingCell);
				boolean requestFocus =
					(graph != null
						&& (graph.hasFocus() || editingComponent.hasFocus()));
				editingCell = null;
				editingComponent = null;
				if (messageStop)
					oldEditor.stopCellEditing();
				else if (messageCancel)
					oldEditor.cancelCellEditing();
				editDialog.dispose();
				if (requestFocus)
					graph.requestFocus();
				if (messageGraph) {
					AttributeMap map = getModel().createAttributes();
					GraphConstants.setValue(map, newValue);
					Map nested = new Hashtable();
					nested.put(oldCell, map);
					graphLayoutCache.edit(nested, null, null, null);
				}
				updateSize();
				// Remove Editor Listener 
				if (oldEditor != null && cellEditorListener != null)
					oldEditor.removeCellEditorListener(cellEditorListener);
				cellEditor = null;
				editDialog = null;
			}
		}

		/** 
		* Will start editing for cell if there is a cellEditor and 
		* shouldSelectCell returns true.<p> 
		* This assumes that cell is valid and visible. 
		*/
		protected boolean startEditing(Object cell, MouseEvent event) {
			completeEditing();
			if (graph.isCellEditable(cell) && editDialog == null) {

				// Create Editing Component **** ***** 
				CellView tmp = graphLayoutCache.getMapping(cell, false);
				cellEditor = tmp.getEditor();
				editingComponent =
					cellEditor.getGraphCellEditorComponent(
						graph,
						cell,
						graph.isCellSelected(cell));
				if (cellEditor.isCellEditable(event)) {
					editingCell = cell;

					// Create Wrapper Dialog **** ***** 
					createEditDialog(cell, event);

					// Add Editor Listener 
					if (cellEditorListener == null)
						cellEditorListener = createCellEditorListener();
					if (cellEditor != null && cellEditorListener != null)
						cellEditor.addCellEditorListener(cellEditorListener);

					if (cellEditor.shouldSelectCell(event)) {
						stopEditingInCompleteEditing = false;
						try {
							graph.setSelectionCell(cell);
						} catch (Exception e) {
							System.err.println("Editing exception: " + e);
						}
						stopEditingInCompleteEditing = true;
					}

					if (event instanceof MouseEvent) {
						/* Find the component that will get forwarded all the 
						mouse events until mouseReleased. */
						Point componentPoint =
							SwingUtilities.convertPoint(
								graph,
								new Point(event.getX(), event.getY()),
								editingComponent);

						/* Create an instance of BasicTreeMouseListener to handle 
						passing the mouse/motion events to the necessary 
						component. */
						// We really want similiar behavior to getMouseEventTarget, 
						// but it is package private. 
						Component activeComponent =
							SwingUtilities.getDeepestComponentAt(
								editingComponent,
								componentPoint.x,
								componentPoint.y);
						if (activeComponent != null) {
							new MouseInputHandler(
								graph,
								activeComponent,
								event);
						}
					}
					return true;
				} else
					editingComponent = null;
			}
			return false;
		}

	}

}
