All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.cockpit.modeler.drawing.ProcessDrawing Maven / Gradle / Ivy

The newest version!
/*
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package org.openbp.cockpit.modeler.drawing;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.swing.JScrollPane;

import org.openbp.cockpit.itemeditor.ItemCreationUtil;
import org.openbp.cockpit.modeler.Modeler;
import org.openbp.cockpit.modeler.figures.VisualElement;
import org.openbp.cockpit.modeler.figures.VisualElementEvent;
import org.openbp.cockpit.modeler.figures.generic.GeometryException;
import org.openbp.cockpit.modeler.figures.generic.UpdatableFigure;
import org.openbp.cockpit.modeler.figures.process.FlowConnection;
import org.openbp.cockpit.modeler.figures.process.HLineFigure;
import org.openbp.cockpit.modeler.figures.process.LineFigure;
import org.openbp.cockpit.modeler.figures.process.NodeFigure;
import org.openbp.cockpit.modeler.figures.process.ParamConnection;
import org.openbp.cockpit.modeler.figures.process.ParamFigure;
import org.openbp.cockpit.modeler.figures.process.ProcessElementContainer;
import org.openbp.cockpit.modeler.figures.process.ProcessElementFigureRegistry;
import org.openbp.cockpit.modeler.figures.process.ProcessVariableConnection;
import org.openbp.cockpit.modeler.figures.process.SocketFigure;
import org.openbp.cockpit.modeler.figures.process.TextElementFigure;
import org.openbp.cockpit.modeler.figures.process.VLineFigure;
import org.openbp.cockpit.modeler.figures.spline.PolySplineConnection;
import org.openbp.cockpit.modeler.figures.tag.AbstractTagFigure;
import org.openbp.cockpit.modeler.skins.Skin;
import org.openbp.cockpit.modeler.util.FigureUtil;
import org.openbp.cockpit.modeler.util.ModelerFlavors;
import org.openbp.common.CommonUtil;
import org.openbp.common.ExceptionUtil;
import org.openbp.common.MsgFormat;
import org.openbp.common.generic.Modifiable;
import org.openbp.core.model.Model;
import org.openbp.core.model.ModelObject;
import org.openbp.core.model.ModelQualifier;
import org.openbp.core.model.item.Item;
import org.openbp.core.model.item.process.ControlLink;
import org.openbp.core.model.item.process.DataLink;
import org.openbp.core.model.item.process.ItemSynchronization;
import org.openbp.core.model.item.process.Node;
import org.openbp.core.model.item.process.NodeParam;
import org.openbp.core.model.item.process.NodeProvider;
import org.openbp.core.model.item.process.NodeSocket;
import org.openbp.core.model.item.process.Param;
import org.openbp.core.model.item.process.ProcessItem;
import org.openbp.core.model.item.process.ProcessObject;
import org.openbp.core.model.item.process.ProcessVariable;
import org.openbp.core.model.item.process.TextElement;
import org.openbp.guiclient.util.ClientFlavors;
import org.openbp.jaspira.action.ActionMgr;
import org.openbp.jaspira.action.JaspiraAction;
import org.openbp.jaspira.event.AskEvent;
import org.openbp.jaspira.gui.interaction.DropClientUtil;
import org.openbp.jaspira.gui.interaction.Importer;
import org.openbp.jaspira.gui.interaction.InteractionClient;
import org.openbp.jaspira.gui.interaction.ViewDropRegion;
import org.openbp.jaspira.plugins.propertybrowser.PropertyBrowserSetEvent;
import org.openbp.jaspira.util.StandardFlavors;
import org.openbp.swing.SwingUtil;
import org.openbp.swing.components.JMsgBox;

import CH.ifa.draw.framework.Connector;
import CH.ifa.draw.framework.Figure;
import CH.ifa.draw.framework.FigureEnumeration;
import CH.ifa.draw.standard.CompositeFigure;
import CH.ifa.draw.standard.StandardDrawing;

/**
 * A ProcessDrawing is the graphical representation of a OpenBP process.
 * (i.e. a JHotDraw StandardDrawing with Process related elements).
 *
 * @author Stephan Moritz
 */
