blob: f02214f47f0a3f30c3d407c3065c14df243378ab [file] [log] [blame]
/**
* Copyright (c) 2006-2015, JGraph Ltd
* Copyright (c) 2006-2015, Gaudenz Alder
*/
/**
* Class: mxSwimlaneManager
*
* Manager for swimlanes and nested swimlanes that sets the size of newly added
* swimlanes to that of their siblings, and propagates changes to the size of a
* swimlane to its siblings, if <siblings> is true, and its ancestors, if
* <bubbling> is true.
*
* Constructor: mxSwimlaneManager
*
* Constructs a new swimlane manager for the given graph.
*
* Arguments:
*
* graph - Reference to the enclosing graph.
*/
function mxSwimlaneManager(graph, horizontal, addEnabled, resizeEnabled)
{
this.horizontal = (horizontal != null) ? horizontal : true;
this.addEnabled = (addEnabled != null) ? addEnabled : true;
this.resizeEnabled = (resizeEnabled != null) ? resizeEnabled : true;
this.addHandler = mxUtils.bind(this, function(sender, evt)
{
if (this.isEnabled() && this.isAddEnabled())
{
this.cellsAdded(evt.getProperty('cells'));
}
});
this.resizeHandler = mxUtils.bind(this, function(sender, evt)
{
if (this.isEnabled() && this.isResizeEnabled())
{
this.cellsResized(evt.getProperty('cells'));
}
});
this.setGraph(graph);
};
/**
* Extends mxEventSource.
*/
mxSwimlaneManager.prototype = new mxEventSource();
mxSwimlaneManager.prototype.constructor = mxSwimlaneManager;
/**
* Variable: graph
*
* Reference to the enclosing <mxGraph>.
*/
mxSwimlaneManager.prototype.graph = null;
/**
* Variable: enabled
*
* Specifies if event handling is enabled. Default is true.
*/
mxSwimlaneManager.prototype.enabled = true;
/**
* Variable: horizontal
*
* Specifies the orientation of the swimlanes. Default is true.
*/
mxSwimlaneManager.prototype.horizontal = true;
/**
* Variable: addEnabled
*
* Specifies if newly added cells should be resized to match the size of their
* existing siblings. Default is true.
*/
mxSwimlaneManager.prototype.addEnabled = true;
/**
* Variable: resizeEnabled
*
* Specifies if resizing of swimlanes should be handled. Default is true.
*/
mxSwimlaneManager.prototype.resizeEnabled = true;
/**
* Variable: moveHandler
*
* Holds the function that handles the move event.
*/
mxSwimlaneManager.prototype.addHandler = null;
/**
* Variable: moveHandler
*
* Holds the function that handles the move event.
*/
mxSwimlaneManager.prototype.resizeHandler = null;
/**
* Function: isEnabled
*
* Returns true if events are handled. This implementation
* returns <enabled>.
*/
mxSwimlaneManager.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.
*/
mxSwimlaneManager.prototype.setEnabled = function(value)
{
this.enabled = value;
};
/**
* Function: isHorizontal
*
* Returns <horizontal>.
*/
mxSwimlaneManager.prototype.isHorizontal = function()
{
return this.horizontal;
};
/**
* Function: setHorizontal
*
* Sets <horizontal>.
*/
mxSwimlaneManager.prototype.setHorizontal = function(value)
{
this.horizontal = value;
};
/**
* Function: isAddEnabled
*
* Returns <addEnabled>.
*/
mxSwimlaneManager.prototype.isAddEnabled = function()
{
return this.addEnabled;
};
/**
* Function: setAddEnabled
*
* Sets <addEnabled>.
*/
mxSwimlaneManager.prototype.setAddEnabled = function(value)
{
this.addEnabled = value;
};
/**
* Function: isResizeEnabled
*
* Returns <resizeEnabled>.
*/
mxSwimlaneManager.prototype.isResizeEnabled = function()
{
return this.resizeEnabled;
};
/**
* Function: setResizeEnabled
*
* Sets <resizeEnabled>.
*/
mxSwimlaneManager.prototype.setResizeEnabled = function(value)
{
this.resizeEnabled = value;
};
/**
* Function: getGraph
*
* Returns the graph that this manager operates on.
*/
mxSwimlaneManager.prototype.getGraph = function()
{
return this.graph;
};
/**
* Function: setGraph
*
* Sets the graph that the manager operates on.
*/
mxSwimlaneManager.prototype.setGraph = function(graph)
{
if (this.graph != null)
{
this.graph.removeListener(this.addHandler);
this.graph.removeListener(this.resizeHandler);
}
this.graph = graph;
if (this.graph != null)
{
this.graph.addListener(mxEvent.ADD_CELLS, this.addHandler);
this.graph.addListener(mxEvent.CELLS_RESIZED, this.resizeHandler);
}
};
/**
* Function: isSwimlaneIgnored
*
* Returns true if the given swimlane should be ignored.
*/
mxSwimlaneManager.prototype.isSwimlaneIgnored = function(swimlane)
{
return !this.getGraph().isSwimlane(swimlane);
};
/**
* Function: isCellHorizontal
*
* Returns true if the given cell is horizontal. If the given cell is not a
* swimlane, then the global orientation is returned.
*/
mxSwimlaneManager.prototype.isCellHorizontal = function(cell)
{
if (this.graph.isSwimlane(cell))
{
var style = this.graph.getCellStyle(cell);
return mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, 1) == 1;
}
return !this.isHorizontal();
};
/**
* Function: cellsAdded
*
* Called if any cells have been added.
*
* Parameters:
*
* cell - Array of <mxCells> that have been added.
*/
mxSwimlaneManager.prototype.cellsAdded = function(cells)
{
if (cells != null)
{
var model = this.getGraph().getModel();
model.beginUpdate();
try
{
for (var i = 0; i < cells.length; i++)
{
if (!this.isSwimlaneIgnored(cells[i]))
{
this.swimlaneAdded(cells[i]);
}
}
}
finally
{
model.endUpdate();
}
}
};
/**
* Function: swimlaneAdded
*
* Updates the size of the given swimlane to match that of any existing
* siblings swimlanes.
*
* Parameters:
*
* swimlane - <mxCell> that represents the new swimlane.
*/
mxSwimlaneManager.prototype.swimlaneAdded = function(swimlane)
{
var model = this.getGraph().getModel();
var parent = model.getParent(swimlane);
var childCount = model.getChildCount(parent);
var geo = null;
// Finds the first valid sibling swimlane as reference
for (var i = 0; i < childCount; i++)
{
var child = model.getChildAt(parent, i);
if (child != swimlane && !this.isSwimlaneIgnored(child))
{
geo = model.getGeometry(child);
if (geo != null)
{
break;
}
}
}
// Applies the size of the refernece to the newly added swimlane
if (geo != null)
{
var parentHorizontal = (parent != null) ? this.isCellHorizontal(parent) : this.horizontal;
this.resizeSwimlane(swimlane, geo.width, geo.height, parentHorizontal);
}
};
/**
* Function: cellsResized
*
* Called if any cells have been resizes. Calls <swimlaneResized> for all
* swimlanes where <isSwimlaneIgnored> returns false.
*
* Parameters:
*
* cells - Array of <mxCells> whose size was changed.
*/
mxSwimlaneManager.prototype.cellsResized = function(cells)
{
if (cells != null)
{
var model = this.getGraph().getModel();
model.beginUpdate();
try
{
// Finds the top-level swimlanes and adds offsets
for (var i = 0; i < cells.length; i++)
{
if (!this.isSwimlaneIgnored(cells[i]))
{
var geo = model.getGeometry(cells[i]);
if (geo != null)
{
var size = new mxRectangle(0, 0, geo.width, geo.height);
var top = cells[i];
var current = top;
while (current != null)
{
top = current;
current = model.getParent(current);
var tmp = (this.graph.isSwimlane(current)) ?
this.graph.getStartSize(current) :
new mxRectangle();
size.width += tmp.width;
size.height += tmp.height;
}
var parentHorizontal = (current != null) ? this.isCellHorizontal(current) : this.horizontal;
this.resizeSwimlane(top, size.width, size.height, parentHorizontal);
}
}
}
}
finally
{
model.endUpdate();
}
}
};
/**
* Function: resizeSwimlane
*
* Called from <cellsResized> for all swimlanes that are not ignored to update
* the size of the siblings and the size of the parent swimlanes, recursively,
* if <bubbling> is true.
*
* Parameters:
*
* swimlane - <mxCell> whose size has changed.
*/
mxSwimlaneManager.prototype.resizeSwimlane = function(swimlane, w, h, parentHorizontal)
{
var model = this.getGraph().getModel();
model.beginUpdate();
try
{
var horizontal = this.isCellHorizontal(swimlane);
if (!this.isSwimlaneIgnored(swimlane))
{
var geo = model.getGeometry(swimlane);
if (geo != null)
{
if ((parentHorizontal && geo.height != h) || (!parentHorizontal && geo.width != w))
{
geo = geo.clone();
if (parentHorizontal)
{
geo.height = h;
}
else
{
geo.width = w;
}
model.setGeometry(swimlane, geo);
}
}
}
var tmp = (this.graph.isSwimlane(swimlane)) ?
this.graph.getStartSize(swimlane) :
new mxRectangle();
w -= tmp.width;
h -= tmp.height;
var childCount = model.getChildCount(swimlane);
for (var i = 0; i < childCount; i++)
{
var child = model.getChildAt(swimlane, i);
this.resizeSwimlane(child, w, h, horizontal);
}
}
finally
{
model.endUpdate();
}
};
/**
* Function: destroy
*
* Removes all handlers from the <graph> and deletes the reference to it.
*/
mxSwimlaneManager.prototype.destroy = function()
{
this.setGraph(null);
};