123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- /**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- /**
- * Class: mxLayoutManager
- *
- * Implements a layout manager that runs a given layout after any changes to the graph:
- *
- * Example:
- *
- * (code)
- * var layoutMgr = new mxLayoutManager(graph);
- * layoutMgr.getLayout = function(cell, eventName)
- * {
- * return layout;
- * };
- * (end)
- *
- * See <getLayout> for a description of the possible eventNames.
- *
- * Event: mxEvent.LAYOUT_CELLS
- *
- * Fires between begin- and endUpdate after all cells have been layouted in
- * <layoutCells>. The <code>cells</code> property contains all cells that have
- * been passed to <layoutCells>.
- *
- * Constructor: mxLayoutManager
- *
- * Constructs a new automatic layout for the given graph.
- *
- * Arguments:
- *
- * graph - Reference to the enclosing graph.
- */
- function mxLayoutManager(graph)
- {
- // Executes the layout before the changes are dispatched
- this.undoHandler = mxUtils.bind(this, function(sender, evt)
- {
- if (this.isEnabled())
- {
- this.beforeUndo(evt.getProperty('edit'));
- }
- });
-
- // Notifies the layout of a move operation inside a parent
- this.moveHandler = mxUtils.bind(this, function(sender, evt)
- {
- if (this.isEnabled())
- {
- this.cellsMoved(evt.getProperty('cells'), evt.getProperty('event'));
- }
- });
-
- // Notifies the layout of a move operation inside a parent
- this.resizeHandler = mxUtils.bind(this, function(sender, evt)
- {
- if (this.isEnabled())
- {
- this.cellsResized(evt.getProperty('cells'), evt.getProperty('bounds'),
- evt.getProperty('previous'));
- }
- });
-
- this.setGraph(graph);
- };
- /**
- * Extends mxEventSource.
- */
- mxLayoutManager.prototype = new mxEventSource();
- mxLayoutManager.prototype.constructor = mxLayoutManager;
- /**
- * Variable: graph
- *
- * Reference to the enclosing <mxGraph>.
- */
- mxLayoutManager.prototype.graph = null;
- /**
- * Variable: bubbling
- *
- * Specifies if the layout should bubble along
- * the cell hierarchy. Default is true.
- */
- mxLayoutManager.prototype.bubbling = true;
- /**
- * Variable: enabled
- *
- * Specifies if event handling is enabled. Default is true.
- */
- mxLayoutManager.prototype.enabled = true;
- /**
- * Variable: undoHandler
- *
- * Holds the function that handles the endUpdate event.
- */
- mxLayoutManager.prototype.undoHandler = null;
- /**
- * Variable: moveHandler
- *
- * Holds the function that handles the move event.
- */
- mxLayoutManager.prototype.moveHandler = null;
- /**
- * Variable: resizeHandler
- *
- * Holds the function that handles the resize event.
- */
- mxLayoutManager.prototype.resizeHandler = null;
- /**
- * Function: isEnabled
- *
- * Returns true if events are handled. This implementation
- * returns <enabled>.
- */
- mxLayoutManager.prototype.isEnabled = function()
- {
- return this.enabled;
- };
- /**
- * Function: setEnabled
- *
- * Enables or disables event handling. This implementation
- * updates <enabled>.
- *
- * Parameters:
- *
- * enabled - Boolean that specifies the new enabled state.
- */
- mxLayoutManager.prototype.setEnabled = function(enabled)
- {
- this.enabled = enabled;
- };
- /**
- * Function: isBubbling
- *
- * Returns true if a layout should bubble, that is, if the parent layout
- * should be executed whenever a cell layout (layout of the children of
- * a cell) has been executed. This implementation returns <bubbling>.
- */
- mxLayoutManager.prototype.isBubbling = function()
- {
- return this.bubbling;
- };
- /**
- * Function: setBubbling
- *
- * Sets <bubbling>.
- */
- mxLayoutManager.prototype.setBubbling = function(value)
- {
- this.bubbling = value;
- };
- /**
- * Function: getGraph
- *
- * Returns the graph that this layout operates on.
- */
- mxLayoutManager.prototype.getGraph = function()
- {
- return this.graph;
- };
- /**
- * Function: setGraph
- *
- * Sets the graph that the layouts operate on.
- */
- mxLayoutManager.prototype.setGraph = function(graph)
- {
- if (this.graph != null)
- {
- var model = this.graph.getModel();
- model.removeListener(this.undoHandler);
- this.graph.removeListener(this.moveHandler);
- this.graph.removeListener(this.resizeHandler);
- }
-
- this.graph = graph;
-
- if (this.graph != null)
- {
- var model = this.graph.getModel();
- model.addListener(mxEvent.BEFORE_UNDO, this.undoHandler);
- this.graph.addListener(mxEvent.MOVE_CELLS, this.moveHandler);
- this.graph.addListener(mxEvent.RESIZE_CELLS, this.resizeHandler);
- }
- };
- /**
- * Function: hasLayout
- *
- * Returns true if the given cell has a layout. This implementation invokes
- * <getLayout> with <mxEvent.LAYOUT_CELLS> as the eventName. Override this
- * if creating layouts in <getLayout> is expensive and return true if
- * <getLayout> will return a layout for the given cell for
- * <mxEvent.BEGIN_UPDATE> or <mxEvent.END_UPDATE>.
- */
- mxLayoutManager.prototype.hasLayout = function(cell)
- {
- return this.getLayout(cell, mxEvent.LAYOUT_CELLS);
- };
- /**
- * Function: getLayout
- *
- * Returns the layout for the given cell and eventName. Possible
- * event names are <mxEvent.MOVE_CELLS> and <mxEvent.RESIZE_CELLS>
- * when cells are moved or resized and <mxEvent.BEGIN_UPDATE> or
- * <mxEvent.END_UPDATE> for the bottom up and top down phases after
- * changes to the graph model. <mxEvent.LAYOUT_CELLS> is used to
- * check if a layout exists for the given cell. This is called
- * from <hasLayout>.
- */
- mxLayoutManager.prototype.getLayout = function(cell, eventName)
- {
- return null;
- };
- /**
- * Function: beforeUndo
- *
- * Called from <undoHandler>.
- *
- * Parameters:
- *
- * cell - Array of <mxCells> that have been moved.
- * evt - Mouse event that represents the mousedown.
- */
- mxLayoutManager.prototype.beforeUndo = function(undoableEdit)
- {
- this.executeLayoutForCells(this.getCellsForChanges(undoableEdit.changes));
- };
- /**
- * Function: cellsMoved
- *
- * Called from <moveHandler>.
- *
- * Parameters:
- *
- * cell - Array of <mxCells> that have been moved.
- * evt - Mouse event that represents the mousedown.
- */
- mxLayoutManager.prototype.cellsMoved = function(cells, evt)
- {
- if (cells != null && evt != null)
- {
- var point = mxUtils.convertPoint(this.getGraph().container,
- mxEvent.getClientX(evt), mxEvent.getClientY(evt));
- var model = this.getGraph().getModel();
-
- for (var i = 0; i < cells.length; i++)
- {
- var layout = this.getLayout(model.getParent(cells[i]), mxEvent.MOVE_CELLS);
- if (layout != null)
- {
- layout.moveCell(cells[i], point.x, point.y);
- }
- }
- }
- };
- /**
- * Function: cellsResized
- *
- * Called from <resizeHandler>.
- *
- * Parameters:
- *
- * cell - Array of <mxCells> that have been resized.
- * bounds - <mxRectangle> taht represents the new bounds.
- */
- mxLayoutManager.prototype.cellsResized = function(cells, bounds, prev)
- {
- if (cells != null && bounds != null)
- {
- var model = this.getGraph().getModel();
-
- for (var i = 0; i < cells.length; i++)
- {
- var layout = this.getLayout(model.getParent(cells[i]), mxEvent.RESIZE_CELLS);
- if (layout != null)
- {
- layout.resizeCell(cells[i], bounds[i], prev[i]);
- }
- }
- }
- };
- /**
- * Function: getCellsForChanges
- *
- * Returns the cells for which a layout should be executed.
- */
- mxLayoutManager.prototype.getCellsForChanges = function(changes)
- {
- var result = [];
-
- for (var i = 0; i < changes.length; i++)
- {
- var change = changes[i];
-
- if (change instanceof mxRootChange)
- {
- return [];
- }
- else
- {
- result = result.concat(this.getCellsForChange(change));
- }
- }
-
- return result;
- };
- /**
- * Function: getCellsForChange
- *
- * Executes all layouts which have been scheduled during the
- * changes.
- */
- mxLayoutManager.prototype.getCellsForChange = function(change)
- {
- if (change instanceof mxChildChange)
- {
- return this.addCellsWithLayout(change.child,
- this.addCellsWithLayout(change.previous));
- }
- else if (change instanceof mxTerminalChange ||
- change instanceof mxGeometryChange)
- {
- return this.addCellsWithLayout(change.cell);
- }
- else if (change instanceof mxVisibleChange ||
- change instanceof mxStyleChange)
- {
- return this.addCellsWithLayout(change.cell);
- }
-
- return [];
- };
- /**
- * Function: addCellsWithLayout
- *
- * Adds all ancestors of the given cell that have a layout.
- */
- mxLayoutManager.prototype.addCellsWithLayout = function(cell, result)
- {
- return this.addDescendantsWithLayout(cell,
- this.addAncestorsWithLayout(cell, result));
- };
- /**
- * Function: addAncestorsWithLayout
- *
- * Adds all ancestors of the given cell that have a layout.
- */
- mxLayoutManager.prototype.addAncestorsWithLayout = function(cell, result)
- {
- result = (result != null) ? result : [];
-
- if (cell != null)
- {
- var layout = this.hasLayout(cell);
-
- if (layout != null)
- {
- result.push(cell);
- }
-
- if (this.isBubbling())
- {
- var model = this.getGraph().getModel();
- this.addAncestorsWithLayout(
- model.getParent(cell), result);
- }
- }
-
- return result;
- };
- /**
- * Function: addDescendantsWithLayout
- *
- * Adds all descendants of the given cell that have a layout.
- */
- mxLayoutManager.prototype.addDescendantsWithLayout = function(cell, result)
- {
- result = (result != null) ? result : [];
-
- if (cell != null && this.hasLayout(cell))
- {
- var model = this.getGraph().getModel();
-
- for (var i = 0; i < model.getChildCount(cell); i++)
- {
- var child = model.getChildAt(cell, i);
-
- if (this.hasLayout(child))
- {
- result.push(child);
- this.addDescendantsWithLayout(child, result);
- }
- }
- }
-
- return result;
- };
- /**
- * Function: executeLayoutForCells
- *
- * Executes all layouts for the given cells in two phases: In the first phase
- * layouts for child cells are executed before layouts for parent cells with
- * <mxEvent.BEGIN_UPDATE>, in the second phase layouts for parent cells are
- * executed before layouts for child cells with <mxEvent.END_UPDATE>.
- */
- mxLayoutManager.prototype.executeLayoutForCells = function(cells)
- {
- var sorted = mxUtils.sortCells(cells, false);
- this.layoutCells(sorted, true);
- this.layoutCells(sorted.reverse(), false);
- };
- /**
- * Function: layoutCells
- *
- * Executes all layouts which have been scheduled during the changes.
- */
- mxLayoutManager.prototype.layoutCells = function(cells, bubble)
- {
- if (cells.length > 0)
- {
- // Invokes the layouts while removing duplicates
- var model = this.getGraph().getModel();
-
- model.beginUpdate();
- try
- {
- var last = null;
-
- for (var i = 0; i < cells.length; i++)
- {
- if (cells[i] != model.getRoot() && cells[i] != last)
- {
- this.executeLayout(cells[i], bubble);
- last = cells[i];
- }
- }
-
- this.fireEvent(new mxEventObject(mxEvent.LAYOUT_CELLS, 'cells', cells));
- }
- finally
- {
- model.endUpdate();
- }
- }
- };
- /**
- * Function: executeLayout
- *
- * Executes the given layout on the given parent.
- */
- mxLayoutManager.prototype.executeLayout = function(cell, bubble)
- {
- var layout = this.getLayout(cell, (bubble) ?
- mxEvent.BEGIN_UPDATE : mxEvent.END_UPDATE);
- if (layout != null)
- {
- layout.execute(cell);
- }
- };
- /**
- * Function: destroy
- *
- * Removes all handlers from the <graph> and deletes the reference to it.
- */
- mxLayoutManager.prototype.destroy = function()
- {
- this.setGraph(null);
- };
- __mxOutput.mxLayoutManager = typeof mxLayoutManager !== 'undefined' ? mxLayoutManager : undefined;
|