001: /*
002: * $Id: JGraphpadVertexTool.java,v 1.9 2007/03/25 13:07:51 david Exp $
003: * Copyright (c) 2001-2005, Gaudenz Alder
004: *
005: * All rights reserved.
006: *
007: * See LICENSE file for license details. If you are unable to locate
008: * this file please contact info (at) jgraph (dot) com.
009: */
010: package com.jgraph.pad.tool;
011:
012: import java.awt.Dimension;
013: import java.awt.Graphics;
014: import java.awt.Graphics2D;
015: import java.awt.event.MouseEvent;
016: import java.awt.geom.AffineTransform;
017: import java.awt.geom.Rectangle2D;
018:
019: import org.jgraph.JGraph;
020: import org.jgraph.graph.CellView;
021: import org.jgraph.graph.DefaultGraphModel;
022: import org.jgraph.graph.GraphConstants;
023: import org.jgraph.graph.GraphLayoutCache;
024: import org.jgraph.graph.GraphModel;
025: import org.jgraph.graph.VertexView;
026: import org.jgraph.plaf.GraphUI;
027: import org.jgraph.plaf.basic.BasicGraphUI;
028:
029: import com.jgraph.editor.JGraphEditorTool;
030:
031: /**
032: * Tool that inserts vertices based on a prototype.
033: */
034: public class JGraphpadVertexTool extends JGraphEditorTool {
035:
036: /**
037: * Defines the default name for tools of this kind.
038: */
039: public static final String NAME_VERTEXTOOL = "vertexTool";
040:
041: /**
042: * Holds the prototype to create new cells with.
043: */
044: protected Object prototype;
045:
046: /**
047: * Defines the threshhold (minimum size) for a drag to be used for an
048: * insert. Default is 4.
049: */
050: protected int threshold = 4;
051:
052: /**
053: * Defines the default size for vertices that are generated with a single
054: * click. A value of null ignores single clicks. Default is null.
055: */
056: protected Dimension singleClickSize = null;
057:
058: /**
059: * Preview vertex view.
060: */
061: protected transient CellView previewView;
062:
063: /**
064: * Specifies if the cellview should be previewed. Default is true.
065: */
066: protected transient boolean previewEnabled = true;
067:
068: /**
069: * Constructs a new vertex tool for the specified prototype using
070: * {@link #NAME_VERTEXTOOL}.
071: *
072: * @param prototype
073: * The prototype cell to create new vertices with.
074: */
075: public JGraphpadVertexTool(Object prototype) {
076: this (NAME_VERTEXTOOL, prototype);
077: }
078:
079: /**
080: * Constructs a new vertex tool for the specified name and prototype.
081: *
082: * @param name
083: * The name of the tool to be created.
084: * @param prototype
085: * The prototype cell to create new vertices with.
086: */
087: public JGraphpadVertexTool(String name, Object prototype) {
088: super (name);
089: this .prototype = prototype;
090: }
091:
092: /**
093: * Returns the prototype used to create new cells.
094: *
095: * @return Returns the prototype.
096: */
097: public Object getPrototype() {
098: return prototype;
099: }
100:
101: /**
102: * Sets the prototype to be used to create new cells.
103: *
104: * @param prototype
105: * The prototype to set.
106: */
107: public void setPrototype(Object prototype) {
108: this .prototype = prototype;
109: }
110:
111: /**
112: * Returns the single click size which should be used to insert cells with a
113: * single click.
114: *
115: * @return Returns the singleClickSize.
116: */
117: public Dimension getSingleClickSize() {
118: return singleClickSize;
119: }
120:
121: /**
122: * Sets the size of cells to be inserted with a single click.
123: *
124: * @param singleClickSize
125: * The singleClickSize to set.
126: */
127: public void setSingleClickSize(Dimension singleClickSize) {
128: this .singleClickSize = singleClickSize;
129: }
130:
131: /**
132: * Returns the threshold for a drag to count as an insert.
133: *
134: * @return Returns the threshold.
135: */
136: public int getThreshold() {
137: return threshold;
138: }
139:
140: /**
141: * Sets the threshold for a drag to count as an insert.
142: *
143: * @param threshold
144: * The threshold to set.
145: */
146: public void setThreshold(int threshold) {
147: this .threshold = threshold;
148: }
149:
150: /**
151: * Sets if the cellview should be previewed.
152: *
153: * @return Returns the previewEnabled.
154: */
155: public boolean isPreviewEnabled() {
156: return previewEnabled;
157: }
158:
159: /**
160: * Returns if the cellview should be previewed.
161: *
162: * @param previewEnabled
163: * The previewEnabled to set.
164: */
165: public void setPreviewEnabled(boolean previewEnabled) {
166: this .previewEnabled = previewEnabled;
167: }
168:
169: /**
170: * Extends the parent's implementation to create a clone of the pototype
171: * using {@link #createCell(GraphModel)} and a new view using the layout
172: * cache's factory. The view is then configured and stored in
173: * {@link #previewView}.
174: *
175: * @param event
176: * The object that describes the event.
177: */
178: public void mousePressed(MouseEvent event) {
179: super .mousePressed(event);
180: JGraph graph = getGraphForEvent(event);
181: if (graph != null) {
182: Object cell = createCell(graph.getModel());
183: if (cell != null) {
184: previewView = graph.getGraphLayoutCache().getFactory()
185: .createView(graph.getModel(), cell);
186:
187: // Configures the previewView by setting its bounds
188: // to the marquee bounds and calling refresh.
189: if (previewView != null) {
190: Rectangle2D rect = graph.fromScreen(graph
191: .snap((Rectangle2D) marqueeBounds.clone()));
192: previewView.getAttributes().applyValue(
193: GraphConstants.BOUNDS, rect);
194: previewView.refresh(graph.getGraphLayoutCache(),
195: graph.getGraphLayoutCache(), false);
196: }
197: }
198: }
199: }
200:
201: /**
202: * Returns a deep clone of the cell prototype.
203: *
204: * @param model
205: * The model to use for cloning the prototype.
206: * @return Returns a clone of {@link #prototype}.
207: */
208: protected Object createCell(GraphModel model) {
209: return DefaultGraphModel.cloneCell(model, prototype);
210: }
211:
212: /**
213: * Overrides the parent's implementation to update the preview bounds to the
214: * current {@link org.jgraph.graph.BasicMarqueeHandler#marqueeBounds}.
215: *
216: * @param event
217: * The object that describes the event.
218: */
219: protected void processMouseDraggedEvent(MouseEvent event) {
220: super .processMouseDraggedEvent(event);
221: if (marqueeBounds != null && previewView instanceof VertexView) {
222: JGraph graph = getGraphForEvent(event);
223:
224: // Special handling of constrained events or cells
225: if (isConstrainedSizeEvent(event)
226: || GraphConstants.isConstrained(previewView
227: .getAllAttributes())) {
228: marqueeBounds.setFrame(startPoint.getX(), startPoint
229: .getY(), marqueeBounds.getWidth(),
230: marqueeBounds.getWidth());
231: }
232:
233: // Updates the bounds of the previewView and updates
234: Rectangle2D rect = graph.fromScreen(graph
235: .snap((Rectangle2D) marqueeBounds.clone()));
236: previewView.getAttributes().applyValue(
237: GraphConstants.BOUNDS, rect);
238: previewView.update(graph.getGraphLayoutCache());
239: }
240: }
241:
242: /**
243: * Hook for subclassers to define the key assignments for constrained
244: * inserts. This implementation redirects to
245: * {@link BasicGraphUI#isConstrainedMoveEvent(MouseEvent)}.
246: *
247: * @param event
248: * The object that describes the event.
249: */
250: protected boolean isConstrainedSizeEvent(MouseEvent event) {
251: JGraph graph = getGraphForEvent(event);
252: GraphUI ui = graph.getUI();
253: if (ui instanceof BasicGraphUI)
254: return ((BasicGraphUI) ui).isConstrainedMoveEvent(event);
255: return false;
256: }
257:
258: /**
259: * Overrides the parent's implementation to check if the event triggers an
260: * insert (checks {@link #threshold} & {@link #singleClickSize}) and
261: * inserts the cell stored in {@link #previewView} using
262: * {@link #execute(GraphLayoutCache, Object)}.
263: *
264: * @param event
265: * The object that describes the event.
266: */
267: public void mouseReleased(MouseEvent event) {
268: try {
269: JGraph graph = getGraphForEvent(event);
270: if (graph != null) {
271: if (previousCursor != null) {
272: graph.setCursor(previousCursor);
273: }
274: if (marqueeBounds != null
275: && ((singleClickSize != null) || (marqueeBounds
276: .getWidth() > threshold || marqueeBounds
277: .getHeight() > threshold))) {
278: if (previewView != null) {
279:
280: // Check if the minimum size requirement is met and do a
281: // single-click insert with the specified size. This
282: // takes the preview view and modifies the bounds
283: // in place to be minimum of single click size.
284: if (singleClickSize != null) {
285: Rectangle2D bounds = GraphConstants
286: .getBounds(previewView
287: .getAllAttributes());
288: if (bounds != null)
289: bounds.setFrame(bounds.getX(), bounds
290: .getY(), Math.max(
291: singleClickSize.width, bounds
292: .getWidth()), Math.max(
293: singleClickSize.height, bounds
294: .getHeight()));
295: }
296:
297: // Prepares the cell for the execute call by moving all
298: // attributes from the preview view to the cell, then
299: // calls execute with that cell and the current cache.
300: Object cell = previewView.getCell();
301: graph.getModel().getAttributes(cell).applyMap(
302: previewView.getAllAttributes());
303: execute(graph, cell);
304: }
305:
306: // Prevents further event processing
307: event.consume();
308: } else {
309:
310: // After an invalid insert the selection and the graph
311: // canvas
312: // is cleared and theh event is passed along the chain.
313: graph.clearSelection();
314: if (previewView != null) {
315: Rectangle2D r = previewView.getBounds();
316: graph.getGraphics().setClip((int) r.getX() - 1,
317: (int) r.getY() - 1,
318: (int) r.getWidth() + 2,
319: (int) r.getHeight() + 2);
320: graph.repaint();
321: }
322: }
323: }
324: } finally {
325: previousCursor = null;
326: marqueeBounds = null;
327: currentPoint = null;
328: previewView = null;
329: startPoint = null;
330: }
331: }
332:
333: /**
334: * Provides a hook for subclassers to insert the specified cell into
335: * <code>cache</code>. This implementation passes the cell to the
336: * {@link GraphLayoutCache#insert(Object)}.
337: *
338: * @param cache
339: * The cache into which to insert the edge.
340: * @param cell
341: * The cell to be inserted into <code>cache</code>.
342: */
343: protected void execute(JGraph graph, Object cell) {
344: graph.getGraphLayoutCache().insert(cell);
345: }
346:
347: /**
348: * Extends the parent's implementation to draw the {@link #previewView}.
349: *
350: * @param graph
351: * The graph to paint in.
352: * @param g
353: * The graphics to use for paiting.
354: * @param clear
355: * Wether to clear the display.
356: */
357: public void overlay(JGraph graph, Graphics g, boolean clear) {
358: if (!previewEnabled) {
359: super .overlay(graph, g, clear);
360: } else if (previewView != null) {
361: Rectangle2D bounds = previewView.getBounds();
362: if (clear) {
363: g.setPaintMode();
364: graph.repaint((int) bounds.getX() - 1, (int) bounds
365: .getY() - 1, (int) bounds.getWidth() + 2,
366: (int) bounds.getHeight() + 2);
367: } else {
368: Graphics2D g2 = (Graphics2D) g;
369: AffineTransform tmp = g2.getTransform();
370: g2.scale(graph.getScale(), graph.getScale());
371: graph.getUI().paintCell(g, previewView, bounds, true);
372: g2.setTransform(tmp);
373: }
374: }
375: }
376:
377: }
|