1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxGraphHandler
- *
- * Graph event handler that handles selection. Individual cells are handled
- * separately using <mxVertexHandler> or one of the edge handlers. These
- * handlers are created using <mxGraph.createHandler> in
- * <mxGraphSelectionModel.cellAdded>.
- *
- * To avoid the container to scroll a moved cell into view, set
- * <scrollAfterMove> to false.
- *
- * Constructor: mxGraphHandler
- *
- * Constructs an event handler that creates handles for the
- * selection cells.
- *
- * Parameters:
- *
- * graph - Reference to the enclosing <mxGraph>.
- */
- function mxGraphHandler(graph)
- {
- this.graph = graph;
- this.graph.addMouseListener(this);
-
- // Repaints the handler after autoscroll
- this.panHandler = mxUtils.bind(this, function()
- {
- if (!this.suspended)
- {
- this.updatePreview();
- this.updateHint();
- }
- });
-
- this.graph.addListener(mxEvent.PAN, this.panHandler);
-
- // Handles escape keystrokes
- this.escapeHandler = mxUtils.bind(this, function(sender, evt)
- {
- this.reset();
- });
-
- this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
-
- // Updates the preview box for remote changes
- this.refreshHandler = mxUtils.bind(this, function(sender, evt)
- {
- // Merges multiple pending calls
- if (this.refreshThread)
- {
- window.clearTimeout(this.refreshThread);
- }
- // Waits for the states and handlers to be updated
- this.refreshThread = window.setTimeout(mxUtils.bind(this, function()
- {
- this.refreshThread = null;
-
- if (this.first != null && !this.suspended)
- {
- // Updates preview with no translate to compute bounding box
- var dx = this.currentDx;
- var dy = this.currentDy;
- this.currentDx = 0;
- this.currentDy = 0;
- this.updatePreview();
- this.bounds = this.graph.getView().getBounds(this.cells);
- this.pBounds = this.getPreviewBounds(this.cells);
- if (this.pBounds == null && !this.livePreviewUsed)
- {
- this.reset();
- }
- else
- {
- // Restores translate and updates preview
- this.currentDx = dx;
- this.currentDy = dy;
- this.updatePreview();
- this.updateHint();
- if (this.livePreviewUsed)
- {
- // Forces update to ignore last visible state
- this.setHandlesVisibleForCells(
- this.graph.selectionCellsHandler.
- getHandledSelectionCells(), false, true);
- this.updatePreview();
- }
- }
- }
- }), 0);
- });
-
- this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
- this.graph.addListener(mxEvent.REFRESH, this.refreshHandler);
-
- this.keyHandler = mxUtils.bind(this, function(e)
- {
- if (this.graph.container != null && this.graph.container.style.visibility != 'hidden' &&
- this.first != null && !this.suspended)
- {
- var clone = this.graph.isCloneEvent(e) &&
- this.graph.isCellsCloneable() &&
- this.isCloneEnabled();
-
- if (clone != this.cloning)
- {
- this.cloning = clone;
- this.checkPreview();
- this.updatePreview();
- }
- }
- });
-
- mxEvent.addListener(document, 'keydown', this.keyHandler);
- mxEvent.addListener(document, 'keyup', this.keyHandler);
- };
- /**
- * Variable: graph
- *
- * Reference to the enclosing <mxGraph>.
- */
- mxGraphHandler.prototype.graph = null;
- /**
- * Variable: maxCells
- *
- * Defines the maximum number of cells to paint subhandles
- * for. Default is 50 for Firefox and 20 for IE. Set this
- * to 0 if you want an unlimited number of handles to be
- * displayed. This is only recommended if the number of
- * cells in the graph is limited to a small number, eg.
- * 500.
- */
- mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
- /**
- * Variable: enabled
- *
- * Specifies if events are handled. Default is true.
- */
- mxGraphHandler.prototype.enabled = true;
- /**
- * Variable: highlightEnabled
- *
- * Specifies if drop targets under the mouse should be enabled. Default is
- * true.
- */
- mxGraphHandler.prototype.highlightEnabled = true;
- /**
- * Variable: cloneEnabled
- *
- * Specifies if cloning by control-drag is enabled. Default is true.
- */
- mxGraphHandler.prototype.cloneEnabled = true;
- /**
- * Variable: moveEnabled
- *
- * Specifies if moving is enabled. Default is true.
- */
- mxGraphHandler.prototype.moveEnabled = true;
- /**
- * Variable: guidesEnabled
- *
- * Specifies if other cells should be used for snapping the right, center or
- * left side of the current selection. Default is false.
- */
- mxGraphHandler.prototype.guidesEnabled = false;
- /**
- * Variable: handlesVisible
- *
- * Whether the handles of the selection are currently visible.
- */
- mxGraphHandler.prototype.handlesVisible = true;
- /**
- * Variable: guide
- *
- * Holds the <mxGuide> instance that is used for alignment.
- */
- mxGraphHandler.prototype.guide = null;
- /**
- * Variable: currentDx
- *
- * Stores the x-coordinate of the current mouse move.
- */
- mxGraphHandler.prototype.currentDx = null;
- /**
- * Variable: currentDy
- *
- * Stores the y-coordinate of the current mouse move.
- */
- mxGraphHandler.prototype.currentDy = null;
- /**
- * Variable: updateCursor
- *
- * Specifies if a move cursor should be shown if the mouse is over a movable
- * cell. Default is true.
- */
- mxGraphHandler.prototype.updateCursor = true;
- /**
- * Variable: selectEnabled
- *
- * Specifies if selecting is enabled. Default is true.
- */
- mxGraphHandler.prototype.selectEnabled = true;
- /**
- * Variable: removeCellsFromParent
- *
- * Specifies if cells may be moved out of their parents. Default is true.
- */
- mxGraphHandler.prototype.removeCellsFromParent = true;
- /**
- * Variable: removeEmptyParents
- *
- * If empty parents should be removed from the model after all child cells
- * have been moved out. Default is true.
- */
- mxGraphHandler.prototype.removeEmptyParents = false;
- /**
- * Variable: connectOnDrop
- *
- * Specifies if drop events are interpreted as new connections if no other
- * drop action is defined. Default is false.
- */
- mxGraphHandler.prototype.connectOnDrop = false;
- /**
- * Variable: scrollOnMove
- *
- * Specifies if the view should be scrolled so that a moved cell is
- * visible. Default is true.
- */
- mxGraphHandler.prototype.scrollOnMove = true;
- /**
- * Variable: minimumSize
- *
- * Specifies the minimum number of pixels for the width and height of a
- * selection border. Default is 6.
- */
- mxGraphHandler.prototype.minimumSize = 6;
- /**
- * Variable: previewColor
- *
- * Specifies the color of the preview shape. Default is black.
- */
- mxGraphHandler.prototype.previewColor = 'black';
- /**
- * Variable: htmlPreview
- *
- * Specifies if the graph container should be used for preview. If this is used
- * then drop target detection relies entirely on <mxGraph.getCellAt> because
- * the HTML preview does not "let events through". Default is false.
- */
- mxGraphHandler.prototype.htmlPreview = false;
- /**
- * Variable: shape
- *
- * Reference to the <mxShape> that represents the preview.
- */
- mxGraphHandler.prototype.shape = null;
- /**
- * Variable: scaleGrid
- *
- * Specifies if the grid should be scaled. Default is false.
- */
- mxGraphHandler.prototype.scaleGrid = false;
- /**
- * Variable: rotationEnabled
- *
- * Specifies if the bounding box should allow for rotation. Default is true.
- */
- mxGraphHandler.prototype.rotationEnabled = true;
- /**
- * Variable: maxLivePreview
- *
- * Maximum number of cells for which live preview should be used. Default is 0
- * which means no live preview.
- */
- mxGraphHandler.prototype.maxLivePreview = 0;
- /**
- * Variable: allowLivePreview
- *
- * If live preview is allowed on this system. Default is true for systems with
- * SVG support.
- */
- mxGraphHandler.prototype.allowLivePreview = mxClient.IS_SVG;
- /**
- * Function: isEnabled
- *
- * Returns <enabled>.
- */
- mxGraphHandler.prototype.isEnabled = function()
- {
- return this.enabled;
- };
- /**
- * Function: setEnabled
- *
- * Sets <enabled>.
- */
- mxGraphHandler.prototype.setEnabled = function(value)
- {
- this.enabled = value;
- };
- /**
- * Function: isCloneEnabled
- *
- * Returns <cloneEnabled>.
- */
- mxGraphHandler.prototype.isCloneEnabled = function()
- {
- return this.cloneEnabled;
- };
- /**
- * Function: setCloneEnabled
- *
- * Sets <cloneEnabled>.
- *
- * Parameters:
- *
- * value - Boolean that specifies the new clone enabled state.
- */
- mxGraphHandler.prototype.setCloneEnabled = function(value)
- {
- this.cloneEnabled = value;
- };
- /**
- * Function: isMoveEnabled
- *
- * Returns <moveEnabled>.
- */
- mxGraphHandler.prototype.isMoveEnabled = function()
- {
- return this.moveEnabled;
- };
- /**
- * Function: setMoveEnabled
- *
- * Sets <moveEnabled>.
- */
- mxGraphHandler.prototype.setMoveEnabled = function(value)
- {
- this.moveEnabled = value;
- };
- /**
- * Function: isSelectEnabled
- *
- * Returns <selectEnabled>.
- */
- mxGraphHandler.prototype.isSelectEnabled = function()
- {
- return this.selectEnabled;
- };
- /**
- * Function: setSelectEnabled
- *
- * Sets <selectEnabled>.
- */
- mxGraphHandler.prototype.setSelectEnabled = function(value)
- {
- this.selectEnabled = value;
- };
- /**
- * Function: isRemoveCellsFromParent
- *
- * Returns <removeCellsFromParent>.
- */
- mxGraphHandler.prototype.isRemoveCellsFromParent = function()
- {
- return this.removeCellsFromParent;
- };
- /**
- * Function: setRemoveCellsFromParent
- *
- * Sets <removeCellsFromParent>.
- */
- mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
- {
- this.removeCellsFromParent = value;
- };
- /**
- * Function: isPropagateSelectionCell
- *
- * Returns true if the given cell and parent should propagate
- * selection state to the parent.
- */
- mxGraphHandler.prototype.isPropagateSelectionCell = function(cell, immediate, me)
- {
- var parent = this.graph.model.getParent(cell);
- if (immediate)
- {
- var geo = (this.graph.model.isEdge(cell)) ? null :
- this.graph.getCellGeometry(cell);
-
- return !this.graph.isSiblingSelected(cell) &&
- ((geo != null && geo.relative) ||
- !this.graph.isSwimlane(parent));
- }
- else
- {
- return (!this.graph.isToggleEvent(me.getEvent()) ||
- (!this.graph.isSiblingSelected(cell) &&
- !this.graph.isCellSelected(cell) &&
- (!this.graph.isSwimlane(parent)) ||
- this.graph.isCellSelected(parent))) &&
- (this.graph.isToggleEvent(me.getEvent()) ||
- !this.graph.isCellSelected(parent));
- }
- };
- /**
- * Function: getInitialCellForEvent
- *
- * Hook to return initial cell for the given event. This returns
- * the topmost cell that is not a swimlane or is selected.
- */
- mxGraphHandler.prototype.getInitialCellForEvent = function(me)
- {
- var state = me.getState();
-
- if ((!this.graph.isToggleEvent(me.getEvent()) || !mxEvent.isAltDown(me.getEvent())) &&
- state != null && !this.graph.isCellSelected(state.cell))
- {
- var model = this.graph.model;
- var next = this.graph.view.getState(model.getParent(state.cell));
- while (next != null && !this.graph.isCellSelected(next.cell) &&
- (model.isVertex(next.cell) || model.isEdge(next.cell)) &&
- this.isPropagateSelectionCell(state.cell, true, me))
- {
- state = next;
- next = this.graph.view.getState(this.graph.getModel().getParent(state.cell));
- }
- }
-
- return (state != null) ? state.cell : null;
- };
- /**
- * Function: isDelayedSelection
- *
- * Returns true if the cell or one of its ancestors is selected.
- */
- mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
- {
- if (!this.graph.isToggleEvent(me.getEvent()) || !mxEvent.isAltDown(me.getEvent()))
- {
- while (cell != null)
- {
- if (this.graph.selectionCellsHandler.isHandled(cell))
- {
- return this.graph.cellEditor.getEditingCell() != cell;
- }
-
- cell = this.graph.model.getParent(cell);
- }
- }
-
- return this.graph.isToggleEvent(me.getEvent()) && !mxEvent.isAltDown(me.getEvent());
- };
- /**
- * Function: selectDelayed
- *
- * Implements the delayed selection for the given mouse event.
- */
- mxGraphHandler.prototype.selectDelayed = function(me)
- {
- if (!this.graph.popupMenuHandler.isPopupTrigger(me))
- {
- var cell = me.getCell();
-
- if (cell == null)
- {
- cell = this.cell;
- }
-
- this.selectCellForEvent(cell, me);
- }
- };
- /**
- * Function: selectCellForEvent
- *
- * Selects the given cell for the given <mxMouseEvent>.
- */
- mxGraphHandler.prototype.selectCellForEvent = function(cell, me)
- {
- var state = this.graph.view.getState(cell);
-
- if (state != null)
- {
- if (me.isSource(state.control))
- {
- this.graph.selectCellForEvent(cell, me.getEvent());
- }
- else
- {
- if (!this.graph.isToggleEvent(me.getEvent()) ||
- !mxEvent.isAltDown(me.getEvent()))
- {
- var model = this.graph.getModel();
- var parent = model.getParent(cell);
-
- while (this.graph.view.getState(parent) != null &&
- (model.isVertex(parent) || model.isEdge(parent)) &&
- this.isPropagateSelectionCell(cell, false, me))
- {
- cell = parent;
- parent = model.getParent(cell);
- }
- }
- this.graph.selectCellForEvent(cell, me.getEvent());
- }
- }
-
- return cell;
- };
- /**
- * Function: consumeMouseEvent
- *
- * Consumes the given mouse event. NOTE: This may be used to enable click
- * events for links in labels on iOS as follows as consuming the initial
- * touchStart disables firing the subsequent click event on the link.
- *
- * <code>
- * mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
- * {
- * var source = mxEvent.getSource(me.getEvent());
- *
- * if (!mxEvent.isTouchEvent(me.getEvent()) || source.nodeName != 'A')
- * {
- * me.consume();
- * }
- * }
- * </code>
- */
- mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
- {
- me.consume();
- };
- /**
- * Function: mouseDown
- *
- * Handles the event by selecing the given cell and creating a handle for
- * it. By consuming the event all subsequent events of the gesture are
- * redirected to this handler.
- */
- mxGraphHandler.prototype.mouseDown = function(sender, me)
- {
- if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
- me.getState() != null && !mxEvent.isMultiTouchEvent(me.getEvent()))
- {
- var cell = this.getInitialCellForEvent(me);
- this.delayedSelection = this.isDelayedSelection(cell, me);
- this.cell = null;
-
- if (this.isSelectEnabled() && !this.delayedSelection)
- {
- this.graph.selectCellForEvent(cell, me.getEvent());
- }
- if (this.isMoveEnabled())
- {
- var model = this.graph.model;
- var geo = model.getGeometry(cell);
- if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1 ||
- (geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null ||
- model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges ||
- (this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
- {
- this.start(cell, me.getX(), me.getY());
- }
- else if (this.delayedSelection)
- {
- this.cell = cell;
- }
- this.cellWasClicked = true;
- this.consumeMouseEvent(mxEvent.MOUSE_DOWN, me);
- }
- }
- };
- /**
- * Function: getGuideStates
- *
- * Creates an array of cell states which should be used as guides.
- */
- mxGraphHandler.prototype.getGuideStates = function()
- {
- var parent = this.graph.getDefaultParent();
- var model = this.graph.getModel();
-
- var filter = mxUtils.bind(this, function(cell)
- {
- return this.graph.view.getState(cell) != null &&
- model.isVertex(cell) &&
- model.getGeometry(cell) != null &&
- !model.getGeometry(cell).relative;
- });
-
- return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
- };
- /**
- * Function: getCells
- *
- * Returns the cells to be modified by this handler. This implementation
- * returns all selection cells that are movable, or the given initial cell if
- * the given cell is not selected and movable. This handles the case of moving
- * unselectable or unselected cells.
- *
- * Parameters:
- *
- * initialCell - <mxCell> that triggered this handler.
- */
- mxGraphHandler.prototype.getCells = function(initialCell)
- {
- if (!this.delayedSelection && this.graph.isCellMovable(initialCell))
- {
- return [initialCell];
- }
- else
- {
- return this.graph.getMovableCells(this.graph.getSelectionCells());
- }
- };
- /**
- * Function: getPreviewBounds
- *
- * Returns the <mxRectangle> used as the preview bounds for
- * moving the given cells.
- */
- mxGraphHandler.prototype.getPreviewBounds = function(cells)
- {
- var bounds = this.getBoundingBox(cells);
-
- if (bounds != null)
- {
- // Corrects width and height
- bounds.width = Math.max(0, bounds.width - 1);
- bounds.height = Math.max(0, bounds.height - 1);
-
- if (bounds.width < this.minimumSize)
- {
- var dx = this.minimumSize - bounds.width;
- bounds.x -= dx / 2;
- bounds.width = this.minimumSize;
- }
- else
- {
- bounds.x = Math.round(bounds.x);
- bounds.width = Math.ceil(bounds.width);
- }
-
- var tr = this.graph.view.translate;
- var s = this.graph.view.scale;
-
- if (bounds.height < this.minimumSize)
- {
- var dy = this.minimumSize - bounds.height;
- bounds.y -= dy / 2;
- bounds.height = this.minimumSize;
- }
- else
- {
- bounds.y = Math.round(bounds.y);
- bounds.height = Math.ceil(bounds.height);
- }
- }
-
- return bounds;
- };
- /**
- * Function: getBoundingBox
- *
- * Returns the union of the <mxCellStates> for the given array of <mxCells>.
- * For vertices, this method uses the bounding box of the corresponding shape
- * if one exists. The bounding box of the corresponding text label and all
- * controls and overlays are ignored. See also: <mxGraphView.getBounds> and
- * <mxGraph.getBoundingBox>.
- *
- * Parameters:
- *
- * cells - Array of <mxCells> whose bounding box should be returned.
- */
- mxGraphHandler.prototype.getBoundingBox = function(cells)
- {
- var result = null;
-
- if (cells != null && cells.length > 0)
- {
- var model = this.graph.getModel();
-
- for (var i = 0; i < cells.length; i++)
- {
- if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
- {
- var state = this.graph.view.getState(cells[i]);
-
- if (state != null)
- {
- var bbox = state;
-
- if (model.isVertex(cells[i]) && state.shape != null && state.shape.boundingBox != null)
- {
- bbox = state.shape.boundingBox;
- }
-
- if (result == null)
- {
- result = mxRectangle.fromRectangle(bbox);
- }
- else
- {
- result.add(bbox);
- }
- }
- }
- }
- }
-
- return result;
- };
- /**
- * Function: createPreviewShape
- *
- * Creates the shape used to draw the preview for the given bounds.
- */
- mxGraphHandler.prototype.createPreviewShape = function(bounds)
- {
- var shape = new mxRectangleShape(bounds, null, this.previewColor);
- shape.isDashed = true;
-
- if (this.htmlPreview)
- {
- shape.dialect = mxConstants.DIALECT_STRICTHTML;
- shape.init(this.graph.container);
- }
- else
- {
- // Makes sure to use either VML or SVG shapes in order to implement
- // event-transparency on the background area of the rectangle since
- // HTML shapes do not let mouseevents through even when transparent
- shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
- mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
- shape.init(this.graph.getView().getOverlayPane());
- shape.pointerEvents = false;
-
- // Workaround for artifacts on iOS
- if (mxClient.IS_IOS)
- {
- shape.getSvgScreenOffset = function()
- {
- return 0;
- };
- }
- }
-
- return shape;
- };
- /**
- * Function: start
- *
- * Starts the handling of the mouse gesture.
- */
- mxGraphHandler.prototype.start = function(cell, x, y, cells)
- {
- this.cell = cell;
- this.first = mxUtils.convertPoint(this.graph.container, x, y);
- this.cells = (cells != null) ? cells : this.getCells(this.cell);
- this.bounds = this.graph.getView().getBounds(this.cells);
- this.pBounds = this.getPreviewBounds(this.cells);
- this.allCells = new mxDictionary();
- this.cloning = false;
- this.cellCount = 0;
-
- for (var i = 0; i < this.cells.length; i++)
- {
- this.cellCount += this.addStates(this.cells[i], this.allCells);
- }
-
- if (this.guidesEnabled)
- {
- this.guide = new mxGuide(this.graph, this.getGuideStates());
- var parent = this.graph.model.getParent(cell);
- var ignore = this.graph.model.getChildCount(parent) < 2;
-
- // Uses connected states as guides
- var connected = new mxDictionary();
- var opps = this.graph.getOpposites(this.graph.getEdges(this.cell), this.cell);
-
- for (var i = 0; i < opps.length; i++)
- {
- var state = this.graph.view.getState(opps[i]);
-
- if (state != null && !connected.get(state))
- {
- connected.put(state, true);
- }
- }
- this.guide.isStateIgnored = mxUtils.bind(this, function(state)
- {
- var p = this.graph.model.getParent(state.cell);
-
- return state.cell != null && ((!this.cloning &&
- this.isCellMoving(state.cell)) ||
- (state.cell != (this.target || parent) && !ignore &&
- !connected.get(state) &&
- (this.target == null || this.graph.model.getChildCount(
- this.target) >= 2) && p != (this.target || parent)));
- });
- }
- };
- /**
- * Function: addStates
- *
- * Adds the states for the given cell recursively to the given dictionary.
- */
- mxGraphHandler.prototype.addStates = function(cell, dict)
- {
- var state = this.graph.view.getState(cell);
- var count = 0;
-
- if (state != null && dict.get(cell) == null)
- {
- dict.put(cell, state);
- count++;
-
- var childCount = this.graph.model.getChildCount(cell);
-
- for (var i = 0; i < childCount; i++)
- {
- count += this.addStates(this.graph.model.getChildAt(cell, i), dict);
- }
- }
-
- return count;
- };
- /**
- * Function: isCellMoving
- *
- * Returns true if the given cell is currently being moved.
- */
- mxGraphHandler.prototype.isCellMoving = function(cell)
- {
- return this.allCells.get(cell) != null;
- };
- /**
- * Function: useGuidesForEvent
- *
- * Returns true if the guides should be used for the given <mxMouseEvent>.
- * This implementation returns <mxGuide.isEnabledForEvent>.
- */
- mxGraphHandler.prototype.useGuidesForEvent = function(me)
- {
- return (this.guide != null) ? this.guide.isEnabledForEvent(me.getEvent()) &&
- !this.graph.isConstrainedEvent(me.getEvent()) : true;
- };
- /**
- * Function: snap
- *
- * Snaps the given vector to the grid and returns the given mxPoint instance.
- */
- mxGraphHandler.prototype.snap = function(vector)
- {
- var scale = (this.scaleGrid) ? this.graph.view.scale : 1;
-
- vector.x = this.graph.snap(vector.x / scale) * scale;
- vector.y = this.graph.snap(vector.y / scale) * scale;
-
- return vector;
- };
- /**
- * Function: getDelta
- *
- * Returns an <mxPoint> that represents the vector for moving the cells
- * for the given <mxMouseEvent>.
- */
- mxGraphHandler.prototype.getDelta = function(me)
- {
- var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
-
- return new mxPoint(point.x - this.first.x - this.graph.panDx,
- point.y - this.first.y - this.graph.panDy);
- };
- /**
- * Function: updateHint
- *
- * Hook for subclassers do show details while the handler is active.
- */
- mxGraphHandler.prototype.updateHint = function(me) { };
- /**
- * Function: removeHint
- *
- * Hooks for subclassers to hide details when the handler gets inactive.
- */
- mxGraphHandler.prototype.removeHint = function() { };
- /**
- * Function: roundLength
- *
- * Hook for rounding the unscaled vector. Allows for half steps in the raster so
- * numbers coming in should be rounded if no half steps are allowed (ie for non
- * aligned standard moving where pixel steps should be preferred).
- */
- mxGraphHandler.prototype.roundLength = function(length)
- {
- return Math.round(length * 100) / 100;
- };
- /**
- * Function: isValidDropTarget
- *
- * Returns true if the given cell is a valid drop target.
- */
- mxGraphHandler.prototype.isValidDropTarget = function(target, me)
- {
- return this.graph.model.getParent(this.cell) != target;
- };
- /**
- * Function: checkPreview
- *
- * Updates the preview if cloning state has changed.
- */
- mxGraphHandler.prototype.checkPreview = function()
- {
- if (this.livePreviewActive && this.cloning)
- {
- this.resetLivePreview();
- this.livePreviewActive = false;
- }
- else if (this.maxLivePreview >= this.cellCount && !this.livePreviewActive && this.allowLivePreview)
- {
- if (!this.cloning || !this.livePreviewActive)
- {
- this.livePreviewActive = true;
- this.livePreviewUsed = true;
- }
- }
- else if (!this.livePreviewUsed && this.shape == null)
- {
- this.shape = this.createPreviewShape(this.bounds);
- }
- };
- /**
- * Function: mouseMove
- *
- * Handles the event by highlighting possible drop targets and updating the
- * preview.
- */
- mxGraphHandler.prototype.mouseMove = function(sender, me)
- {
- var graph = this.graph;
- if (!me.isConsumed() && graph.isMouseDown && this.cell != null &&
- this.first != null && this.bounds != null && !this.suspended)
- {
- // Stops moving if a multi touch event is received
- if (mxEvent.isMultiTouchEvent(me.getEvent()))
- {
- this.reset();
- return;
- }
-
- var delta = this.getDelta(me);
- var tol = graph.tolerance;
- if (this.shape != null || this.livePreviewActive || Math.abs(delta.x) > tol || Math.abs(delta.y) > tol)
- {
- // Highlight is used for highlighting drop targets
- if (this.highlight == null)
- {
- this.highlight = new mxCellHighlight(this.graph,
- mxConstants.DROP_TARGET_COLOR, 3);
- }
- var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
- var gridEnabled = graph.isGridEnabledEvent(me.getEvent());
- var cell = me.getCell();
- var hideGuide = true;
- var target = null;
- this.cloning = clone;
-
- if (graph.isDropEnabled() && this.highlightEnabled)
- {
- // Contains a call to getCellAt to find the cell under the mouse
- target = graph.getDropTarget(this.cells, me.getEvent(), cell, clone);
- }
- var state = graph.getView().getState(target);
- var highlight = false;
-
- if (state != null && (clone || this.isValidDropTarget(target, me)))
- {
- if (this.target != target)
- {
- this.target = target;
- this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
- }
-
- highlight = true;
- }
- else
- {
- this.target = null;
- if (this.connectOnDrop && cell != null && this.cells.length == 1 &&
- graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
- {
- state = graph.getView().getState(cell);
-
- if (state != null)
- {
- var error = graph.getEdgeValidationError(null, this.cell, cell);
- var color = (error == null) ?
- mxConstants.VALID_COLOR :
- mxConstants.INVALID_CONNECT_TARGET_COLOR;
- this.setHighlightColor(color);
- highlight = true;
- }
- }
- }
-
- if (state != null && highlight)
- {
- this.highlight.highlight(state);
- }
- else
- {
- this.highlight.hide();
- }
- if (this.guide != null && this.useGuidesForEvent(me))
- {
- delta = this.guide.move(this.bounds, delta, gridEnabled, clone);
- hideGuide = false;
- }
- else
- {
- delta = this.graph.snapDelta(delta, this.bounds, !gridEnabled, false, false);
- }
-
- if (this.guide != null && hideGuide)
- {
- this.guide.hide();
- }
- // Constrained movement if shift key is pressed
- if (graph.isConstrainedEvent(me.getEvent()))
- {
- if (Math.abs(delta.x) > Math.abs(delta.y))
- {
- delta.y = 0;
- }
- else
- {
- delta.x = 0;
- }
- }
-
- this.checkPreview();
-
- if (this.currentDx != delta.x || this.currentDy != delta.y)
- {
- this.currentDx = delta.x;
- this.currentDy = delta.y;
- this.updatePreview();
- }
- }
- this.updateHint(me);
- this.consumeMouseEvent(mxEvent.MOUSE_MOVE, me);
-
- // Cancels the bubbling of events to the container so
- // that the droptarget is not reset due to an mouseMove
- // fired on the container with no associated state.
- mxEvent.consume(me.getEvent());
- }
- else if ((this.isMoveEnabled() || this.isCloneEnabled()) && this.updateCursor && !me.isConsumed() &&
- (me.getState() != null || me.sourceState != null) && !graph.isMouseDown)
- {
- var cursor = graph.getCursorForMouseEvent(me);
-
- if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell()))
- {
- if (graph.getModel().isEdge(me.getCell()))
- {
- cursor = mxConstants.CURSOR_MOVABLE_EDGE;
- }
- else
- {
- cursor = mxConstants.CURSOR_MOVABLE_VERTEX;
- }
- }
- // Sets the cursor on the original source state under the mouse
- // instead of the event source state which can be the parent
- if (cursor != null && me.sourceState != null)
- {
- me.sourceState.setCursor(cursor);
- }
- }
- };
- /**
- * Function: updatePreview
- *
- * Updates the bounds of the preview shape.
- */
- mxGraphHandler.prototype.updatePreview = function(remote)
- {
- if (this.livePreviewUsed && !remote)
- {
- if (this.cells != null)
- {
- this.setHandlesVisibleForCells(
- this.graph.selectionCellsHandler.
- getHandledSelectionCells(), false);
- this.updateLivePreview(this.currentDx, this.currentDy);
- }
- }
- else
- {
- this.updatePreviewShape();
- }
- };
- /**
- * Function: updatePreviewShape
- *
- * Updates the bounds of the preview shape.
- */
- mxGraphHandler.prototype.updatePreviewShape = function()
- {
- if (this.shape != null && this.pBounds != null)
- {
- this.shape.bounds = new mxRectangle(Math.round(this.pBounds.x + this.currentDx),
- Math.round(this.pBounds.y + this.currentDy), this.pBounds.width, this.pBounds.height);
- this.shape.redraw();
- }
- };
- /**
- * Function: updateLivePreview
- *
- * Updates the bounds of the preview shape.
- */
- mxGraphHandler.prototype.updateLivePreview = function(dx, dy)
- {
- if (!this.suspended)
- {
- var states = [];
-
- if (this.allCells != null)
- {
- this.allCells.visit(mxUtils.bind(this, function(key, state)
- {
- var realState = this.graph.view.getState(state.cell);
-
- // Checks if cell was removed or replaced
- if (realState != state)
- {
- state.destroy();
-
- if (realState != null)
- {
- this.allCells.put(state.cell, realState);
- }
- else
- {
- this.allCells.remove(state.cell);
- }
-
- state = realState;
- }
-
- if (state != null)
- {
- // Saves current state
- var tempState = state.clone();
- states.push([state, tempState]);
-
- // Makes transparent for events to detect drop targets
- if (state.shape != null)
- {
- if (state.shape.originalPointerEvents == null)
- {
- state.shape.originalPointerEvents = state.shape.pointerEvents;
- }
-
- state.shape.pointerEvents = false;
-
- if (state.text != null)
- {
- if (state.text.originalPointerEvents == null)
- {
- state.text.originalPointerEvents = state.text.pointerEvents;
- }
-
- state.text.pointerEvents = false;
- }
- }
-
- // Temporarily changes position
- if (this.graph.model.isVertex(state.cell))
- {
- state.x += dx;
- state.y += dy;
-
- // Draws the live preview
- if (!this.cloning)
- {
- state.view.graph.cellRenderer.redraw(state, true);
-
- // Forces redraw of connected edges after all states
- // have been updated but avoids update of state
- state.view.invalidate(state.cell);
- state.invalid = false;
-
- // Hides folding icon
- if (state.control != null && state.control.node != null)
- {
- state.control.node.style.visibility = 'hidden';
- }
- }
- // Clone live preview may use text bounds
- else if (state.text != null)
- {
- state.text.updateBoundingBox();
-
- // Fixes preview box for edge labels
- if (state.text.boundingBox != null)
- {
- state.text.boundingBox.x += dx;
- state.text.boundingBox.y += dy;
- }
-
- if (state.text.unrotatedBoundingBox != null)
- {
- state.text.unrotatedBoundingBox.x += dx;
- state.text.unrotatedBoundingBox.y += dy;
- }
- }
- }
- }
- }));
- }
-
- // Resets the handler if everything was removed
- if (states.length == 0)
- {
- this.reset();
- }
- else
- {
- // Redraws connected edges
- var s = this.graph.view.scale;
-
- for (var i = 0; i < states.length; i++)
- {
- var state = states[i][0];
-
- if (this.graph.model.isEdge(state.cell))
- {
- var geometry = this.graph.getCellGeometry(state.cell);
- var points = [];
-
- if (geometry != null && geometry.points != null)
- {
- for (var j = 0; j < geometry.points.length; j++)
- {
- if (geometry.points[j] != null)
- {
- points.push(new mxPoint(
- geometry.points[j].x + dx / s,
- geometry.points[j].y + dy / s));
- }
- }
- }
-
- var source = state.visibleSourceState;
- var target = state.visibleTargetState;
- var pts = states[i][1].absolutePoints;
-
- if (source == null || !this.isCellMoving(source.cell))
- {
- var pt0 = pts[0];
- state.setAbsoluteTerminalPoint(new mxPoint(pt0.x + dx, pt0.y + dy), true);
- source = null;
- }
- else
- {
- state.view.updateFixedTerminalPoint(state, source, true,
- this.graph.getConnectionConstraint(state, source, true));
- }
-
- if (target == null || !this.isCellMoving(target.cell))
- {
- var ptn = pts[pts.length - 1];
- state.setAbsoluteTerminalPoint(new mxPoint(ptn.x + dx, ptn.y + dy), false);
- target = null;
- }
- else
- {
- state.view.updateFixedTerminalPoint(state, target, false,
- this.graph.getConnectionConstraint(state, target, false));
- }
-
- state.view.updatePoints(state, points, source, target);
- state.view.updateFloatingTerminalPoints(state, source, target);
- state.view.updateEdgeLabelOffset(state);
- state.invalid = false;
-
- // Draws the live preview but avoids update of state
- if (!this.cloning)
- {
- state.view.graph.cellRenderer.redraw(state, true);
- }
- }
- }
-
- this.graph.view.validate();
- this.redrawHandles(states);
- this.resetPreviewStates(states);
- }
- }
- };
- /**
- * Function: redrawHandles
- *
- * Redraws the preview shape for the given states array.
- */
- mxGraphHandler.prototype.redrawHandles = function(states)
- {
- for (var i = 0; i < states.length; i++)
- {
- var handler = this.graph.selectionCellsHandler.getHandler(states[i][0].cell);
-
- if (handler != null)
- {
- handler.redraw(true);
- }
- }
- };
- /**
- * Function: resetPreviewStates
- *
- * Resets the given preview states array.
- */
- mxGraphHandler.prototype.resetPreviewStates = function(states)
- {
- for (var i = 0; i < states.length; i++)
- {
- states[i][0].setState(states[i][1]);
- }
- };
- /**
- * Function: suspend
- *
- * Suspends the livew preview.
- */
- mxGraphHandler.prototype.suspend = function()
- {
- if (!this.suspended)
- {
- if (this.livePreviewUsed)
- {
- this.updateLivePreview(0, 0);
- }
-
- if (this.shape != null)
- {
- this.shape.node.style.visibility = 'hidden';
- }
-
- if (this.guide != null)
- {
- this.guide.setVisible(false);
- }
-
- this.suspended = true;
- }
- };
- /**
- * Function: resume
- *
- * Suspends the livew preview.
- */
- mxGraphHandler.prototype.resume = function()
- {
- if (this.suspended)
- {
- this.suspended = null;
-
- if (this.livePreviewUsed)
- {
- this.livePreviewActive = true;
- }
-
- if (this.shape != null)
- {
- this.shape.node.style.visibility = 'visible';
- }
-
- if (this.guide != null)
- {
- this.guide.setVisible(true);
- }
- }
- };
- /**
- * Function: resetLivePreview
- *
- * Resets the livew preview.
- */
- mxGraphHandler.prototype.resetLivePreview = function()
- {
- if (this.allCells != null)
- {
- this.allCells.visit(mxUtils.bind(this, function(key, state)
- {
- // Restores event handling
- if (state.shape != null && state.shape.originalPointerEvents != null)
- {
- state.shape.pointerEvents = state.shape.originalPointerEvents;
- state.shape.originalPointerEvents = null;
-
- // Forces repaint even if not moved to update pointer events
- state.shape.bounds = null;
-
- if (state.text != null)
- {
- state.text.pointerEvents = state.text.originalPointerEvents;
- state.text.originalPointerEvents = null;
- }
- }
- // Shows folding icon
- if (state.control != null && state.control.node != null &&
- state.control.node.style.visibility == 'hidden')
- {
- state.control.node.style.visibility = '';
- }
-
- // Fixes preview box for edge labels
- if (!this.cloning)
- {
- if (state.text != null)
- {
- state.text.updateBoundingBox();
- }
- }
-
- // Forces repaint of connected edges
- state.view.invalidate(state.cell);
- }));
- // Repaints all invalid states
- this.graph.view.validate();
- }
- };
- /**
- * Function: setHandlesVisibleForCells
- *
- * Sets wether the handles attached to the given cells are visible.
- *
- * Parameters:
- *
- * cells - Array of <mxCells>.
- * visible - Boolean that specifies if the handles should be visible.
- * force - Forces an update of the handler regardless of the last used value.
- */
- mxGraphHandler.prototype.setHandlesVisibleForCells = function(cells, visible, force)
- {
- if (force || this.handlesVisible != visible)
- {
- this.handlesVisible = visible;
-
- for (var i = 0; i < cells.length; i++)
- {
- var handler = this.graph.selectionCellsHandler.getHandler(cells[i]);
-
- if (handler != null)
- {
- handler.setHandlesVisible(visible);
-
- if (visible)
- {
- handler.redraw();
- }
- }
- }
- }
- };
- /**
- * Function: setHighlightColor
- *
- * Sets the color of the rectangle used to highlight drop targets.
- *
- * Parameters:
- *
- * color - String that represents the new highlight color.
- */
- mxGraphHandler.prototype.setHighlightColor = function(color)
- {
- if (this.highlight != null)
- {
- this.highlight.setHighlightColor(color);
- }
- };
- /**
- * Function: mouseUp
- *
- * Handles the event by applying the changes to the selection cells.
- */
- mxGraphHandler.prototype.mouseUp = function(sender, me)
- {
- if (!me.isConsumed())
- {
- if (this.livePreviewUsed)
- {
- this.resetLivePreview();
- }
-
- if (this.cell != null && this.first != null && (this.shape != null || this.livePreviewUsed) &&
- this.currentDx != null && this.currentDy != null)
- {
- var graph = this.graph;
- var cell = me.getCell();
-
- if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell) &&
- graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
- {
- graph.connectionHandler.connect(this.cell, cell, me.getEvent());
- }
- else
- {
- var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
- var scale = graph.getView().scale;
- var dx = this.roundLength(this.currentDx / scale);
- var dy = this.roundLength(this.currentDy / scale);
- var target = this.target;
-
- if (graph.isSplitEnabled() && graph.isSplitTarget(target, this.cells, me.getEvent()))
- {
- graph.splitEdge(target, this.cells, null, dx, dy,
- me.getGraphX(), me.getGraphY());
- }
- else
- {
- this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
- }
- }
- }
- else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
- {
- this.selectDelayed(me);
- }
- }
- // Consumes the event if a cell was initially clicked
- if (this.cellWasClicked)
- {
- this.consumeMouseEvent(mxEvent.MOUSE_UP, me);
- }
- this.reset();
- };
- /**
- * Function: reset
- *
- * Resets the state of this handler.
- */
- mxGraphHandler.prototype.reset = function()
- {
- if (this.livePreviewUsed)
- {
- this.resetLivePreview();
- this.setHandlesVisibleForCells(
- this.graph.selectionCellsHandler.
- getHandledSelectionCells(), true);
- }
-
- this.destroyShapes();
- this.removeHint();
- this.delayedSelection = false;
- this.livePreviewActive = null;
- this.livePreviewUsed = null;
- this.cellWasClicked = false;
- this.suspended = null;
- this.currentDx = null;
- this.currentDy = null;
- this.cellCount = null;
- this.cloning = false;
- this.allCells = null;
- this.pBounds = null;
- this.guides = null;
- this.target = null;
- this.first = null;
- this.cells = null;
- this.cell = null;
- };
- /**
- * Function: shouldRemoveCellsFromParent
- *
- * Returns true if the given cells should be removed from the parent for the specified
- * mousereleased event.
- */
- mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
- {
- if (this.graph.getModel().isVertex(parent))
- {
- var pState = this.graph.getView().getState(parent);
-
- if (pState != null)
- {
- var pt = mxUtils.convertPoint(this.graph.container,
- mxEvent.getClientX(evt), mxEvent.getClientY(evt));
- var alpha = mxUtils.toRadians(mxUtils.getValue(pState.style, mxConstants.STYLE_ROTATION) || 0);
-
- if (alpha != 0)
- {
- var cos = Math.cos(-alpha);
- var sin = Math.sin(-alpha);
- var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
- pt = mxUtils.getRotatedPoint(pt, cos, sin, cx);
- }
-
- return !mxUtils.contains(pState, pt.x, pt.y);
- }
- }
-
- return false;
- };
- /**
- * Function: moveCells
- *
- * Moves the given cells by the specified amount.
- */
- mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
- {
- if (clone)
- {
- cells = this.graph.getCloneableCells(cells);
- }
-
- // Removes cells from parent
- var parent = this.graph.getModel().getParent(this.cell);
-
- if (target == null && this.isRemoveCellsFromParent() &&
- this.shouldRemoveCellsFromParent(parent, cells, evt))
- {
- target = this.graph.getDefaultParent();
- }
-
- // Cloning into locked cells is not allowed
- clone = clone && !this.graph.isCellLocked(target || this.graph.getDefaultParent());
- this.graph.getModel().beginUpdate();
- try
- {
- var parents = [];
-
- // Removes parent if all child cells are removed
- if (!clone && target != null && this.removeEmptyParents)
- {
- // Collects all non-selected parents
- var dict = new mxDictionary();
-
- for (var i = 0; i < cells.length; i++)
- {
- dict.put(cells[i], true);
- }
-
- // LATER: Recurse up the cell hierarchy
- for (var i = 0; i < cells.length; i++)
- {
- var par = this.graph.model.getParent(cells[i]);
- if (par != null && !dict.get(par))
- {
- dict.put(par, true);
- parents.push(par);
- }
- }
- }
-
- // Passes all selected cells in order to correctly clone or move into
- // the target cell. The method checks for each cell if its movable.
- cells = this.graph.moveCells(cells, dx, dy, clone, target, evt);
- // Removes parent if all child cells are removed
- var temp = [];
-
- for (var i = 0; i < parents.length; i++)
- {
- if (this.shouldRemoveParent(parents[i]))
- {
- temp.push(parents[i]);
- }
- }
-
- this.graph.removeCells(temp, false);
- }
- finally
- {
- this.graph.getModel().endUpdate();
- }
- // Selects the new cells if cells have been cloned
- if (clone)
- {
- this.graph.setSelectionCells(cells);
- }
- if (this.isSelectEnabled() && this.scrollOnMove)
- {
- this.graph.scrollCellToVisible(cells[0]);
- }
- };
- /**
- * Function: shouldRemoveParent
- *
- * Returns true if the given parent should be removed after removal of child cells.
- */
- mxGraphHandler.prototype.shouldRemoveParent = function(parent)
- {
- var state = this.graph.view.getState(parent);
-
- return state != null && (this.graph.model.isEdge(state.cell) || this.graph.model.isVertex(state.cell)) &&
- this.graph.isCellDeletable(state.cell) && this.graph.model.getChildCount(state.cell) == 0 &&
- this.graph.isTransparentState(state);
- };
- /**
- * Function: destroyShapes
- *
- * Destroy the preview and highlight shapes.
- */
- mxGraphHandler.prototype.destroyShapes = function()
- {
- // Destroys the preview dashed rectangle
- if (this.shape != null)
- {
- this.shape.destroy();
- this.shape = null;
- }
-
- if (this.guide != null)
- {
- this.guide.destroy();
- this.guide = null;
- }
-
- // Destroys the drop target highlight
- if (this.highlight != null)
- {
- this.highlight.destroy();
- this.highlight = null;
- }
- };
- /**
- * Function: destroy
- *
- * Destroys the handler and all its resources and DOM nodes.
- */
- mxGraphHandler.prototype.destroy = function()
- {
- this.graph.removeMouseListener(this);
- this.graph.removeListener(this.panHandler);
-
- if (this.escapeHandler != null)
- {
- this.graph.removeListener(this.escapeHandler);
- this.escapeHandler = null;
- }
-
- if (this.refreshHandler != null)
- {
- this.graph.getModel().removeListener(this.refreshHandler);
- this.graph.removeListener(this.refreshHandler);
- this.refreshHandler = null;
- }
-
- mxEvent.removeListener(document, 'keydown', this.keyHandler);
- mxEvent.removeListener(document, 'keyup', this.keyHandler);
-
- this.destroyShapes();
- this.removeHint();
- };
- __mxOutput.mxGraphHandler = typeof mxGraphHandler !== 'undefined' ? mxGraphHandler : undefined;
|