diff --git a/js/ui/mxgraph/src/js/handler/mxPanningHandler.js b/js/ui/mxgraph/src/js/handler/mxPanningHandler.js new file mode 100644 index 0000000..e9aa4cd --- /dev/null +++ b/js/ui/mxgraph/src/js/handler/mxPanningHandler.js @@ -0,0 +1,462 @@ +/** + * Copyright (c) 2006-2015, JGraph Ltd + * Copyright (c) 2006-2015, Gaudenz Alder + */ +/** + * Class: mxPanningHandler + * + * Event handler that pans and creates popupmenus. To use the left + * mousebutton for panning without interfering with cell moving and + * resizing, use and . For grid size + * steps while panning, use . This handler is built-into + * and enabled using . + * + * Constructor: mxPanningHandler + * + * Constructs an event handler that creates a + * and pans the graph. + * + * Event: mxEvent.PAN_START + * + * Fires when the panning handler changes its state to true. The + * event property contains the corresponding . + * + * Event: mxEvent.PAN + * + * Fires while handle is processing events. The event property contains + * the corresponding . + * + * Event: mxEvent.PAN_END + * + * Fires when the panning handler changes its state to false. The + * event property contains the corresponding . + */ +function mxPanningHandler(graph) +{ + if (graph != null) + { + this.graph = graph; + this.graph.addMouseListener(this); + + // Handles force panning event + this.forcePanningHandler = mxUtils.bind(this, function(sender, evt) + { + var evtName = evt.getProperty('eventName'); + var me = evt.getProperty('event'); + + if (evtName == mxEvent.MOUSE_DOWN && this.isForcePanningEvent(me)) + { + this.start(me); + this.active = true; + this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me)); + me.consume(); + } + }); + + this.graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.forcePanningHandler); + + // Handles pinch gestures + this.gestureHandler = mxUtils.bind(this, function(sender, eo) + { + if (this.isPinchEnabled()) + { + var evt = eo.getProperty('event'); + + if (!mxEvent.isConsumed(evt) && evt.type == 'gesturestart') + { + this.initialScale = this.graph.view.scale; + + // Forces start of panning when pinch gesture starts + if (!this.active && this.mouseDownEvent != null) + { + this.start(this.mouseDownEvent); + this.mouseDownEvent = null; + } + } + else if (evt.type == 'gestureend' && this.initialScale == null) + { + this.initialScale = null; + } + + if (this.initialScale != null) + { + var value = Math.round(this.initialScale * evt.scale * 100) / 100; + + if (this.minScale != null) + { + value = Math.max(this.minScale, value); + } + + if (this.maxScale != null) + { + value = Math.min(this.maxScale, value); + } + + if (this.graph.view.scale != value) + { + this.graph.zoomTo(value); + mxEvent.consume(evt); + } + } + } + }); + + this.graph.addListener(mxEvent.GESTURE, this.gestureHandler); + } +}; + +/** + * Extends mxEventSource. + */ +mxPanningHandler.prototype = new mxEventSource(); +mxPanningHandler.prototype.constructor = mxPanningHandler; + +/** + * Variable: graph + * + * Reference to the enclosing . + */ +mxPanningHandler.prototype.graph = null; + +/** + * Variable: useLeftButtonForPanning + * + * Specifies if panning should be active for the left mouse button. + * Setting this to true may conflict with . Default is false. + */ +mxPanningHandler.prototype.useLeftButtonForPanning = false; + +/** + * Variable: usePopupTrigger + * + * Specifies if should also be used for panning. + */ +mxPanningHandler.prototype.usePopupTrigger = true; + +/** + * Variable: ignoreCell + * + * Specifies if panning should be active even if there is a cell under the + * mousepointer. Default is false. + */ +mxPanningHandler.prototype.ignoreCell = false; + +/** + * Variable: previewEnabled + * + * Specifies if the panning should be previewed. Default is true. + */ +mxPanningHandler.prototype.previewEnabled = true; + +/** + * Variable: useGrid + * + * Specifies if the panning steps should be aligned to the grid size. + * Default is false. + */ +mxPanningHandler.prototype.useGrid = false; + +/** + * Variable: panningEnabled + * + * Specifies if panning should be enabled. Default is true. + */ +mxPanningHandler.prototype.panningEnabled = true; + +/** + * Variable: pinchEnabled + * + * Specifies if pinch gestures should be handled as zoom. Default is true. + */ +mxPanningHandler.prototype.pinchEnabled = true; + +/** + * Variable: maxScale + * + * Specifies the maximum scale. Default is 8. + */ +mxPanningHandler.prototype.maxScale = 8; + +/** + * Variable: minScale + * + * Specifies the minimum scale. Default is 0.01. + */ +mxPanningHandler.prototype.minScale = 0.01; + +/** + * Variable: dx + * + * Holds the current horizontal offset. + */ +mxPanningHandler.prototype.dx = null; + +/** + * Variable: dy + * + * Holds the current vertical offset. + */ +mxPanningHandler.prototype.dy = null; + +/** + * Variable: startX + * + * Holds the x-coordinate of the start point. + */ +mxPanningHandler.prototype.startX = 0; + +/** + * Variable: startY + * + * Holds the y-coordinate of the start point. + */ +mxPanningHandler.prototype.startY = 0; + +/** + * Function: isActive + * + * Returns true if the handler is currently active. + */ +mxPanningHandler.prototype.isActive = function() +{ + return this.active || this.initialScale != null; +}; + +/** + * Function: isPanningEnabled + * + * Returns . + */ +mxPanningHandler.prototype.isPanningEnabled = function() +{ + return this.panningEnabled; +}; + +/** + * Function: setPanningEnabled + * + * Sets . + */ +mxPanningHandler.prototype.setPanningEnabled = function(value) +{ + this.panningEnabled = value; +}; + +/** + * Function: isPinchEnabled + * + * Returns . + */ +mxPanningHandler.prototype.isPinchEnabled = function() +{ + return this.pinchEnabled; +}; + +/** + * Function: setPinchEnabled + * + * Sets . + */ +mxPanningHandler.prototype.setPinchEnabled = function(value) +{ + this.pinchEnabled = value; +}; + +/** + * Function: isPanningTrigger + * + * Returns true if the given event is a panning trigger for the optional + * given cell. This returns true if control-shift is pressed or if + * is true and the event is a popup trigger. + */ +mxPanningHandler.prototype.isPanningTrigger = function(me) +{ + var evt = me.getEvent(); + + return (this.useLeftButtonForPanning && me.getState() == null && + mxEvent.isLeftMouseButton(evt)) || (mxEvent.isControlDown(evt) && + mxEvent.isShiftDown(evt)) || (this.usePopupTrigger && mxEvent.isPopupTrigger(evt)); +}; + +/** + * Function: isForcePanningEvent + * + * Returns true if the given should start panning. This + * implementation always returns true if is true or for + * multi touch events. + */ +mxPanningHandler.prototype.isForcePanningEvent = function(me) +{ + return this.ignoreCell || mxEvent.isMultiTouchEvent(me.getEvent()); +}; + +/** + * Function: mouseDown + * + * Handles the event by initiating the panning. By consuming the event all + * subsequent events of the gesture are redirected to this handler. + */ +mxPanningHandler.prototype.mouseDown = function(sender, me) +{ + this.mouseDownEvent = me; + + if (!me.isConsumed() && this.isPanningEnabled() && !this.active && this.isPanningTrigger(me)) + { + this.start(me); + this.consumePanningTrigger(me); + } +}; + +/** + * Function: start + * + * Starts panning at the given event. + */ +mxPanningHandler.prototype.start = function(me) +{ + this.dx0 = -this.graph.container.scrollLeft; + this.dy0 = -this.graph.container.scrollTop; + + // Stores the location of the trigger event + this.startX = me.getX(); + this.startY = me.getY(); + this.dx = null; + this.dy = null; + + this.panningTrigger = true; +}; + +/** + * Function: consumePanningTrigger + * + * Consumes the given if it was a panning trigger in + * . The default is to invoke . Note that this + * will block any further event processing. If you haven't disabled built-in + * context menus and require immediate selection of the cell on mouseDown in + * Safari and/or on the Mac, then use the following code: + * + * (code) + * mxPanningHandler.prototype.consumePanningTrigger = function(me) + * { + * if (me.evt.preventDefault) + * { + * me.evt.preventDefault(); + * } + * + * // Stops event processing in IE + * me.evt.returnValue = false; + * + * // Sets local consumed state + * if (!mxClient.IS_SF && !mxClient.IS_MAC) + * { + * me.consumed = true; + * } + * }; + * (end) + */ +mxPanningHandler.prototype.consumePanningTrigger = function(me) +{ + me.consume(); +}; + +/** + * Function: mouseMove + * + * Handles the event by updating the panning on the graph. + */ +mxPanningHandler.prototype.mouseMove = function(sender, me) +{ + this.dx = me.getX() - this.startX; + this.dy = me.getY() - this.startY; + + if (this.active) + { + if (this.previewEnabled) + { + // Applies the grid to the panning steps + if (this.useGrid) + { + this.dx = this.graph.snap(this.dx); + this.dy = this.graph.snap(this.dy); + } + + this.graph.panGraph(this.dx + this.dx0, this.dy + this.dy0); + } + + this.fireEvent(new mxEventObject(mxEvent.PAN, 'event', me)); + } + else if (this.panningTrigger) + { + var tmp = this.active; + + // Panning is activated only if the mouse is moved + // beyond the graph tolerance + this.active = Math.abs(this.dx) > this.graph.tolerance || Math.abs(this.dy) > this.graph.tolerance; + + if (!tmp && this.active) + { + this.fireEvent(new mxEventObject(mxEvent.PAN_START, 'event', me)); + } + } + + if (this.active || this.panningTrigger) + { + me.consume(); + } +}; + +/** + * Function: mouseUp + * + * Handles the event by setting the translation on the view or showing the + * popupmenu. + */ +mxPanningHandler.prototype.mouseUp = function(sender, me) +{ + if (this.active) + { + if (this.dx != null && this.dy != null) + { + // Ignores if scrollbars have been used for panning + if (!this.graph.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.graph.container)) + { + var scale = this.graph.getView().scale; + var t = this.graph.getView().translate; + this.graph.panGraph(0, 0); + this.panGraph(t.x + this.dx / scale, t.y + this.dy / scale); + } + + me.consume(); + } + + this.fireEvent(new mxEventObject(mxEvent.PAN_END, 'event', me)); + } + + this.panningTrigger = false; + this.mouseDownEvent = null; + this.active = false; + this.dx = null; + this.dy = null; +}; + +/** + * Function: panGraph + * + * Pans by the given amount. + */ +mxPanningHandler.prototype.panGraph = function(dx, dy) +{ + this.graph.getView().setTranslate(dx, dy); +}; + +/** + * Function: destroy + * + * Destroys the handler and all its resources and DOM nodes. + */ +mxPanningHandler.prototype.destroy = function() +{ + this.graph.removeMouseListener(this); + this.graph.removeListener(this.forcePanningHandler); + this.graph.removeListener(this.gestureHandler); +};