mxStackLayout.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxStackLayout
  7. *
  8. * Extends <mxGraphLayout> to create a horizontal or vertical stack of the
  9. * child vertices. The children do not need to be connected for this layout
  10. * to work.
  11. *
  12. * Example:
  13. *
  14. * (code)
  15. * var layout = new mxStackLayout(graph, true);
  16. * layout.execute(graph.getDefaultParent());
  17. * (end)
  18. *
  19. * Constructor: mxStackLayout
  20. *
  21. * Constructs a new stack layout layout for the specified graph,
  22. * spacing, orientation and offset.
  23. */
  24. function mxStackLayout(graph, horizontal, spacing, x0, y0, border)
  25. {
  26. mxGraphLayout.call(this, graph);
  27. this.horizontal = (horizontal != null) ? horizontal : true;
  28. this.spacing = (spacing != null) ? spacing : 0;
  29. this.x0 = (x0 != null) ? x0 : 0;
  30. this.y0 = (y0 != null) ? y0 : 0;
  31. this.border = (border != null) ? border : 0;
  32. };
  33. /**
  34. * Extends mxGraphLayout.
  35. */
  36. mxStackLayout.prototype = new mxGraphLayout();
  37. mxStackLayout.prototype.constructor = mxStackLayout;
  38. /**
  39. * Variable: horizontal
  40. *
  41. * Specifies the orientation of the layout. Default is true.
  42. */
  43. mxStackLayout.prototype.horizontal = null;
  44. /**
  45. * Variable: spacing
  46. *
  47. * Specifies the spacing between the cells. Default is 0.
  48. */
  49. mxStackLayout.prototype.spacing = null;
  50. /**
  51. * Variable: x0
  52. *
  53. * Specifies the horizontal origin of the layout. Default is 0.
  54. */
  55. mxStackLayout.prototype.x0 = null;
  56. /**
  57. * Variable: y0
  58. *
  59. * Specifies the vertical origin of the layout. Default is 0.
  60. */
  61. mxStackLayout.prototype.y0 = null;
  62. /**
  63. * Variable: border
  64. *
  65. * Border to be added if fill is true. Default is 0.
  66. */
  67. mxStackLayout.prototype.border = 0;
  68. /**
  69. * Variable: marginTop
  70. *
  71. * Top margin for the child area. Default is 0.
  72. */
  73. mxStackLayout.prototype.marginTop = 0;
  74. /**
  75. * Variable: marginLeft
  76. *
  77. * Top margin for the child area. Default is 0.
  78. */
  79. mxStackLayout.prototype.marginLeft = 0;
  80. /**
  81. * Variable: marginRight
  82. *
  83. * Top margin for the child area. Default is 0.
  84. */
  85. mxStackLayout.prototype.marginRight = 0;
  86. /**
  87. * Variable: marginBottom
  88. *
  89. * Top margin for the child area. Default is 0.
  90. */
  91. mxStackLayout.prototype.marginBottom = 0;
  92. /**
  93. * Variable: keepFirstLocation
  94. *
  95. * Boolean indicating if the location of the first cell should be
  96. * kept, that is, it will not be moved to x0 or y0. Default is false.
  97. */
  98. mxStackLayout.prototype.keepFirstLocation = false;
  99. /**
  100. * Variable: fill
  101. *
  102. * Boolean indicating if dimension should be changed to fill out the parent
  103. * cell. Default is false.
  104. */
  105. mxStackLayout.prototype.fill = false;
  106. /**
  107. * Variable: resizeParent
  108. *
  109. * If the parent should be resized to match the width/height of the
  110. * stack. Default is false.
  111. */
  112. mxStackLayout.prototype.resizeParent = false;
  113. /**
  114. * Variable: resizeParentMax
  115. *
  116. * Use maximum of existing value and new value for resize of parent.
  117. * Default is false.
  118. */
  119. mxStackLayout.prototype.resizeParentMax = false;
  120. /**
  121. * Variable: resizeLast
  122. *
  123. * If the last element should be resized to fill out the parent. Default is
  124. * false. If <resizeParent> is true then this is ignored.
  125. */
  126. mxStackLayout.prototype.resizeLast = false;
  127. /**
  128. * Variable: wrap
  129. *
  130. * Value at which a new column or row should be created. Default is null.
  131. */
  132. mxStackLayout.prototype.wrap = null;
  133. /**
  134. * Variable: borderCollapse
  135. *
  136. * If the strokeWidth should be ignored. Default is true.
  137. */
  138. mxStackLayout.prototype.borderCollapse = true;
  139. /**
  140. * Variable: allowGaps
  141. *
  142. * If gaps should be allowed in the stack. Default is false.
  143. */
  144. mxStackLayout.prototype.allowGaps = false;
  145. /**
  146. * Variable: gridSize
  147. *
  148. * Grid size for alignment of position and size. Default is 0.
  149. */
  150. mxStackLayout.prototype.gridSize = 0;
  151. /**
  152. * Function: isHorizontal
  153. *
  154. * Returns <horizontal>.
  155. */
  156. mxStackLayout.prototype.isHorizontal = function()
  157. {
  158. return this.horizontal;
  159. };
  160. /**
  161. * Function: moveCell
  162. *
  163. * Implements <mxGraphLayout.moveCell>.
  164. */
  165. mxStackLayout.prototype.moveCell = function(cell, x, y)
  166. {
  167. var model = this.graph.getModel();
  168. var parent = model.getParent(cell);
  169. var horizontal = this.isHorizontal();
  170. if (cell != null && parent != null)
  171. {
  172. var i = 0;
  173. var last = 0;
  174. var childCount = model.getChildCount(parent);
  175. var value = (horizontal) ? x : y;
  176. var pstate = this.graph.getView().getState(parent);
  177. if (pstate != null)
  178. {
  179. value -= (horizontal) ? pstate.x : pstate.y;
  180. }
  181. value /= this.graph.view.scale;
  182. for (i = 0; i < childCount; i++)
  183. {
  184. var child = model.getChildAt(parent, i);
  185. if (child != cell)
  186. {
  187. var bounds = model.getGeometry(child);
  188. if (bounds != null)
  189. {
  190. var tmp = (horizontal) ?
  191. bounds.x + bounds.width / 2 :
  192. bounds.y + bounds.height / 2;
  193. if (last <= value && tmp > value)
  194. {
  195. break;
  196. }
  197. last = tmp;
  198. }
  199. }
  200. }
  201. // Changes child order in parent
  202. var idx = parent.getIndex(cell);
  203. idx = Math.max(0, i - ((i > idx) ? 1 : 0));
  204. model.add(parent, cell, idx);
  205. }
  206. };
  207. /**
  208. * Function: getParentSize
  209. *
  210. * Returns the size for the parent container or the size of the graph
  211. * container if the parent is a layer or the root of the model.
  212. */
  213. mxStackLayout.prototype.getParentSize = function(parent)
  214. {
  215. var model = this.graph.getModel();
  216. var pgeo = model.getGeometry(parent);
  217. // Handles special case where the parent is either a layer with no
  218. // geometry or the current root of the view in which case the size
  219. // of the graph's container will be used.
  220. if (this.graph.container != null && ((pgeo == null &&
  221. model.isLayer(parent)) || parent == this.graph.getView().currentRoot))
  222. {
  223. var width = this.graph.container.offsetWidth - 1;
  224. var height = this.graph.container.offsetHeight - 1;
  225. pgeo = new mxRectangle(0, 0, width, height);
  226. }
  227. return pgeo;
  228. };
  229. /**
  230. * Function: getLayoutCells
  231. *
  232. * Returns the cells to be layouted.
  233. */
  234. mxStackLayout.prototype.getLayoutCells = function(parent)
  235. {
  236. var model = this.graph.getModel();
  237. var childCount = model.getChildCount(parent);
  238. var cells = [];
  239. for (var i = 0; i < childCount; i++)
  240. {
  241. var child = model.getChildAt(parent, i);
  242. if (!this.isVertexIgnored(child) && this.isVertexMovable(child))
  243. {
  244. cells.push(child);
  245. }
  246. }
  247. if (this.allowGaps)
  248. {
  249. cells.sort(mxUtils.bind(this, function(c1, c2)
  250. {
  251. var geo1 = this.graph.getCellGeometry(c1);
  252. var geo2 = this.graph.getCellGeometry(c2);
  253. return (this.horizontal) ?
  254. ((geo1.x == geo2.x) ? 0 : ((geo1.x > geo2.x > 0) ? 1 : -1)) :
  255. ((geo1.y == geo2.y) ? 0 : ((geo1.y > geo2.y > 0) ? 1 : -1));
  256. }));
  257. }
  258. return cells;
  259. };
  260. /**
  261. * Function: snap
  262. *
  263. * Snaps the given value to the grid size.
  264. */
  265. mxStackLayout.prototype.snap = function(value)
  266. {
  267. if (this.gridSize != null && this.gridSize > 0)
  268. {
  269. value = Math.max(value, this.gridSize);
  270. if (value / this.gridSize > 1)
  271. {
  272. var mod = value % this.gridSize;
  273. value += mod > this.gridSize / 2 ? (this.gridSize - mod) : -mod;
  274. }
  275. }
  276. return value;
  277. };
  278. /**
  279. * Function: execute
  280. *
  281. * Implements <mxGraphLayout.execute>.
  282. *
  283. * Only children where <isVertexIgnored> returns false are taken into
  284. * account.
  285. */
  286. mxStackLayout.prototype.execute = function(parent)
  287. {
  288. if (parent != null)
  289. {
  290. var pgeo = this.getParentSize(parent);
  291. var horizontal = this.isHorizontal();
  292. var model = this.graph.getModel();
  293. var fillValue = null;
  294. if (pgeo != null)
  295. {
  296. fillValue = (horizontal) ? pgeo.height - this.marginTop - this.marginBottom :
  297. pgeo.width - this.marginLeft - this.marginRight;
  298. }
  299. fillValue -= 2 * this.border;
  300. var x0 = this.x0 + this.border + this.marginLeft;
  301. var y0 = this.y0 + this.border + this.marginTop;
  302. // Handles swimlane start size
  303. if (this.graph.isSwimlane(parent))
  304. {
  305. // Uses computed style to get latest
  306. var style = this.graph.getCellStyle(parent);
  307. var start = mxUtils.getNumber(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE);
  308. var horz = mxUtils.getValue(style, mxConstants.STYLE_HORIZONTAL, true) == 1;
  309. if (pgeo != null)
  310. {
  311. if (horz)
  312. {
  313. start = Math.min(start, pgeo.height);
  314. }
  315. else
  316. {
  317. start = Math.min(start, pgeo.width);
  318. }
  319. }
  320. if (horizontal == horz)
  321. {
  322. fillValue -= start;
  323. }
  324. if (horz)
  325. {
  326. y0 += start;
  327. }
  328. else
  329. {
  330. x0 += start;
  331. }
  332. }
  333. model.beginUpdate();
  334. try
  335. {
  336. var tmp = 0;
  337. var last = null;
  338. var lastValue = 0;
  339. var lastChild = null;
  340. var cells = this.getLayoutCells(parent);
  341. for (var i = 0; i < cells.length; i++)
  342. {
  343. var child = cells[i];
  344. var geo = model.getGeometry(child);
  345. if (geo != null)
  346. {
  347. geo = geo.clone();
  348. if (this.wrap != null && last != null)
  349. {
  350. if ((horizontal && last.x + last.width +
  351. geo.width + 2 * this.spacing > this.wrap) ||
  352. (!horizontal && last.y + last.height +
  353. geo.height + 2 * this.spacing > this.wrap))
  354. {
  355. last = null;
  356. if (horizontal)
  357. {
  358. y0 += tmp + this.spacing;
  359. }
  360. else
  361. {
  362. x0 += tmp + this.spacing;
  363. }
  364. tmp = 0;
  365. }
  366. }
  367. tmp = Math.max(tmp, (horizontal) ? geo.height : geo.width);
  368. var sw = 0;
  369. if (!this.borderCollapse)
  370. {
  371. var childStyle = this.graph.getCellStyle(child);
  372. sw = mxUtils.getNumber(childStyle, mxConstants.STYLE_STROKEWIDTH, 1);
  373. }
  374. if (last != null)
  375. {
  376. var temp = lastValue + this.spacing + Math.floor(sw / 2);
  377. if (horizontal)
  378. {
  379. geo.x = this.snap(((this.allowGaps) ? Math.max(temp, geo.x) :
  380. temp) - this.marginLeft) + this.marginLeft;
  381. }
  382. else
  383. {
  384. geo.y = this.snap(((this.allowGaps) ? Math.max(temp, geo.y) :
  385. temp) - this.marginTop) + this.marginTop;
  386. }
  387. }
  388. else if (!this.keepFirstLocation)
  389. {
  390. if (horizontal)
  391. {
  392. geo.x = (this.allowGaps && geo.x > x0) ? Math.max(this.snap(geo.x -
  393. this.marginLeft) + this.marginLeft, x0) : x0;
  394. }
  395. else
  396. {
  397. geo.y = (this.allowGaps && geo.y > y0) ? Math.max(this.snap(geo.y -
  398. this.marginTop) + this.marginTop, y0) : y0;
  399. }
  400. }
  401. if (horizontal)
  402. {
  403. geo.y = y0;
  404. }
  405. else
  406. {
  407. geo.x = x0;
  408. }
  409. if (this.fill && fillValue != null)
  410. {
  411. if (horizontal)
  412. {
  413. geo.height = fillValue;
  414. }
  415. else
  416. {
  417. geo.width = fillValue;
  418. }
  419. }
  420. if (horizontal)
  421. {
  422. geo.width = this.snap(geo.width);
  423. }
  424. else
  425. {
  426. geo.height = this.snap(geo.height);
  427. }
  428. this.setChildGeometry(child, geo);
  429. lastChild = child;
  430. last = geo;
  431. if (horizontal)
  432. {
  433. lastValue = last.x + last.width + Math.floor(sw / 2);
  434. }
  435. else
  436. {
  437. lastValue = last.y + last.height + Math.floor(sw / 2);
  438. }
  439. }
  440. }
  441. if (this.resizeParent && pgeo != null && last != null && !this.graph.isCellCollapsed(parent))
  442. {
  443. this.updateParentGeometry(parent, pgeo, last);
  444. }
  445. else if (this.resizeLast && pgeo != null && last != null && lastChild != null)
  446. {
  447. if (horizontal)
  448. {
  449. last.width = pgeo.width - last.x - this.spacing - this.marginRight - this.marginLeft;
  450. }
  451. else
  452. {
  453. last.height = pgeo.height - last.y - this.spacing - this.marginBottom;
  454. }
  455. this.setChildGeometry(lastChild, last);
  456. }
  457. }
  458. finally
  459. {
  460. model.endUpdate();
  461. }
  462. }
  463. };
  464. /**
  465. * Function: setChildGeometry
  466. *
  467. * Sets the specific geometry to the given child cell.
  468. *
  469. * Parameters:
  470. *
  471. * child - The given child of <mxCell>.
  472. * geo - The specific geometry of <mxGeometry>.
  473. */
  474. mxStackLayout.prototype.setChildGeometry = function(child, geo)
  475. {
  476. var geo2 = this.graph.getCellGeometry(child);
  477. if (geo2 == null || geo.x != geo2.x || geo.y != geo2.y ||
  478. geo.width != geo2.width || geo.height != geo2.height)
  479. {
  480. this.graph.getModel().setGeometry(child, geo);
  481. }
  482. };
  483. /**
  484. * Function: updateParentGeometry
  485. *
  486. * Updates the geometry of the given parent cell.
  487. *
  488. * Parameters:
  489. *
  490. * parent - The given parent of <mxCell>.
  491. * pgeo - The new <mxGeometry> for parent.
  492. * last - The last <mxGeometry>.
  493. */
  494. mxStackLayout.prototype.updateParentGeometry = function(parent, pgeo, last)
  495. {
  496. var horizontal = this.isHorizontal();
  497. var model = this.graph.getModel();
  498. var pgeo2 = pgeo.clone();
  499. if (horizontal)
  500. {
  501. var tmp = last.x + last.width + this.marginRight + this.border;
  502. if (this.resizeParentMax)
  503. {
  504. pgeo2.width = Math.max(pgeo2.width, tmp);
  505. }
  506. else
  507. {
  508. pgeo2.width = tmp;
  509. }
  510. }
  511. else
  512. {
  513. var tmp = last.y + last.height + this.marginBottom + this.border;
  514. if (this.resizeParentMax)
  515. {
  516. pgeo2.height = Math.max(pgeo2.height, tmp);
  517. }
  518. else
  519. {
  520. pgeo2.height = tmp;
  521. }
  522. }
  523. if (pgeo.x != pgeo2.x || pgeo.y != pgeo2.y ||
  524. pgeo.width != pgeo2.width || pgeo.height != pgeo2.height)
  525. {
  526. model.setGeometry(parent, pgeo2);
  527. }
  528. };
  529. __mxOutput.mxStackLayout = typeof mxStackLayout !== 'undefined' ? mxStackLayout : undefined;