public class ProcessDrawing extends StandardDrawing
	implements ProcessElementContainer, Modifiable
{
	/** The process this drawing represents */
	private ProcessItem process;

	/** Model qualifier of the process */
	private ModelQualifier processQualifier;

	/** Process skin */
	private Skin processSkin;

	/** Visual status of the drawing (see the constants of the {@link VisualElement} class) */
	private int visualStatus;

	/** Status variable: The process is readonly */
	private boolean readOnly;

	/** Editor that edits this drawing */
	private DrawingEditorPlugin editor;

	/**
	 * Constructor.
	 *
	 * @param process The process this drawing represents
	 * @param editor Editor that edits this drawing
	 */
	public ProcessDrawing(ProcessItem process, DrawingEditorPlugin editor)
	{
		super();

		this.editor = editor;

		setProcess(process);
	}

	/**
	 * Gets the process represented by this drawing.
	 */
	public ProcessItem getProcess()
	{
		return process;
	}

	/**
	 * Sets the process represented by this drawing.
	 *
	 * @param process Process to represent or null to clear.
	 * In the latter case, the method will remove all references of the
	 * current process to drawing objects (figures) from the current process, if any.
* So call setProces (null) after you closing the modeler associated with this drawing. */ public void setProcess(ProcessItem process) { if (this.process != null) { // Clear the references of the current process to the modeler's figures this.process.maintainReferences(ModelObject.UNLINK_FROM_REPRESENTATION); } processSkin = null; if (process != null) { try { this.process = process; process.setRepresentation(this); processQualifier = process.getQualifier(); // Determine the skin for this process processSkin = FigureUtil.determineProcessSkin(process); setTitle(process.getDisplayName()); decodeGeometry(); } catch (RuntimeException e) { String msg; if (e instanceof GeometryException) msg = ExceptionUtil.getNestedMessage(e); else msg = MsgFormat.format("Error parsing geometry information of process $0: {1}", process.getQualifier().toString(), ExceptionUtil.getNestedMessage(e)); JMsgBox.show(null, msg, JMsgBox.ICON_ERROR | JMsgBox.TYPE_OKLATER); throw e; } } } /** * Gets the process skin. */ public Skin getProcessSkin() { return processSkin; } /** * Sets the process skin. */ public void setProcessSkin(Skin processSkin) { this.processSkin = processSkin; } /** * Gets the editor that edits this drawing. */ public DrawingEditorPlugin getEditor() { return editor; } /** * Sets the editor of this drawing. */ public void setEditor(DrawingEditorPlugin editor) { this.editor = editor; } /** * Gets the (only) drawing view of this editor. */ public WorkspaceDrawingView getView() { return editor != null ? (WorkspaceDrawingView) editor.view() : null; } /** * Gets the status variable: The process is readonly. */ public boolean isReadOnly() { return readOnly; } /** * Sets the status variable: The process is readonly. */ public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; } ///////////////////////////////////////////////////////////////////////// // @@ Drawing element access ///////////////////////////////////////////////////////////////////////// /** * Gets all top level figures of this drawing. * * @return An iterator of {@link Figure} objects */ public Iterator getAllFigures() { return getFigureList().iterator(); } /** * Gets all top level figures of this drawing. * * @return A list of {@link Figure} objects */ protected List getFigureList() { List li = new ArrayList(); for (FigureEnumeration fe = figures(); fe.hasMoreElements();) { Figure f = fe.nextFigure(); li.add(f); } return li; } /** * Gets the node figures of this drawing. * * @return An iterator of {@link NodeFigure} objects */ public Iterator getNodeFigures() { return getNodeFigureList().iterator(); } /** * Gets the node figures of this drawing. * * @return A list of {@link NodeFigure} objects */ protected List getNodeFigureList() { List li = new ArrayList(); for (FigureEnumeration fe = figures(); fe.hasMoreElements();) { Figure f = fe.nextFigure(); if (f instanceof NodeFigure) { li.add(f); } } return li; } /** * Gets the parameter connections of this drawing. * * @return An iterator of {@link ParamConnection} objects */ public Iterator getParamConnections() { return getParamConnectionList().iterator(); } /** * Gets the parameter connections of this drawing. * * @return A list of {@link ParamConnection} objects */ protected List getParamConnectionList() { List li = new ArrayList(); for (FigureEnumeration fe = figures(); fe.hasMoreElements();) { Figure f = fe.nextFigure(); if (f instanceof ParamConnection) { li.add(f); } } return li; } /** * Gets the flow connections of this drawing. * * @return An iterator of {@link FlowConnection} objects */ public Iterator getFlowConnections() { return getFlowConnectionList().iterator(); } /** * Gets the flow connections of this drawing. * * @return A list of {@link FlowConnection} objects */ protected List getFlowConnectionList() { List li = new ArrayList(); for (FigureEnumeration fe = figures(); fe.hasMoreElements();) { Figure f = fe.nextFigure(); if (f instanceof FlowConnection) { li.add(f); } } return li; } public Figure add(Figure figure) { if (figure == null) { return null; } Figure result = super.add(figure); if (result instanceof VisualElement) { ((VisualElement) result).setDrawing(this); } return result; } /** * Sets the tag state of all tags of the nodes of this drawing. * * @param stateUpdate State to add to the current state */ public void setTagState(int stateUpdate) { // Change the state of all tag figures of each node figure for (Enumeration drawingFigures = figures(); drawingFigures.hasMoreElements();) { Figure f = (Figure) drawingFigures.nextElement(); if (f instanceof NodeFigure) { for (FigureEnumeration nodeFigures = ((NodeFigure) f).figures(); nodeFigures.hasMoreElements();) { Figure nodeChild = nodeFigures.nextFigure(); if (nodeChild instanceof SocketFigure) { SocketFigure socketFigure = (SocketFigure) nodeChild; int contentState = socketFigure.getContentState(); contentState &= ~(AbstractTagFigure.CONTENT_FLOW | AbstractTagFigure.CONTENT_DATA); contentState |= stateUpdate; socketFigure.setContentState(contentState); } } } } // Positions may have changed, recalculate the connection layout layoutAllConnections(); } /** * Recalculate the layout of all connections. */ public void layoutAllConnections() { // Changing the tag state may require a recomputation of the connection figures for (FigureEnumeration drawingFigures = figures(); drawingFigures.hasMoreElements();) { Figure f = drawingFigures.nextFigure(); if (f instanceof PolySplineConnection) { ((PolySplineConnection) f).layoutConnection(); } } } ////////////////////////////////////////////////// // @@ CompositeFigure overrides ////////////////////////////////////////////////// public Figure remove(Figure figure) { if (figure instanceof ProcessElementContainer) { Figure parent = ((ProcessElementContainer) figure).getParentElement(); if (parent instanceof ProcessDrawing) { return super.remove(figure); } else if (parent instanceof CompositeFigure) { ((CompositeFigure) parent).remove(figure); figure.release(); return figure; } } else { // This will call removeFromContainer and release orphan(figure); } return figure; } /** * Removes a figure from the drawing. * Resets the propertybrowser beforehand. * * @param figure Figure to be removed */ public void removeAndUpdate(Figure figure) { // Clear the PropertyBrowser. editor.fireEvent(new PropertyBrowserSetEvent(editor)); // Update the cut/copy/paste button status editor.fireEvent("global.clipboard.updatestatus"); // Update the modification flag setModified(); // Remove the figure remove(figure); // Update screen // ((WorkspaceDrawingView) editor.view ()).redraw (); editor.repairDamage(); } /** * If the user clicks a flow access (e\.g\. to create a new flow connection), it often happens that * he selects an already existing link to the flow access point. * This is due to the fact that the links appear on top of the nodes. * In order to prevent this, we override this method and use figures() instead of the * original figuresReverse() for enumerating the figures. * @see CH.ifa.draw.standard.StandardDrawing#findFigure(int, int) */ public Figure findFigure(int x, int y) { FigureEnumeration figures = figures(); while (figures.hasMoreElements()) { Figure figure = figures.nextFigure(); if (figure.containsPoint(x, y)) { return figure; } } return null; } /** * Gets a process element container by the model qualifier of the corrresponding process element. * * @param qualifier Model qualifier of the element * @return The element or null if not found */ public ProcessElementContainer getFigureByQualifier(ModelQualifier qualifier) { // We first check if this is the right process. if (!CommonUtil.equalsNull(processQualifier.getModel(), qualifier.getModel())) return null; if (!CommonUtil.equalsNull(processQualifier.getItem(), qualifier.getItem())) return null; if (qualifier.getItemType() != null && !CommonUtil.equalsNull(processQualifier.getItemType(), qualifier.getItemType())) return null; ProcessObject po = process.getProcessElementByName(qualifier.getObjectPath()); if (po == null) return null; if (po.getRepresentation() == null) { Throwable t = new Throwable("No representation for qualifier '" + qualifier + "'."); ExceptionUtil.printTrace(t); } return (ProcessElementContainer) po.getRepresentation(); } ////////////////////////////////////////////////// // @@ Z-order algorithm ////////////////////////////////////////////////// /** * Brings the given figure to front. * Will also bring the parent figure element to the front if its a node figure. * The method will keep the required z-order of the element types (see {@link #recalculateZOrders}). */ public void bringToFront(Figure f) { // check if we have a connection - bring it to the front and finish. if (!(f instanceof PolySplineConnection || f instanceof TextElementFigure)) { // This should be a node object or one of its sub objects while (f instanceof ProcessElementContainer) { if (f instanceof NodeFigure) break; f = ((ProcessElementContainer) f).getParentElement(); } } if (f != null) { super.bringToFront(f); } // Make sure the z-order is correct recalculateZOrders(); } /** Comparator for the item nodes */ private Comparator zComparator = new ZComparator(); /** * Reorders the figures of the drawing according to their preferred z-order. * * The z-order is important for the figure selection mechanisms to work. * The selection will be determined using reverse z-order. This may seem odd, however, there is a * discrepancy between visibility and selection. For example, data links should appear above the nodes * they connect. However, when clicking a node parameter, the parameter should have a higher selection * precedence than the link, which overlaps the parameter a little. An easy solution was to use reverse * z-ordering selection. This works quite fine because the elements of a drawing are usually not stacked. * Sometime, we may implement a more accurate solution. * * The z-order is as follows: * * Text figures are always positioned to the front (i. e. have the highest z-value). * This ensures visibility and also allows easy selection of other figures. * * Splines follow next. They also should appear above nodes, but node elements need to have selection precedence. * * Nodes appear last in the z-order. */ protected void recalculateZOrders() { // Sort according to z-order strategy Collections.sort(fFigures, zComparator); // Reorder z-values int n = fFigures.size(); for (int i = 0; i < n; ++i) { Figure f = (Figure) fFigures.elementAt(i); f.setZValue(i); } // Update low/high z value _nLowestZ = 0; _nHighestZ = n - 1; } // Z order of the figure types: // Line figures in the back, then text element figures, then connections, then node private static Class [] figureClassOrder = new Class [] { PolySplineConnection.class, ProcessVariableConnection.class, NodeFigure.class, TextElementFigure.class, LineFigure.class, }; /** * Gets the z order value for the given figure type class. * * @param f Event * @return Figures with z-value 0 appear in the front of the drawing. * Unknown figures will get z-value 0. */ private static int getFigureTypeZOrder(Figure f) { for (int i = 0; i < figureClassOrder.length; ++i) { if (figureClassOrder [i].isInstance(f)) return i + 1; } return 0; } /** * Z-order comparator. */ private static class ZComparator implements Comparator { public int compare(Object o1, Object o2) { Figure f1 = (Figure) o1; Figure f2 = (Figure) o2; int fo1 = getFigureTypeZOrder(f1); int fo2 = getFigureTypeZOrder(f2); if (fo1 == fo2) { // Identical figures, let the current z-order decide return f1.getZValue() - f2.getZValue(); } return fo1 - fo2; } } ////////////////////////////////////////////////// // @@ DrawingEditor implementation ////////////////////////////////////////////////// public Rectangle displayBox() { int n = fFigures.size(); if (n <= 0) { return new Rectangle(0, 0, 0, 0); } Rectangle r = ((Figure) fFigures.elementAt(0)).displayBox(); for (int i = 1; i < n; ++i) { Figure f = (Figure) fFigures.elementAt(i); if (f instanceof LineFigure) { // Line figures have an inifinite dimension, so we skip them continue; } r.add(f.displayBox()); } // Ensure that the display box of the drawing itself always starts at (0,0) r.x = r.y = 0; return r; } /** * Returns the upper left corner of the used part of the drawing. * @return The origin */ public Point getActualOrigin() { return super.displayBox().getLocation(); } /** * Normalizes the drawing, i\.e\. moves it so that its top left corner lies at 50,50. */ public void normalize() { Point p = getActualOrigin(); moveBy(-p.x + 50, -p.y + 50); setModified(); } ////////////////////////////////////////////////// // @@ Geometry serialization support ////////////////////////////////////////////////// /** * Encodes the geometry of this process, i\.e\. causes all figures to encode their geometry. */ public void encodeGeometry() { // Encode the swim lane geometry String geometry = encodeProcessGeometry(); process.setGeometry(geometry); // Add dependent objects from the drawing for (Enumeration en = figures(); en.hasMoreElements();) { Figure f = (Figure) en.nextElement(); if (f instanceof NodeFigure) { ((NodeFigure) f).encodeGeometry(); } else if (f instanceof ParamConnection) { ((ParamConnection) f).encodeGeometry(); } else if (f instanceof FlowConnection) { ((FlowConnection) f).encodeGeometry(); } else if (f instanceof TextElementFigure) { ((TextElementFigure) f).encodeGeometry(); } } } /** * Encodes the geometry of this process. * Creates figures from the geometry information of the process and * adds them to the drawing. */ public void decodeGeometry() { removeAll(); // Decode the swim lane geometry decodeProcessGeometry(process.getGeometry()); for (Iterator it = process.getNodes(); it.hasNext();) { NodeFigure p = createNodeFigure((Node) it.next()); if (p != null) add(p); } for (Iterator it = process.getControlLinks(); it.hasNext();) { FlowConnection p = createFlowConnection((ControlLink) it.next()); if (p != null) add(p); } for (Iterator it = process.getDataLinks(); it.hasNext();) { ParamConnection p = createParamConnection((DataLink) it.next()); if (p != null) add(p); } for (Iterator it = process.getTextElements(); it.hasNext();) { TextElementFigure p = createTextElementFigure((TextElement) it.next()); if (p != null) { add(p); } } // Make sure the elements appear in the right z-order recalculateZOrders(); // Recalculate the connection layout layoutAllConnections(); } /** * Encodes the swim lane geometry of the process. * * @return Geometry string containing "|" and ":" separated tokens */ protected String encodeProcessGeometry() { StringBuffer sb = new StringBuffer(); // Add dependent objects from the drawing for (Enumeration en = figures(); en.hasMoreElements();) { Figure f = (Figure) en.nextElement(); if (f instanceof LineFigure) { if (sb.length() > 0) sb.append("|"); if (f instanceof VLineFigure) sb.append("vline:"); else sb.append("hline:"); sb.append(((LineFigure) f).encodeGeometry()); } } return sb.length() > 0 ? sb.toString() : null; } /** * Decodes the swim lane geometry of the process. * * @param geometry Geometry string to decode or null */ protected void decodeProcessGeometry(String geometry) { if (geometry == null) return; StringTokenizer tok = new StringTokenizer(geometry, "|"); while (tok.hasMoreTokens()) { String t = tok.nextToken(); int i = t.indexOf(":"); if (i > 0) { String ident = t.substring(0, i); LineFigure lineFigure = null; if (ident.equalsIgnoreCase("vline")) { lineFigure = new VLineFigure(this); } else if (ident.equalsIgnoreCase("hline")) { lineFigure = new HLineFigure(this); } if (lineFigure != null) { lineFigure.decodeGeometry(t.substring(i + 1)); add(lineFigure); } } } } ////////////////////////////////////////////////// // @@ Figure creation ////////////////////////////////////////////////// /** * Creates a new node figure based on the given node. * * @param node The node * @return The new figure or null on error */ public NodeFigure createNodeFigure(Node node) { NodeFigure figure = (NodeFigure) ProcessElementFigureRegistry.getInstance().createProcessElementContainer(node); figure.connect(node, this); return figure; } /** * Creates a text element figure based on the given text element. * * @param textElement The text element * @return The new figure or null on error */ public TextElementFigure createTextElementFigure(TextElement textElement) { TextElementFigure figure = new TextElementFigure(); figure.connect(textElement, this); return figure; } /** * Creates a new flow connection based on the given control link. * * @param link The control link * @return The new figure or null on error */ public FlowConnection createFlowConnection(ControlLink link) { NodeSocket source = link.getSourceSocket(); NodeSocket target = link.getTargetSocket(); if (source == null) { System.err.println("Missing source socket for control link '" + link.getQualifier() + "'"); return null; } if (target == null) { System.err.println("Missing target socket for control link '" + link.getQualifier() + "'"); return null; } SocketFigure sourceFigure = (SocketFigure) source.getRepresentation(); if (sourceFigure == null) { System.err.println("Control link source socket '" + source.getQualifier() + "' has no figure representation."); return null; } SocketFigure targetFigure = (SocketFigure) target.getRepresentation(); if (targetFigure == null) { System.err.println("Control link target socket '" + target.getQualifier() + "' has no figure representation."); return null; } Connector start = sourceFigure.connectorAt(0, 0); Connector end = targetFigure.connectorAt(0, 0); link.unlink(); FlowConnection flow = new FlowConnection(link, this); flow.connectStart(start); flow.connectEnd(end); return flow; } /** * Creates a new parameter connection based on the given data link. * * @param link The data link * @return The new figure or null on error */ public ParamConnection createParamConnection(DataLink link) { Param source = link.getSourceParam(); Param target = link.getTargetParam(); if (source == null) { System.err.println("Missing source parameter for data link '" + link.getQualifier() + "'"); return null; } if (target == null) { System.err.println("Missing target parameter for data link '" + link.getQualifier() + "'"); return null; } ParamFigure sourceFigure = null; ParamFigure targetFigure = null; if (source instanceof NodeParam) { sourceFigure = (ParamFigure) source.getRepresentation(); if (sourceFigure == null) { System.err.println("Data link source parameter '" + source.getQualifier() + "' has no figure representation."); return null; } } if (target instanceof NodeParam) { targetFigure = (ParamFigure) target.getRepresentation(); if (targetFigure == null) { System.err.println("Data link target parameter '" + target.getQualifier() + "' has no figure representation."); return null; } } if (source instanceof ProcessVariable && targetFigure != null) { // Global -> Node targetFigure.setProcessVariableConnection((ProcessVariable) source, link); } else if (target instanceof ProcessVariable && sourceFigure != null) { // Node -> Global sourceFigure.setProcessVariableConnection((ProcessVariable) target, link); } else if (sourceFigure != null && targetFigure != null) { // Node -> Node Connector start = sourceFigure.connectorAt(0, 0); Connector end = targetFigure.connectorAt(0, 0); link.unlink(); return new ParamConnection(link, start, end, this); } return null; } ////////////////////////////////////////////////// // @@ ProcessElementContainer implementation ////////////////////////////////////////////////// public ProcessObject getProcessElement() { return process; } public ProcessObject getReferredProcessElement() { return getProcessElement(); } public Figure selectionOnDelete() { // Can't be deleted, so nop return null; } ////////////////////////////////////////////////// // @@ VisualElement implementation ////////////////////////////////////////////////// public ProcessDrawing getDrawing() { return this; } public void setDrawing(ProcessDrawing processDrawing) { } public VisualElement getParentElement() { return null; } public Figure getPresentationFigure() { return this; } public void updatePresentationFigure() { // No dynamic presentation figure, so do nothing } public boolean isVisible() { return (visualStatus & VisualElement.VISUAL_VISIBLE) != 0; } public void setVisible(boolean visible) { willChange(); if (visible) { visualStatus |= VisualElement.VISUAL_VISIBLE; } else { visualStatus &= ~VisualElement.VISUAL_VISIBLE; } changed(); } public boolean handleEvent(VisualElementEvent event) { VisualElement child = (VisualElement) FigureUtil.findChildFigure(this, event.x, event.y, VisualElement.class); if (child != null) { return child.handleEvent(event); } return false; } public boolean isDisplayAll() { return (visualStatus & VisualElement.VISUAL_DISPLAY_ALL) != 0; } public void setDisplayAll(boolean visible) { willChange(); if (visible) { visualStatus |= VisualElement.VISUAL_DISPLAY_ALL; } else { visualStatus &= ~VisualElement.VISUAL_DISPLAY_ALL; } changed(); } ////////////////////////////////////////////////// // @@ UpdatableFigure implementation ////////////////////////////////////////////////// public void updateFigure() { willChange(); for (FigureEnumeration fe = figures(); fe.hasMoreElements();) { Figure f = fe.nextFigure(); if (f instanceof UpdatableFigure) { ((UpdatableFigure) f).updateFigure(); } } changed(); } ////////////////////////////////////////////////// // @@ Delegation of DisplayObject methods to the process ////////////////////////////////////////////////// public String getName() { return process.getName(); } public void setName(String string) { process.setName(string); } public String getDescription() { return process.getDescription(); } public void setDescription(String string) { process.setDescription(string); } public String getDisplayName() { return process.getDisplayName(); } public void setDisplayName(String string) { process.setDisplayName(string); } public String getDisplayText() { return process.getDisplayText(); } public String getDescriptionText() { return process.getDescriptionText(); } ///////////////////////////////////////////////////////////////////////// // @@ Delegation of Modifiable ///////////////////////////////////////////////////////////////////////// /** * Gets the modified flag for usage by editors. */ public boolean isModified() { return process.isModified(); } /** * Sets the modified flag of the process and updates the save button accordingly. */ public void setModified() { process.setModified(); updateModificationState(); } /** * Clears the modified flag of the process and updates the save button accordingly. */ public void clearModified() { process.clearModified(); updateModificationState(); } /** * Updates the status of the save button according to the modification state of the process. */ public void updateModificationState() { JaspiraAction action = ActionMgr.getInstance().getAction("standard.file.save"); if (action != null) { action.setEnabled(process.isModified()); } } ///////////////////////////////////////////////////////////////////////// // @@ InteractionClient implementation ///////////////////////////////////////////////////////////////////////// public void dragActionTriggered(Object regionId, Point p) { } public void dragEnded(Transferable transferable) { DropClientUtil.dragEnded(this, transferable); } public void dragStarted(Transferable transferable) { DropClientUtil.dragStarted(this, transferable); } public List getAllDropRegions(List flavors, Transferable data, MouseEvent mouseEvent) { return DropClientUtil.getAllDropRegions(this, flavors, data, mouseEvent); } public List getDropRegions(List flavors, Transferable data, MouseEvent mouseEvent) { String regionId = null; if (flavors.contains(ClientFlavors.NODE_PROVIDER)) { regionId = "addNode"; } else if (flavors.contains(ClientFlavors.TEXT_ELEMENT)) { regionId = "addTextElement"; } else if (flavors.contains(ModelerFlavors.HLINE)) { regionId = "addHLine"; } else if (flavors.contains(ModelerFlavors.VLINE)) { regionId = "addVLine"; } if (regionId != null) { WorkspaceDrawingView view = getView(); Rectangle viewSize = new Rectangle(view.getSize()); return Collections.singletonList(new ViewDropRegion(regionId, this, viewSize, view)); } return null; } public List getImportersAt(Point p) { WorkspaceDrawingView view = getDrawing().getView(); JScrollPane pane = SwingUtil.getScrollPaneAncestor(view); Rectangle localBounds = SwingUtil.convertBoundsToGlassCoords(pane.getViewport()); if (localBounds.contains(p)) { List result = new ArrayList(); result.add(new Importer("addNode", this, new DataFlavor [] { ClientFlavors.NODE_PROVIDER })); result.add(new Importer("addTextElement", this, new DataFlavor [] { ClientFlavors.TEXT_ELEMENT })); result.add(new Importer("addHLine", this, new DataFlavor [] { ModelerFlavors.HLINE })); result.add(new Importer("addVLine", this, new DataFlavor [] { ModelerFlavors.VLINE })); return result; } return null; } public List getAllImportersAt(Point p) { WorkspaceDrawingView view = getDrawing().getView(); JScrollPane pane = SwingUtil.getScrollPaneAncestor(view); Rectangle localBounds = SwingUtil.convertBoundsToGlassCoords(pane.getViewport()); if (localBounds.contains(p)) { Point docPoint = SwingUtil.convertFromGlassCoords(p, view); ProcessElementContainer pec = (ProcessElementContainer) FigureUtil.findChildFigure(this, docPoint.x, docPoint.y, ProcessElementContainer.class); if (pec != null && !(pec instanceof PolySplineConnection)) { return pec.getAllImportersAt(p); } return getImportersAt(p); } return null; } public List getSubClients() { return FigureUtil.getTypedFigureList(this, InteractionClient.class); } public boolean importData(Object regionId, Transferable data, Point p) { p = SwingUtil.convertFromGlassCoords(p, getDrawing().getView()); try { Figure addedFigure = null; if ("addNode".equals(regionId)) { NodeProvider np = (NodeProvider) data.getTransferData(StandardFlavors.OBJECT); NodeFigure nodeFigure = createNodeFigureFromNodeProvider(np); if (nodeFigure == null) { // Failed or aborted return false; } editor.startUndo("Add Node"); Node node = nodeFigure.getNode(); process.addNode(node); node.maintainReferences(ModelObject.SYNC_GLOBAL_REFNAMES | ModelObject.SYNC_LOCAL_REFNAMES); nodeFigure.displayBox(new Rectangle(p)); add(nodeFigure); addedFigure = nodeFigure; } else if ("addTextElement".equals(regionId)) { editor.startUndo("Add Text Element"); TextElement textElement = process.createTextElement(); process.addTextElement(textElement); TextElementFigure textElementFigure = createTextElementFigure(textElement); Rectangle db = textElementFigure.displayBox(); db.x = p.x - db.width / 2; db.y = p.y - db.height / 2; textElementFigure.displayBox(db); add(textElementFigure); addedFigure = textElementFigure; } else if ("addHLine".equals(regionId)) { editor.startUndo("Add Horizontal Swimlane Line"); HLineFigure hLineFigure = new HLineFigure(this); Rectangle db = hLineFigure.displayBox(); db.x = p.x; db.y = p.y; hLineFigure.displayBox(db); add(hLineFigure); addedFigure = hLineFigure; } else if ("addVLine".equals(regionId)) { editor.startUndo("Add Vertical Swimlane Line"); VLineFigure vLineFigure = new VLineFigure(this); Rectangle db = vLineFigure.displayBox(); db.x = p.x; db.y = p.y; vLineFigure.displayBox(db); add(vLineFigure); addedFigure = vLineFigure; } if (addedFigure != null) { editor.endUndo(); editor.repairDamage(); // May invoke the autoconnector and others editor.fireEvent("modeler.drawing.figureadded", addedFigure); editor.repairDamage(); } } catch (UnsupportedFlavorException e) { ExceptionUtil.printTrace(e); } catch (IOException e) { ExceptionUtil.printTrace(e); } return false; } /** * Creates a new node, given a node provider. * * @param np The node provider * @return The new node */ private NodeFigure createNodeFigureFromNodeProvider(NodeProvider np) { int syncFlags = ItemSynchronization.SYNC_ALL_EXCEPT_DESCRIPTION; boolean isSkeleton = true; if (np instanceof Item) { Item npItem = (Item) np; if (npItem.getRuntimeAttribute(Modeler.ATTRIBUTE_SKELETON) != null) { // We have a skeleton object. Use its item type to create a new item of this type. Model model = process.getModel(); // Create the item using the item factory String processType = null; if (npItem instanceof ProcessItem) { processType = ((ProcessItem) npItem).getProcessType(); } Item newItem = ItemCreationUtil.createItem(model, null, null, npItem.getItemType(), processType); if (newItem == null) { // Editor cancelled return null; } // The new item will serve as node provider npItem = newItem; } else { // No skeleton, we dragged an existing item. // Check if it is currently being edited by a modeler. isSkeleton = false; AskEvent ae = new AskEvent(editor, "global.edit.geteditedinstance", npItem); editor.fireEvent(ae); Item editedItem = (Item) ae.getAnswer(); if (editedItem != null) { // We will use the edited item instead of the current one as node provider (may be newer!) npItem = editedItem; } } // The new item will serve as node provider np = (NodeProvider) npItem; // Show private entries only if we insert this process as sub process into itself if (!npItem.getQualifier().equals(processQualifier)) { // External reference, hide private entries syncFlags |= ItemSynchronization.SYNC_HIDE_PRIVATE_ENTRIES; } } // Create a node referencing the item Node node = np.toNode(process, syncFlags); node.setProcess(process); // Make sure that the nodename is unique node.setName(process.createUniqueNodeName(node.getName())); NodeFigure nodeFigure = createNodeFigure(node); if (isSkeleton) { nodeFigure.setCreatedFromScratch(true); } return nodeFigure; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy