12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865 |
- /**
- * 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();
- };
|