123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxConstraintHandler
- *
- * Handles constraints on connection targets. This class is in charge of
- * showing fixed points when the mouse is over a vertex and handles constraints
- * to establish new connections.
- *
- * Constructor: mxConstraintHandler
- *
- * Constructs an new constraint handler.
- *
- * Parameters:
- *
- * graph - Reference to the enclosing <mxGraph>.
- * factoryMethod - Optional function to create the edge. The function takes
- * the source and target <mxCell> as the first and second argument and
- * returns the <mxCell> that represents the new edge.
- */
- function mxConstraintHandler(graph)
- {
- this.graph = graph;
-
- // Adds a graph model listener to update the current focus on changes
- this.resetHandler = mxUtils.bind(this, function(sender, evt)
- {
- if (this.currentFocus != null && this.graph.view.getState(this.currentFocus.cell) == null)
- {
- this.reset();
- }
- else
- {
- this.redraw();
- }
- });
-
- this.graph.model.addListener(mxEvent.CHANGE, this.resetHandler);
- this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.resetHandler);
- this.graph.view.addListener(mxEvent.TRANSLATE, this.resetHandler);
- this.graph.view.addListener(mxEvent.SCALE, this.resetHandler);
- this.graph.addListener(mxEvent.ROOT, this.resetHandler);
- };
- /**
- * Variable: pointImage
- *
- * <mxImage> to be used as the image for fixed connection points.
- */
- mxConstraintHandler.prototype.pointImage = new mxImage(mxClient.imageBasePath + '/point.gif', 5, 5);
- /**
- * Variable: graph
- *
- * Reference to the enclosing <mxGraph>.
- */
- mxConstraintHandler.prototype.graph = null;
- /**
- * Variable: enabled
- *
- * Specifies if events are handled. Default is true.
- */
- mxConstraintHandler.prototype.enabled = true;
- /**
- * Variable: highlightColor
- *
- * Specifies the color for the highlight. Default is <mxConstants.DEFAULT_VALID_COLOR>.
- */
- mxConstraintHandler.prototype.highlightColor = mxConstants.DEFAULT_VALID_COLOR;
- /**
- * Function: isEnabled
- *
- * Returns true if events are handled. This implementation
- * returns <enabled>.
- */
- mxConstraintHandler.prototype.isEnabled = function()
- {
- return this.enabled;
- };
-
- /**
- * Function: setEnabled
- *
- * Enables or disables event handling. This implementation
- * updates <enabled>.
- *
- * Parameters:
- *
- * enabled - Boolean that specifies the new enabled state.
- */
- mxConstraintHandler.prototype.setEnabled = function(enabled)
- {
- this.enabled = enabled;
- };
- /**
- * Function: reset
- *
- * Resets the state of this handler.
- */
- mxConstraintHandler.prototype.reset = function()
- {
- if (this.focusIcons != null)
- {
- for (var i = 0; i < this.focusIcons.length; i++)
- {
- this.focusIcons[i].destroy();
- }
-
- this.focusIcons = null;
- }
-
- if (this.focusHighlight != null)
- {
- this.focusHighlight.destroy();
- this.focusHighlight = null;
- }
-
- this.currentConstraint = null;
- this.currentFocusArea = null;
- this.currentPoint = null;
- this.currentFocus = null;
- this.focusPoints = null;
- };
- /**
- * Function: getTolerance
- *
- * Returns the tolerance to be used for intersecting connection points. This
- * implementation returns <mxGraph.tolerance>.
- *
- * Parameters:
- *
- * me - <mxMouseEvent> whose tolerance should be returned.
- */
- mxConstraintHandler.prototype.getTolerance = function(me)
- {
- return this.graph.getTolerance();
- };
- /**
- * Function: getImageForConstraint
- *
- * Returns the tolerance to be used for intersecting connection points.
- */
- mxConstraintHandler.prototype.getImageForConstraint = function(state, constraint, point)
- {
- return this.pointImage;
- };
- /**
- * Function: isEventIgnored
- *
- * Returns true if the given <mxMouseEvent> should be ignored in <update>. This
- * implementation always returns false.
- */
- mxConstraintHandler.prototype.isEventIgnored = function(me, source)
- {
- return false;
- };
- /**
- * Function: isStateIgnored
- *
- * Returns true if the given state should be ignored. This always returns false.
- */
- mxConstraintHandler.prototype.isStateIgnored = function(state, source)
- {
- return false;
- };
- /**
- * Function: destroyIcons
- *
- * Destroys the <focusIcons> if they exist.
- */
- mxConstraintHandler.prototype.destroyIcons = function()
- {
- if (this.focusIcons != null)
- {
- for (var i = 0; i < this.focusIcons.length; i++)
- {
- this.focusIcons[i].destroy();
- }
-
- this.focusIcons = null;
- this.focusPoints = null;
- }
- };
- /**
- * Function: destroyFocusHighlight
- *
- * Destroys the <focusHighlight> if one exists.
- */
- mxConstraintHandler.prototype.destroyFocusHighlight = function()
- {
- if (this.focusHighlight != null)
- {
- this.focusHighlight.destroy();
- this.focusHighlight = null;
- }
- };
- /**
- * Function: isKeepFocusEvent
- *
- * Returns true if the current focused state should not be changed for the given event.
- * This returns true if shift and alt are pressed.
- */
- mxConstraintHandler.prototype.isKeepFocusEvent = function(me)
- {
- return mxEvent.isShiftDown(me.getEvent());
- };
- /**
- * Function: getCellForEvent
- *
- * Returns the cell for the given event.
- */
- mxConstraintHandler.prototype.getCellForEvent = function(me, point)
- {
- var cell = me.getCell();
-
- // Gets cell under actual point if different from event location
- if (cell == null && point != null && (me.getGraphX() != point.x || me.getGraphY() != point.y))
- {
- cell = this.graph.getCellAt(point.x, point.y);
- }
-
- // Uses connectable parent vertex if one exists
- if (cell != null && !this.graph.isCellConnectable(cell))
- {
- var parent = this.graph.getModel().getParent(cell);
-
- if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent))
- {
- cell = parent;
- }
- }
-
- return (this.graph.isCellLocked(cell)) ? null : cell;
- };
- /**
- * Function: update
- *
- * Updates the state of this handler based on the given <mxMouseEvent>.
- * Source is a boolean indicating if the cell is a source or target.
- */
- mxConstraintHandler.prototype.update = function(me, source, existingEdge, point)
- {
- if (this.isEnabled() && !this.isEventIgnored(me))
- {
- // Lazy installation of mouseleave handler
- if (this.mouseleaveHandler == null && this.graph.container != null)
- {
- this.mouseleaveHandler = mxUtils.bind(this, function()
- {
- this.reset();
- });
- mxEvent.addListener(this.graph.container, 'mouseleave', this.resetHandler);
- }
-
- var tol = this.getTolerance(me);
- var x = (point != null) ? point.x : me.getGraphX();
- var y = (point != null) ? point.y : me.getGraphY();
- var grid = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol);
- var mouse = new mxRectangle(me.getGraphX() - tol, me.getGraphY() - tol, 2 * tol, 2 * tol);
- var state = this.graph.view.getState(this.getCellForEvent(me, point));
- // Keeps focus icons visible while over vertex bounds and no other cell under mouse or shift is pressed
- if (!this.isKeepFocusEvent(me) && (this.currentFocusArea == null || this.currentFocus == null ||
- (state != null) || !this.graph.getModel().isVertex(this.currentFocus.cell) ||
- !mxUtils.intersects(this.currentFocusArea, mouse)) && (state != this.currentFocus))
- {
- this.currentFocusArea = null;
- this.currentFocus = null;
- this.setFocus(me, state, source);
- }
- this.currentConstraint = null;
- this.currentPoint = null;
- var minDistSq = null;
-
- if (this.focusIcons != null && this.constraints != null &&
- (state == null || this.currentFocus == state))
- {
- var cx = mouse.getCenterX();
- var cy = mouse.getCenterY();
-
- for (var i = 0; i < this.focusIcons.length; i++)
- {
- var dx = cx - this.focusIcons[i].bounds.getCenterX();
- var dy = cy - this.focusIcons[i].bounds.getCenterY();
- var tmp = dx * dx + dy * dy;
-
- if ((this.intersects(this.focusIcons[i], mouse, source, existingEdge) || (point != null &&
- this.intersects(this.focusIcons[i], grid, source, existingEdge))) &&
- (minDistSq == null || tmp < minDistSq))
- {
- this.currentConstraint = this.constraints[i];
- this.currentPoint = this.focusPoints[i];
- minDistSq = tmp;
-
- var tmp = this.focusIcons[i].bounds.clone();
- tmp.grow(mxConstants.HIGHLIGHT_SIZE + 1);
- tmp.width -= 1;
- tmp.height -= 1;
-
- if (this.focusHighlight == null)
- {
- var hl = this.createHighlightShape();
- hl.dialect = (this.graph.dialect == mxConstants.DIALECT_SVG) ?
- mxConstants.DIALECT_SVG : mxConstants.DIALECT_VML;
- hl.pointerEvents = false;
- hl.init(this.graph.getView().getOverlayPane());
- this.focusHighlight = hl;
-
- var getState = mxUtils.bind(this, function()
- {
- return (this.currentFocus != null) ? this.currentFocus : state;
- });
-
- mxEvent.redirectMouseEvents(hl.node, this.graph, getState);
- }
- this.focusHighlight.bounds = tmp;
- this.focusHighlight.redraw();
- }
- }
- }
-
- if (this.currentConstraint == null)
- {
- this.destroyFocusHighlight();
- }
- }
- else
- {
- this.currentConstraint = null;
- this.currentFocus = null;
- this.currentPoint = null;
- }
- };
- /**
- * Function: redraw
- *
- * Transfers the focus to the given state as a source or target terminal. If
- * the handler is not enabled then the outline is painted, but the constraints
- * are ignored.
- */
- mxConstraintHandler.prototype.redraw = function()
- {
- if (this.currentFocus != null && this.constraints != null && this.focusIcons != null)
- {
- var state = this.graph.view.getState(this.currentFocus.cell);
- this.currentFocus = state;
- this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
-
- for (var i = 0; i < this.constraints.length; i++)
- {
- var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
- var img = this.getImageForConstraint(state, this.constraints[i], cp);
- var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
- Math.round(cp.y - img.height / 2), img.width, img.height);
- this.focusIcons[i].bounds = bounds;
- this.focusIcons[i].redraw();
- this.currentFocusArea.add(this.focusIcons[i].bounds);
- this.focusPoints[i] = cp;
- }
- }
- };
- /**
- * Function: setFocus
- *
- * Transfers the focus to the given state as a source or target terminal. If
- * the handler is not enabled then the outline is painted, but the constraints
- * are ignored.
- */
- mxConstraintHandler.prototype.setFocus = function(me, state, source)
- {
- this.constraints = (state != null && !this.isStateIgnored(state, source) &&
- this.graph.isCellConnectable(state.cell)) ? ((this.isEnabled()) ?
- (this.graph.getAllConnectionConstraints(state, source) || []) : []) : null;
- // Only uses cells which have constraints
- if (this.constraints != null)
- {
- this.currentFocus = state;
- this.currentFocusArea = new mxRectangle(state.x, state.y, state.width, state.height);
-
- if (this.focusIcons != null)
- {
- for (var i = 0; i < this.focusIcons.length; i++)
- {
- this.focusIcons[i].destroy();
- }
-
- this.focusIcons = null;
- this.focusPoints = null;
- }
-
- this.focusPoints = [];
- this.focusIcons = [];
-
- for (var i = 0; i < this.constraints.length; i++)
- {
- var cp = this.graph.getConnectionPoint(state, this.constraints[i]);
- var img = this.getImageForConstraint(state, this.constraints[i], cp);
- var src = img.src;
- var bounds = new mxRectangle(Math.round(cp.x - img.width / 2),
- Math.round(cp.y - img.height / 2), img.width, img.height);
- var icon = new mxImageShape(bounds, src);
- icon.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
- mxConstants.DIALECT_MIXEDHTML : mxConstants.DIALECT_SVG;
- icon.preserveImageAspect = false;
- icon.init(this.graph.getView().getDecoratorPane());
-
- // Fixes lost event tracking for images in quirks / IE8 standards
- if (mxClient.IS_QUIRKS || document.documentMode == 8)
- {
- mxEvent.addListener(icon.node, 'dragstart', function(evt)
- {
- mxEvent.consume(evt);
-
- return false;
- });
- }
-
- // Move the icon behind all other overlays
- if (icon.node.previousSibling != null)
- {
- icon.node.parentNode.insertBefore(icon.node, icon.node.parentNode.firstChild);
- }
- var getState = mxUtils.bind(this, function()
- {
- return (this.currentFocus != null) ? this.currentFocus : state;
- });
-
- icon.redraw();
- mxEvent.redirectMouseEvents(icon.node, this.graph, getState);
- this.currentFocusArea.add(icon.bounds);
- this.focusIcons.push(icon);
- this.focusPoints.push(cp);
- }
-
- this.currentFocusArea.grow(this.getTolerance(me));
- }
- else
- {
- this.destroyIcons();
- this.destroyFocusHighlight();
- }
- };
- /**
- * Function: createHighlightShape
- *
- * Create the shape used to paint the highlight.
- *
- * Returns true if the given icon intersects the given point.
- */
- mxConstraintHandler.prototype.createHighlightShape = function()
- {
- var hl = new mxRectangleShape(null, this.highlightColor, this.highlightColor, mxConstants.HIGHLIGHT_STROKEWIDTH);
- hl.opacity = mxConstants.HIGHLIGHT_OPACITY;
-
- return hl;
- };
- /**
- * Function: intersects
- *
- * Returns true if the given icon intersects the given rectangle.
- */
- mxConstraintHandler.prototype.intersects = function(icon, mouse, source, existingEdge)
- {
- return mxUtils.intersects(icon.bounds, mouse);
- };
- /**
- * Function: destroy
- *
- * Destroy this handler.
- */
- mxConstraintHandler.prototype.destroy = function()
- {
- this.reset();
-
- if (this.resetHandler != null)
- {
- this.graph.model.removeListener(this.resetHandler);
- this.graph.view.removeListener(this.resetHandler);
- this.graph.removeListener(this.resetHandler);
- this.resetHandler = null;
- }
-
- if (this.mouseleaveHandler != null && this.graph.container != null)
- {
- mxEvent.removeListener(this.graph.container, 'mouseleave', this.mouseleaveHandler);
- this.mouseleaveHandler = null;
- }
- };
- __mxOutput.mxConstraintHandler = typeof mxConstraintHandler !== 'undefined' ? mxConstraintHandler : undefined;
|