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

org.yaoqiang.graph.handler.GraphHandler Maven / Gradle / Ivy

package org.yaoqiang.graph.handler;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.TransferHandler;

import org.yaoqiang.graph.model.GraphModel;
import org.yaoqiang.graph.swing.GraphComponent;
import org.yaoqiang.graph.view.Graph;
import org.yaoqiang.util.Constants;

import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.mxGraphComponent.mxGraphControl;
import com.mxgraph.swing.handler.mxCellMarker;
import com.mxgraph.swing.handler.mxGraphHandler;
import com.mxgraph.swing.handler.mxGraphTransferHandler;
import com.mxgraph.swing.util.mxSwingConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;

/**
 * GraphHandler
 * 
 * @author Shi Yaoqiang([email protected])
 */
public class GraphHandler extends mxGraphHandler {

	public GraphHandler(mxGraphComponent graphComponent) {
		super(graphComponent);
	}

	public GraphComponent getGraphComponent() {
		return (GraphComponent) graphComponent;
	}

	protected mxCellMarker createMarker() {
		mxCellMarker marker = new mxCellMarker(graphComponent, Color.BLUE) {
			/**
			 * 
			 */
			private static final long serialVersionUID = -8451338653189373347L;

			/**
			 * 
			 */
			public boolean isEnabled() {
				return graphComponent.getGraph().isDropEnabled();
			}

			/**
			 * 
			 */
			public Object getCell(MouseEvent e) {
				mxIGraphModel model = graphComponent.getGraph().getModel();
				TransferHandler th = graphComponent.getTransferHandler();
				boolean isLocal = th instanceof mxGraphTransferHandler && ((mxGraphTransferHandler) th).isLocalDrag();

				Graph graph = getGraphComponent().getGraph();
				Object cell = super.getCell(e);
				Object[] cells = (isLocal) ? graph.getSelectionCells() : dragCells;
				cell = graph.getDropTarget(cells, e.getPoint(), cell);

				// Checks if parent is dropped into child
				Object parent = cell;

				while (parent != null) {
					if (mxUtils.contains(cells, parent)) {
						return null;
					}

					parent = model.getParent(parent);
				}

				boolean clone = graphComponent.isCloneEvent(e) && cloneEnabled;

				if (isLocal && cell != null && cells.length > 0 && !clone && graph.getModel().getParent(cells[0]) == cell) {
					cell = null;
				}

				// ==============start==============
				if (graph.getSelectionCells().length > 0) {
					Object selectedCell = graph.getSelectionCells()[0];
					if (graph.getModel().isBoundaryEvent(selectedCell)) {
						cell = graph.getModel().getParent(selectedCell);
					}
				}
				// ==============end================

				return cell;
			}

		};

		// Swimlane content area will not be transparent drop targets
		marker.setSwimlaneContentEnabled(true);

		return marker;
	}

	protected Cursor getCursor(MouseEvent e) {
		Cursor cursor = null;

		if (isMoveEnabled()) {
			Object cell = graphComponent.getCellAt(e.getX(), e.getY(), false);

			if (cell != null) {
				if (graphComponent.isFoldingEnabled() && graphComponent.hitFoldingIcon(cell, e.getX(), e.getY())) {
					cursor = FOLD_CURSOR;
				} else if (graphComponent.getGraph().isCellMovable(cell)) {
					// ==============start==============
					if (graphComponent.getGraph().getModel().isEdge(cell)) {
						cursor = new Cursor(Cursor.HAND_CURSOR);
					} else {
						cursor = MOVE_CURSOR;
					}
					// ==============end================
				}
			}
		}

		return cursor;
	}

