X Tutup
* will increment this number and each call to will decrement * it. When the counter reaches 0, the transaction is closed and the * respective events are fired. Initial value is 0. */ var $updateLevel = 0; /** * Constructor: mxGraphModel * * Constructs a new graph model using the specified root cell. */ function mxGraphModel($root = null) { if (isset($root)) { $this->setRoot($root); } else { $this->clear(); } } /** * Function: clear * * Sets a new root using . */ function clear() { $this->setRoot($this->createRoot()); } /** * Function: createRoot * * Creates a new root cell with a default layer (child 0). */ function createRoot() { $root = new mxCell(); $root->insert(new mxCell()); return $root; } /** * Function: getCells * * Returns the internal lookup table that is used to map from Ids to cells. */ function getCells() { return $this->cells; } /** * Function: setRoot * */ function getCell($id) { $result = null; if ($this->cells != null) { $result = mxUtils::getValue($this->cells, $id); } return $result; } /** * Function: getRoot * * Returns the root of the model. */ function getRoot() { return $this->root; } /** * Function: setRoot * * Sets the of the model using and adds the change to * the current transaction. This resets all datastructures in the model and * is the preferred way of clearing an existing model. Returns the new * root. * * Parameters: * * root - that specifies the new root. */ function setRoot($root) { $oldRoot = $this->root; $this->beginUpdate(); try { $this->root = $root; $this->nextId = 0; $this->cells = null; $this->cellAdded($root); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $oldRoot; } // // Cell Cloning // /** * Function: cloneCell * * Returns a deep clone of the given (including * the children) which is created using . * * Parameters: * * cell - to be cloned. */ function cloneCell($cell) { $clones = $this->cloneCells(array($cell), true); return $clones[0]; } /** * Function: cloneCells * * Returns an array of clones for the given array of . * Depending on the value of includeChildren, a deep clone is created for * each cell. Connections are restored based if the corresponding * cell is contained in the passed in array. * * Parameters: * * cells - Array of to be cloned. * includeChildren - Boolean indicating if the cells should be cloned * with all descendants. */ function cloneCells($cells, $includeChildren=true) { $mapping = array(); $clones = array(); for ($i=0; $icloneCellImpl($cell, $mapping, $includeChildren); array_push($clones, $clne); } for ($i=0; $irestoreClone($clones[$i], $cells[$i], $mapping); } return $clones; } /** * Function: cloneCellImpl * * Inner helper method for cloning cells recursively. */ function cloneCellImpl($cell, $mapping, $includeChildren) { $clne = $this->cellCloned($cell); // Stores the clone in the lookup under the // cell path for the original cell $mapping[mxCellPath::create($cell)] = $clne; if ($includeChildren) { $childCount = $this->getChildCount($cell); for ($i = 0; $i < $childCount; $i++) { $child = $this->getChildAt($cell, $i); $cloneChild = $this->cloneCellImpl($child, $mapping, true); $clne->insert($cloneChild); } } return $clne; } /** * Function: cellCloned * * Hook for cloning the cell. This returns cell->copy() or * any possible exceptions. */ function cellCloned($cell) { return $cell->copy(); } /** * Function: restoreClone * * Inner helper method for restoring the connections in * a network of cloned cells. */ function restoreClone($clne, $cell, $mapping) { $source = $this->getTerminal($cell, true); if ($source != null) { $tmp = $mapping[mxCellPath::create($source)]; if ($tmp != null) { $tmp->insertEdge($clne, true); } } $target = $this->getTerminal($cell, false); if ($target != null) { $tmp = $mapping[mxCellPath::create($target)]; if ($tmp != null) { $tmp->insertEdge($clne, false); } } $childCount = $this->getChildCount($clne); for ($i = 0; $i < $childCount; $i++) { $this->restoreClone($this->getChildAt($clne, $i), $this->getChildAt($cell, $i), $mapping); } } /** * Function: isAncestor * * Returns true if the given parent is an ancestor of the given child. * * Parameters: * * parent - that specifies the parent. * child - that specifies the child. */ function isAncestor($parent, $child) { while ($child != null && $child != $parent) { $child = $this->getParent($child); } return $child === $parent; } /** * Function: contains * * Returns true if the model contains the given . * * Parameters: * * cell - that specifies the cell. */ function contains($cell) { return $this->isAncestor($this->root, $cell); } /** * Function: getParent * * Returns the parent of the given cell. * * Parameters: * * cell - whose parent should be returned. */ function getParent($cell) { if ($cell != null) { return $cell->getParent(); } return null; } /** * Function: add * * Adds the specified child to the parent at the given index using * and adds the change to the current transaction. If no * index is specified then the child is appended to the parent's array of * children. Returns the inserted child. * * Parameters: * * parent - that specifies the parent to contain the child. * child - that specifies the child to be inserted. * index - Optional integer that specifies the index of the child. */ function add($parent, $child, $index = null) { if ($child !== $parent && $child != null && $parent != null) { $parentChanged = $parent !== $this->getParent($child); $this->beginUpdate(); try { $parent->insert($child, $index); $this->cellAdded($child); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); if ($parentChanged && $this->maintainEdgeParent) { $this->updateEdgeParents($child); } } return $child; } /** * Function: cellAdded * * Inner callback to update when a cell has been added. This * implementation resolves collisions by creating new Ids. * * Parameters: * * cell - that specifies the cell that has been added. */ function cellAdded($cell) { if ($cell->getId() == null && $this->createIds) { $cell->setId($this->createId($cell)); } if ($cell->getId() != null) { $collision = $this->getCell($cell->getId()); if ($collision != $cell) { while ($collision != null) { $cell->setId($this->createId($cell)); $collision = $this->getCell($cell->getId()); } if ($this->cells == null) { $this->cells = array(); } $this->cells[$cell->getId()] = $cell; } } // Makes sure IDs of deleted cells are not reused if (is_numeric($cell->getId())) { $this->nextId = max($this->nextId, $cell->getId() + 1); } $childCount = $this->getChildCount($cell); for ($i = 0; $i < $childCount; $i++) { $this->cellAdded($this->getChildAt($cell, $i)); } } /** * Function: createId * * Hook method to create an Id for the specified cell. This * implementation increments . * * Parameters: * * cell - to create the Id for. */ function createId($cell) { $id = $this->nextId; $this->nextId++; return $id; } /** * Function: updateEdgeParents * * Updates the parent for all edges that are connected to cell or one of * its descendants using . */ function updateEdgeParents($cell, $root = null) { // Gets the topmost node of the hierarchy $root = $root || $this->getRoot(); // Updates edges on children first $childCount = $this->getChildCount($cell); for ($i = 0; $i < $childCount; $i++) { $child = $this->getChildAt($cell, $i); $this->updateEdgeParents($child, $root); } // Updates the parents of all connected edges $edgeCount = $this->getEdgeCount($cell); $edges = array(); for ($i = 0; $i < $edgeCount; $i++) { array_push($edges, $this->getEdgeAt($cell, $i)); } foreach ($edges as $edge) { // Updates edge parent if edge and child have // a common root node (does not need to be the // model root node) if ($this->isAncestor($root, $edge)) { $this->updateEdgeParent($edge, $root); } } } /** * Function: updateEdgeParent * * Inner callback to update the parent of the specified to the * nearest-common-ancestor of its two terminals. * * Parameters: * * edge - that specifies the edge. * root - that represents the current root of the model. */ function updateEdgeParent($edge, $root) { $source = $this->getTerminal($edge, true); $target = $this->getTerminal($edge, false); $cell = null; // Uses the first non-relative descendants of the source terminal while ($source != null && !$this->isEdge($source) && $source->geometry != null && $source->geometry->relative) { $source = $this->getParent($source); } // Uses the first non-relative descendants of the target terminal while ($target != null && !$this->isEdge($target) && $target->geometry != null && $target->geometry->relative) { $target = $this->getParent($target); } if ($this->isAncestor($root, $source) && $this->isAncestor($root, $target)) { if ($source === $target) { $cell = $this->getParent($source); } else { $cell = $this->getNearestCommonAncestor($source, $target); } if ($cell != null && $this->getParent($cell) !== $this->root && $this->getParent($edge) !== $cell) { $geo = $this->getGeometry($edge); if ($geo != null) { $origin1 = $this->getOrigin($this->getParent($edge)); $origin2 = $this->getOrigin($cell); $dx = $origin2->x - $origin1->x; $dy = $origin2->y - $origin1->y; $geo = $geo->copy(); $geo->translate(-$dx, -$dy); $this->setGeometry($edge, $geo); } $this->add($cell, $edge, $this->getChildCount($cell)); } } } /** * Function: getOrigin * * Returns the absolute, cummulated origin for the children inside the * given parent as an . */ function getOrigin($cell) { $result = null; if ($cell != null) { $result = $this->getOrigin($this->getParent($cell)); if (!$this->isEdge($cell)) { $geo = $this->getGeometry($cell); if ($geo != null) { $result->x += $geo->x; $result->y += $geo->y; } } } else { $result = new mxPoint(); } return $result; } /** * Function: getNearestCommonAncestor * * Returns the nearest common ancestor for the specified cells. * * Parameters: * * cell1 - that specifies the first cell in the tree. * cell2 - that specifies the second cell in the tree. */ function getNearestCommonAncestor($cell1, $cell2) { if ($cell1 != null && $cell2 != null) { // Creates the cell path for the second cell $path = mxCellPath::create($cell2); if (isset($path) && strlen($path) > 0) { // Bubbles through the ancestors of the target // cell to find the nearest common ancestor. $cell = $cell1; $current = mxCellPath::create($cell); while ($cell != null) { $parent = $this->getParent($cell); // Checks if the cell path is equal to the beginning // of the given cell path if (strpos($path, $current.mxCellPath::$PATH_SEPARATOR) === 0 && $parent != null) { return $cell; } $current = mxCellPath::getParentPath($current); $cell = $parent; } } } return null; } /** * Function: remove * * Removes the specified cell from the model using and adds * the change to the current transaction. This operation will remove the * cell and all of its children from the model. Returns the removed cell. * * Parameters: * * cell - that should be removed. */ function remove($cell) { $this->beginUpdate(); try { if ($cell === $this->root) { $this->setRoot(null); } else { $cell->removeFromParent(); } $this->cellRemoved($cell); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $cell; } /** * Function: cellRemoved * * Inner callback to update when a cell has been removed. * * Parameters: * * cell - that specifies the cell that has been removed. */ function cellRemoved($cell) { if ($cell != null) { $childCount = $this->getChildCount($cell); for ($i = 0; $i < $childCount; $i++) { $this->cellRemoved($this->getChildAt($cell, $i)); } $cell->removeFromTerminal(true); $cell->removeFromTerminal(false); if ($this->cells != null && $cell->getId() != null) { $this->cells[$cell->getId()] = null; } } } /** * Function: getChildCount * * Returns the number of children in the given cell. * * Parameters: * * cell - whose number of children should be returned. */ function getChildCount($cell) { return ($cell != null) ? $cell->getChildCount() : 0; } /** * Function: getChildAt * * Returns the child of the given at the given index. * * Parameters: * * cell - that represents the parent. * index - Integer that specifies the index of the child to be returned. */ function getChildAt($cell, $index) { if ($cell != null) { return $cell->getChildAt($index); } return null; } /** * Function: getTerminal * * Returns the source or target of the given edge depending on the * value of the boolean parameter. * * Parameters: * * edge - that specifies the edge. * cource - Boolean indicating which end of the edge should be returned. */ function getTerminal($edge, $cource) { if ($edge != null) { return $edge->getTerminal($cource); } return null; } /** * Function: setTerminal * * Sets the source or target terminal of the given using * and adds the change to the current transaction. * This implementation updates the parent of the edge using * if required. * * Parameters: * * edge - that specifies the edge. * terminal - that specifies the new terminal. * isSource - Boolean indicating if the terminal is the new source or * target terminal of the edge. */ function setTerminal($edge, $terminal, $source) { $previous = $edge->getTerminal($source); $this->beginUpdate(); try { if ($terminal != null) { $terminal->insertEdge($edge, $source); } else if ($previous != null) { $previous->removeEdge($edge, $source); } } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); if ($this->maintainEdgeParent) { $this->updateEdgeParent($edge, $this->getRoot()); } return $terminal; } /** * Function: setTerminals * * Sets the source and target of the given in a single * transaction using for each end of the edge. * * Parameters: * * edge - that specifies the edge. * source - that specifies the new source terminal. * target - that specifies the new target terminal. */ function setTerminals($edge, $source, $target) { $this->beginUpdate(); try { $this->setTerminal($edge, $source, true); $this->setTerminal($edge, $target, false); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); } /** * Function: getEdgeCount * * Returns the number of distinct edges connected to the given cell. * * Parameters: * * cell - that represents the vertex. */ function getEdgeCount($cell) { return ($cell != null) ? $cell->getEdgeCount() : 0; } /** * Function: getEdgeAt * * Returns the edge of cell at the given index. * * Parameters: * * cell - that specifies the vertex. * index - Integer that specifies the index of the edge * to return. */ function getEdgeAt($cell, $index) { return ($cell != null) ? $cell->getEdgeAt($index) : null; } /** * Function: getEdges * * Returns all distinct edges connected to this cell as an array of * . The return value should be only be read. * * Parameters: * * cell - that specifies the cell. */ function getEdges($cell) { return ($cell != null) ? $cell->edges : null; } /** * Function: isVertex * * Returns true if the given cell is a vertex. * * Parameters: * * cell - that represents the possible vertex. */ function isVertex($cell) { return ($cell != null) ? $cell->isVertex() : null; } /** * Function: isEdge * * Returns true if the given cell is an edge. * * Parameters: * * cell - that represents the possible edge. */ function isEdge($cell) { return ($cell != null) ? $cell->isEdge() : null; } /** * Function: isConnectable * * Returns true if the given is connectable. If * is false, then this function returns false for all edges else it returns * the return value of . * * Parameters: * * cell - whose connectable state should be returned. */ function isConnectable($cell) { return ($cell != null) ? $cell->isConnectable() : false; } /** * Function: getValue * * Returns the user object of the given using . * * Parameters: * * cell - whose user object should be returned. */ function getValue($cell) { return ($cell != null) ? $cell->getValue() : null; } /** * Function: setValue * * Sets the user object of then given using * and adds the change to the current transaction. * * Parameters: * * cell - whose user object should be changed. * value - Object that defines the new user object. */ function setValue($cell, $value) { $this->beginUpdate(); try { $cell->setValue($value); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $value; } /** * Function: getGeometry * * Returns the of the given . * * Parameters: * * cell - whose geometry should be returned. */ function getGeometry($cell) { if ($cell != null) { return $cell->getGeometry(); } return null; } /** * Function: setGeometry * * Sets the of the given . The actual update * of the cell is carried out in . The * action is used to encapsulate the change. * * Parameters: * * cell - whose geometry should be changed. * geometry - that defines the new geometry. */ function setGeometry($cell, $geometry) { $this->beginUpdate(); try { $cell->setGeometry($geometry); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $geometry; } /** * Function: getStyle * * Returns the style of the given . * * Parameters: * * cell - whose style should be returned. */ function getStyle($cell) { return ($cell != null) ? $cell->getStyle() : null; } /** * Function: setStyle * * Sets the style of the given using and * adds the change to the current transaction. * * Parameters: * * cell - whose style should be changed. * style - String of the form stylename[;key=value] to specify * the new cell style. */ function setStyle($cell, $style) { $this->beginUpdate(); try { $cell->setStyle($style); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $style; } /** * Function: isCollapsed * * Returns true if the given is collapsed. * * Parameters: * * cell - whose collapsed state should be returned. */ function isCollapsed($cell) { return ($cell != null) ? $cell->isCollapsed() : false; } /** * Function: setCollapsed * * Sets the collapsed state of the given using * and adds the change to the current transaction. * * Parameters: * * cell - whose collapsed state should be changed. * collapsed - Boolean that specifies the new collpased state. */ function setCollapsed($cell, $isCollapsed) { $this->beginUpdate(); try { $cell->setCollapsed($isCollapsed); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $isCollapsed; } /** * Function: isVisible * * Returns true if the given is visible. * * Parameters: * * cell - whose visible state should be returned. */ function isVisible($cell) { return ($cell != null) ? $cell->isVisible() : false; } /** * Function: setVisible * * Sets the visible state of the given using and * adds the change to the current transaction. * * Parameters: * * cell - whose visible state should be changed. * visible - Boolean that specifies the new visible state. */ function setVisible($cell, $visible) { $this->beginUpdate(); try { $cell->setVisible($visible); } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); return $isVisible; } /** * Function: mergeChildren * * Merges the children of the given cell into the given target cell inside * this model. All cells are cloned unless there is a corresponding cell in * the model with the same id, in which case the source cell is ignored and * all edges are connected to the corresponding cell in this model. Edges * are considered to have no identity and are always cloned unless the * cloneAllEdges flag is set to false, in which case edges with the same * id in the target model are reconnected to reflect the terminals of the * source edges. */ function mergeChildren($from, $to, $cloneAllEdges = true) { $this->beginUpdate(); try { $mapping = array(); $this->mergeChildrenImpl($from, $to, $cloneAllEdges, $mapping); // Post-processes all edges in the mapping and // reconnects the terminals to the corresponding // cells in the target model foreach ($mapping as $key => $cell) { $terminal = $this->getTerminal($cell, $true); if (isset($terminal)) { $terminal = $mapping[mxCellPath::create($terminal)]; $this->setTerminal($cell, $terminal, $true); } $terminal = $this->getTerminal(cell, false); if (isset($terminal)) { $terminal = $mapping[mxCellPath::create($terminal)]; $this->setTerminal($cell, $terminal, false); } } } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); } /** * Function: mergeChildrenImpl * * Clones the children of the source cell into the given target cell in * this model and adds an entry to the mapping that maps from the source * cell to the target cell with the same id or the clone of the source cell * that was inserted into this model. */ function mergeChildrenImpl($from, $to, $cloneAllEdges, $mapping) { $this->beginUpdate(); try { $childCount = $from->getChildCount(); for ($i = 0; $i < $childCount; $i++) { $cell = $from->getChildAt($i); $id = $cell->getId(); $target = (isset($d) && (!$this->isEdge($cell) || !$cloneAllEdges)) ? $this->getCell($id) : null; // Clones and adds the child if no cell exists for the id if (!isset($target)) { $clone = $cell->clone(); $clone->setId($id); // Sets the terminals from the original cell to the clone // because the lookup uses strings not cells in PHP $clone->setTerminal($cell->getTerminal(true), true); $clone->setTerminal($cell->getTerminal(false), false); // Do *NOT* use model.add as this will move the edge away // from the parent in updateEdgeParent if maintainEdgeParent // is enabled in the target model $target = $to->insert($clone); $this->cellAdded($target); } // Stores the mapping for later reconnecting edges $mapping[mxCellPath::create($cell)] = $target; // Recurses $this->mergeChildrenImpl($cell, $target, $cloneAllEdges, $mapping); } } catch (Exception $e) { $this->endUpdate(); throw($e); } $this->endUpdate(); } /** * Function: beginUpdate * * Increments the by one. The event notification * is queued until reaches 0 by use of * . */ function beginUpdate() { $this->updateLevel++; } /** * Function: endUpdate * * Decrements the by one and fires a notification event if * the reaches 0. This function indirectly fires a * notification. */ function endUpdate() { $this->updateLevel--; if ($this->updateLevel == 0) { $this->fireEvent(new mxEventObject(mxEvent::$GRAPH_MODEL_CHANGED)); } } /** * Function: getDirectedEdgeCount * * Returns the number of incoming or outgoing edges, ignoring the given * edge. * * Parameters: * * cell - whose edge count should be returned. * outgoing - Boolean that specifies if the number of outgoing or * incoming edges should be returned. * ignoredEdge - that represents an edge to be ignored. */ function getDirectedEdgeCount($cell, $outgoing, $ignoredEdge = null) { $count = 0; $edgeCount = $this->getEdgeCount($cell); for ($i = 0; $i < $edgeCount; $i++) { $edge = $this->getEdgeAt($cell, $i); if ($edge !== $ignoredEdge && $this->getTerminal($edge, $outgoing) === $cell) { $count++; } } return $count; } } ?>
X Tutup