	public void mousePressed(MouseEvent e) {
		if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed() && !graphComponent.isForceMarqueeEvent(e)) {
			cell = graphComponent.getCellAt(e.getX(), e.getY(), false);
			// ==============start==============
			Graph graph = getGraphComponent().getGraph();
			GraphModel model = graph.getModel();
			if (model.isChoreographyTask(cell) || model.isChoreographySubprocess(cell) || model.isChoreographyParticipant(cell)) {
				cell = model.getParent(cell);
			}
			// ==============end================
			initialCell = cell;

			if (cell != null) {
				if (isSelectEnabled() && !graphComponent.getGraph().isCellSelected(cell)) {
					graphComponent.selectCellForEvent(cell, e);
					cell = null;
				}

				// Starts move if the cell under the mouse is movable and/or any
				// cells of the selection are movable
				if (isMoveEnabled() && !e.isPopupTrigger()) {
					start(e);
					e.consume();
				}
			} else if (e.isPopupTrigger()) {
				graphComponent.getGraph().clearSelection();
			}
		}
	}

	public void mouseDragged(MouseEvent e) {
		// LATER: Check scrollborder, use scroll-increments, do not
		// scroll when over ruler dragging from library
		graphComponent.getGraphControl().scrollRectToVisible(new Rectangle(e.getPoint()));

		if (!e.isConsumed()) {
			gridEnabledEvent = graphComponent.isGridEnabledEvent(e);
			constrainedEvent = graphComponent.isConstrainedEvent(e);
			Graph graph = getGraphComponent().getGraph();

			if (constrainedEvent && first != null) {
				int x = e.getX();
				int y = e.getY();

				// ==============start==============
				mxCell cell = (mxCell) graph.getSelectionCell();
				if (graph.getModel().isBoundaryEvent(cell)) {
					mxCell parent = (mxCell) graph.getModel().getParent(cell);
					Rectangle rect = graph.getView().getState(parent).getRectangle();

					Rectangle eventRect = graph.getView().getState(cell).getRectangle();
					if (Math.abs(e.getX() - first.x) > Math.abs(e.getY() - first.y)) {
						if (first.y - eventRect.getHeight() / 2 <= rect.getY() || first.y + eventRect.getHeight() / 2 >= (rect.getY() + rect.getHeight())) {
							if (x > (rect.getX() + rect.getWidth())) {
								x = (int) ((rect.getX() + rect.getWidth()));
							} else if (x < rect.getX()) {
								x = (int) (rect.getX());
							}
							y = first.y;
						} else {
							x = first.x;
							y = first.y;
						}
					} else {
						if (first.x - eventRect.getWidth() / 2 <= rect.getX() || first.x + eventRect.getWidth() / 2 >= (rect.getX() + rect.getWidth())) {
							if (y > (rect.getY() + rect.getHeight())) {
								y = (int) ((rect.getY() + rect.getHeight()));
							} else if (y < rect.getY()) {
								y = (int) (rect.getY());
							}
							x = first.x;
						} else {
							x = first.x;
							y = first.y;
						}
					}
				} else if (graph.getModel().isMessage(cell)) {
					mxCell parent = (mxCell) graph.getModel().getParent(cell);
					if (graph.getModel().isMessageFlow(parent)) {
						mxCellState state = graph.getView().getState(parent);
						double scale = graph.getView().getScale();
						List points = new ArrayList(state.getAbsolutePoints());
						mxPoint[] p = points.toArray(new mxPoint[points.size()]);

						Rectangle rect = state.getRectangle();
						if (x > (rect.getX() + rect.getWidth())) {
							x = (int) ((rect.getX() + rect.getWidth()));
						} else if (x < rect.getX()) {
							x = (int) (rect.getX());
						} else {
							y = (int) p[1].getY();
						}

						if (y > (rect.getY() + rect.getHeight())) {
							y = (int) ((rect.getY() + rect.getHeight()));
						} else if (y < rect.getY()) {
							y = (int) (rect.getY());
						}

						if (Math.abs(x - p[0].getX()) < 5 * scale) {
							if (p[0].getY() > p[1].getY()) {
								if (y < p[1].getY()) {
									y = (int) p[1].getY();
								}
							} else {
								if (y > p[1].getY()) {
									y = (int) p[1].getY();
								}
							}
						} else if (Math.abs(x - p[2].getX()) < 5 * scale) {
							if (p[p.length - 1].getY() > p[p.length - 2].getY()) {
								if (y < p[p.length - 2].getY()) {
									y = (int) p[p.length - 2].getY();
								}
							} else if ((int) p[p.length - 1].getY() == (int) p[p.length - 2].getY()) {
								y = (int) p[p.length - 2].getY();
							} else if (p[p.length - 1].getY() < p[p.length - 2].getY()) {
								if (y > p[p.length - 2].getY()) {
									y = (int) p[p.length - 2].getY();
								}
							} else if (y > p[p.length - 1].getY()) {
								y = (int) p[p.length - 2].getY();
							}
						}

						if ((int) p[0].getX() == (int) p[p.length - 1].getX()) {
							x = first.x;
						}
					}
				} else if (graph.getModel().isPool(cell)) {
					if (graph.isVerticalSwimlane(cell)) {
						y = first.y;
					} else {
						x = first.x;
					}
				} else {
					if (Math.abs(e.getX() - first.x) > Math.abs(e.getY() - first.y)) {
						y = first.y;
					} else {
						x = first.x;
					}
				}
				// ==============end================
				e = new MouseEvent(e.getComponent(), e.getID(), e.getWhen(), e.getModifiers(), x, y, e.getClickCount(), e.isPopupTrigger(), e.getButton());
			}

			if (isVisible() && isMarkerEnabled()) {
				marker.process(e);
			}

			if (first != null) {
				if (movePreview.isActive()) {
					double dx = e.getX() - first.x;
					double dy = e.getY() - first.y;

					if (graphComponent.isGridEnabledEvent(e)) {
						dx = graph.snap(dx);
						dy = graph.snap(dy);
					}

					boolean clone = isCloneEnabled() && graphComponent.isCloneEvent(e);
					movePreview.update(e, dx, dy, clone);
					e.consume();
				} else if (cellBounds != null) {
					double dx = e.getX() - first.x;
					double dy = e.getY() - first.y;

					if (previewBounds != null) {
						setPreviewBounds(new Rectangle(getPreviewLocation(e, gridEnabledEvent), previewBounds.getSize()));
					}

					if (!isVisible() && graphComponent.isSignificant(dx, dy)) {
						if (imagePreview && dragImage == null && !graphComponent.isDragEnabled()) {
							updateDragImage(cells);
						}

						setVisible(true);
					}

					e.consume();
				}
			}
		}
	}

	public void mouseReleased(MouseEvent e) {
		if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()) {
			Graph graph = getGraphComponent().getGraph();
			double dx = 0;
			double dy = 0;

			if (first != null && (cellBounds != null || movePreview.isActive())) {
				double scale = graph.getView().getScale();
				mxPoint trans = graph.getView().getTranslate();

				// TODO: Simplify math below, this was copy pasted from
				// getPreviewLocation with the rounding removed
				dx = e.getX() - first.x;
				dy = e.getY() - first.y;

				if (cellBounds != null && bbox != null) {
					double dxg = ((cellBounds.getX() + dx) / scale) - trans.getX();
					double dyg = ((cellBounds.getY() + dy) / scale) - trans.getY();

					if (gridEnabledEvent) {
						dxg = graph.snap(dxg);
						dyg = graph.snap(dyg);
					}

					double x = ((dxg + trans.getX()) * scale) + (bbox.getX()) - (cellBounds.getX());
					double y = ((dyg + trans.getY()) * scale) + (bbox.getY()) - (cellBounds.getY());

					dx = Math.round((x - bbox.getX()) / scale);
					dy = Math.round((y - bbox.getY()) / scale);
				}
			}

			if (first == null || !graphComponent.isSignificant(e.getX() - first.x, e.getY() - first.y)) {
				// Delayed handling of selection
				if (cell != null && !e.isPopupTrigger() && isSelectEnabled() && (first != null || !isMoveEnabled())) {
					graphComponent.selectCellForEvent(cell, e);
				}

				// Delayed folding for cell that was initially under the mouse
				if (graphComponent.isFoldingEnabled() && graphComponent.hitFoldingIcon(initialCell, e.getX(), e.getY())) {
					fold(initialCell);
				} else {
					// Handles selection if no cell was initially under the mouse
					Object tmp = graphComponent.getCellAt(e.getX(), e.getY(), graphComponent.isSwimlaneSelectionEnabled());

					if (cell == null && first == null) {
						if (tmp == null) {
							if (!graphComponent.isToggleEvent(e)) {
								graph.clearSelection();
							}
						} else if (graph.isSwimlane(tmp)
								&& graphComponent.getCanvas().hitSwimlaneContent(graphComponent, graph.getView().getState(tmp), e.getX(), e.getY())) {
							graphComponent.selectCellForEvent(tmp, e);
						}
					}

					if (graphComponent.isFoldingEnabled() && graphComponent.hitFoldingIcon(tmp, e.getX(), e.getY())) {
						fold(tmp);
						e.consume();
					}
				}
			} else if (movePreview.isActive()) {
				if (graphComponent.isConstrainedEvent(e)) {
					if (Math.abs(dx) > Math.abs(dy)) {
						dy = 0;
					} else {
						dx = 0;
					}
				}

				mxCellState markedState = marker.getMarkedState();
				Object target = (markedState != null) ? markedState.getCell() : null;

				// FIXME: Cell is null if selection was carried out, need other variable
				// trace("cell", cell);

				if (target == null && isRemoveCellsFromParent() && shouldRemoveCellFromParent(graph.getModel().getParent(initialCell), cells, e)) {
					target = graph.getDefaultParent();
				}

				boolean clone = isCloneEnabled() && graphComponent.isCloneEvent(e);
				Object[] result = movePreview.stop(true, e, dx, dy, clone, target);

				if (cells != result) {
					graph.setSelectionCells(result);
				}

				e.consume();
			} else if (isVisible()) {
				// ==============start==============
				if (constrainedEvent) {
					mxCell cell = (mxCell) graph.getSelectionCell();
					if (graph.getModel().isBoundaryEvent(cell)) {
						mxCell parent = (mxCell) graph.getModel().getParent(cell);
						if (parent.getGeometry() == null) {
							return;
						}
						Rectangle rect = graph.getView().getState(parent).getRectangle();
						mxGeometry geo = cell.getGeometry();
						mxPoint eventOffset = geo.getOffset();

						if (Math.abs(dx) > Math.abs(dy)) {
							if (first.x + dx > rect.getX() + rect.getWidth()) {
								dx = rect.getX() + rect.getWidth() - first.x;
							} else if (first.x + dx < rect.getX()) {
								dx = rect.getX() - first.x;
							}
							dy = 0;
							if (eventOffset.getY() < -parent.getGeometry().getHeight() / 2) { // align subprocess top
								geo.setY(0);
								eventOffset.setY(-geo.getWidth() / 2);
							} else if (geo.getY() == 0) {
								if (geo.getX() == 1) {
									geo.setX(0);
									if (eventOffset.getY() > 0) {
										geo.setY(1);
										eventOffset.setY(-geo.getWidth() / 2);
									}
									eventOffset.setX(parent.getGeometry().getWidth() + eventOffset.getX());
								} else {
									if (eventOffset.getY() > 0) {
										geo.setY(1);
									}
									eventOffset.setY(-geo.getWidth() / 2);
								}
							} else { // align subprocess bottom
								geo.setY(1);
								eventOffset.setY(-geo.getWidth() / 2);
							}
						} else {
							if (first.y + dy > rect.getY() + rect.getHeight()) {
								dy = rect.getY() + rect.getHeight() - first.y;
							} else if (first.y + dy < rect.getY()) {
								dy = rect.getY() - first.y;
							}
							dx = 0;
							if (eventOffset.getX() > 0) { // align subprocess right
								geo.setX(1);
								eventOffset.setX(-geo.getWidth() / 2);
								if (geo.getY() == 1) {
									geo.setY(0);
									eventOffset.setY(parent.getGeometry().getHeight() + eventOffset.getY());
								}
							} else if (geo.getX() == 1) {
							} else { // align subprocess left
								geo.setX(0);
								eventOffset.setX(-geo.getWidth() / 2);
								if (geo.getY() == 1) {
									geo.setY(0);
									eventOffset.setY(parent.getGeometry().getHeight() + eventOffset.getY());
								}
							}
						}

						geo.setOffset(eventOffset);
						geo.setAlternateBounds(null);
						graph.getModel().setGeometry(cell, geo);
					} else if (graph.getModel().isMessage(cell)) {
						mxCell parent = (mxCell) graph.getModel().getParent(cell);
						if (graph.getModel().isMessageFlow(parent)) {
							mxCellState state = graph.getView().getState(parent);
							double scale = graph.getView().getScale();
							List points = new ArrayList(state.getAbsolutePoints());
							mxPoint[] p = points.toArray(new mxPoint[points.size()]);
							Rectangle rect = graph.getView().getState(parent).getRectangle();

							if (first.x + dx > rect.getX() + rect.getWidth()) {
								dx = rect.getX() + rect.getWidth() - first.x;
							} else if (first.x + dx < rect.getX()) {
								dx = rect.getX() - first.x;
							} else {
								dy = (int) p[1].getY() - first.y;
							}
							if (first.y + dy > rect.getY() + rect.getHeight()) {
								dy = rect.getY() + rect.getHeight() - first.y;
							} else if (first.y + dy < rect.getY()) {
								dy = rect.getY() - first.y;
							}

							if (Math.abs(first.x + dx - p[0].getX()) < 5 * scale) {
								if (p[0].getY() > p[1].getY()) {
									if (first.y + dy < p[1].getY()) {
										dy = p[1].getY() - first.y;
									}
								} else {
									if (first.y + dy > p[1].getY()) {
										dy = p[1].getY() - first.y;
									}
								}
							} else if (Math.abs(first.x + dx - p[2].getX()) < 5 * scale) {
								if (p[p.length - 1].getY() > p[p.length - 2].getY()) {
									if (first.y + dy < p[p.length - 2].getY()) {
										dy = p[p.length - 2].getY() - first.y;
									}
								} else if ((int) p[p.length - 1].getY() == (int) p[p.length - 2].getY()) {
									dy = p[p.length - 2].getY() - first.y;
								} else if (p[p.length - 1].getY() < p[p.length - 2].getY()) {
									if (first.y + dy > p[p.length - 2].getY()) {
										dy = p[p.length - 2].getY() - first.y;
									}
								} else if (first.y + dy > p[p.length - 1].getY()) {
									dy = (int) p[p.length - 2].getY() - first.y;
								}
							}

							if ((int) p[0].getX() == (int) p[p.length - 1].getX()) {
								dx = 0;
							}

						}
					} else if (graph.getModel().isPool(cell)) {
						if (graph.isVerticalSwimlane(cell)) {
							dy = 0;
						} else {
							dx = 0;
						}
					} else {
						if (Math.abs(dx) > Math.abs(dy)) {
							dy = 0;
						} else {
							dx = 0;
						}
					}
				}
				// ==============end================
				mxCellState targetState = marker.getValidState();
				Object target = (targetState != null) ? targetState.getCell() : null;

				if (graph.isSplitEnabled() && graph.isSplitTarget(target, cells)) {
					graph.splitEdge(target, cells, dx, dy);
				} else {
					moveCells(cells, dx, dy, target, e);
				}

				e.consume();
			}
		}

		reset();
	}

	public void paint(Graphics g) {
		if (isVisible() && previewBounds != null) {
			if (dragImage != null) {
				// LATER: Clipping with mxUtils doesnt fix the problem
				// of the drawImage being painted over the scrollbars
				Graphics2D tmp = (Graphics2D) g.create();

				if (graphComponent.getPreviewAlpha() < 1) {
					tmp.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, graphComponent.getPreviewAlpha()));
				}

				tmp.drawImage(dragImage.getImage(), previewBounds.x, previewBounds.y, dragImage.getIconWidth(), dragImage.getIconHeight(), null);
				// ==============start==============
				if (cells == null || cells != null && cells.length == 1) {
					if (Constants.SETTINGS.getProperty("showAuxiliary", "0").equals("1")) {
						getGraphComponent().paintAuxiliaryLines(tmp, previewBounds);
					}
				}
				// ==============end================
				tmp.dispose();
			} else if (!imagePreview) {
				mxSwingConstants.PREVIEW_BORDER.paintBorder(graphComponent, g, previewBounds.x, previewBounds.y, previewBounds.width, previewBounds.height);
			}
		}
	}

	protected Point getPreviewLocation(MouseEvent e, boolean gridEnabled) {
		int x = 0;
		int y = 0;

		if (first != null && cellBounds != null) {
			mxGraph graph = graphComponent.getGraph();
			double scale = graph.getView().getScale();
			mxPoint trans = graph.getView().getTranslate();

			// LATER: Drag image _size_ depends on the initial position and may sometimes
			// not align with the grid when dragging. This is because the rounding of the width
			// and height at the initial position may be different than that at the current
			// position as the left and bottom side of the shape must align to the grid lines.
			// Only fix is a full repaint of the drag cells at each new mouse location.
			double dx = e.getX() - first.x;
			double dy = e.getY() - first.y;

			double dxg = ((cellBounds.getX() + dx) / scale) - trans.getX();
			double dyg = ((cellBounds.getY() + dy) / scale) - trans.getY();

			if (gridEnabled) {
				dxg = graph.snap(dxg);
				dyg = graph.snap(dyg);
			}

			// ==============start==============
			x = (int) Math.round((dxg + trans.getX()) * scale);
			y = (int) Math.round((dyg + trans.getY()) * scale);
			// ==============end================
		}

		return new Point(x, y);
	}

	public void setPreviewBounds(Rectangle bounds) {
		if ((bounds == null && previewBounds != null) || (bounds != null && previewBounds == null)
				|| (bounds != null && previewBounds != null && !bounds.equals(previewBounds))) {
			Rectangle dirty = null;

			if (isVisible()) {
				dirty = previewBounds;

				if (dirty != null) {
					dirty.add(bounds);
				} else {
					dirty = bounds;
				}
			}

			previewBounds = bounds;

			if (dirty != null) {
				graphComponent.getGraphControl().repaint(dirty.x - 1, dirty.y - 1, dirty.width + 2, dirty.height + 2);
			}
			// ==============start==============
			if (previewBounds != null) {
				repaintAuxiliaryArea(previewBounds);
			}
			// ==============end================
		}
	}

	public void reset() {
		super.reset();
		// ==============start==============
		if (previewBounds != null) {
			repaintAuxiliaryArea(previewBounds);
		}
		// ==============end================
	}

	protected void repaintAuxiliaryArea(Rectangle bounds) {
		if (Constants.SETTINGS.getProperty("showAuxiliary", "0").equals("1")) {
			mxGraphControl graphControl = graphComponent.getGraphControl();
			graphControl.repaint(bounds.x - 1, 0, bounds.width + 2, graphControl.getHeight());
			graphControl.repaint(0, bounds.y - 1, graphControl.getWidth(), bounds.height + 2);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy