mxGraphHandler.js 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxGraphHandler
  7. *
  8. * Graph event handler that handles selection. Individual cells are handled
  9. * separately using <mxVertexHandler> or one of the edge handlers. These
  10. * handlers are created using <mxGraph.createHandler> in
  11. * <mxGraphSelectionModel.cellAdded>.
  12. *
  13. * To avoid the container to scroll a moved cell into view, set
  14. * <scrollAfterMove> to false.
  15. *
  16. * Constructor: mxGraphHandler
  17. *
  18. * Constructs an event handler that creates handles for the
  19. * selection cells.
  20. *
  21. * Parameters:
  22. *
  23. * graph - Reference to the enclosing <mxGraph>.
  24. */
  25. function mxGraphHandler(graph)
  26. {
  27. this.graph = graph;
  28. this.graph.addMouseListener(this);
  29. // Repaints the handler after autoscroll
  30. this.panHandler = mxUtils.bind(this, function()
  31. {
  32. if (!this.suspended)
  33. {
  34. this.updatePreview();
  35. this.updateHint();
  36. }
  37. });
  38. this.graph.addListener(mxEvent.PAN, this.panHandler);
  39. // Handles escape keystrokes
  40. this.escapeHandler = mxUtils.bind(this, function(sender, evt)
  41. {
  42. this.reset();
  43. });
  44. this.graph.addListener(mxEvent.ESCAPE, this.escapeHandler);
  45. // Updates the preview box for remote changes
  46. this.refreshHandler = mxUtils.bind(this, function(sender, evt)
  47. {
  48. // Merges multiple pending calls
  49. if (this.refreshThread)
  50. {
  51. window.clearTimeout(this.refreshThread);
  52. }
  53. // Waits for the states and handlers to be updated
  54. this.refreshThread = window.setTimeout(mxUtils.bind(this, function()
  55. {
  56. this.refreshThread = null;
  57. if (this.first != null && !this.suspended)
  58. {
  59. // Updates preview with no translate to compute bounding box
  60. var dx = this.currentDx;
  61. var dy = this.currentDy;
  62. this.currentDx = 0;
  63. this.currentDy = 0;
  64. this.updatePreview();
  65. this.bounds = this.graph.getView().getBounds(this.cells);
  66. this.pBounds = this.getPreviewBounds(this.cells);
  67. if (this.pBounds == null && !this.livePreviewUsed)
  68. {
  69. this.reset();
  70. }
  71. else
  72. {
  73. // Restores translate and updates preview
  74. this.currentDx = dx;
  75. this.currentDy = dy;
  76. this.updatePreview();
  77. this.updateHint();
  78. if (this.livePreviewUsed)
  79. {
  80. // Forces update to ignore last visible state
  81. this.setHandlesVisibleForCells(
  82. this.graph.selectionCellsHandler.
  83. getHandledSelectionCells(), false, true);
  84. this.updatePreview();
  85. }
  86. }
  87. }
  88. }), 0);
  89. });
  90. this.graph.getModel().addListener(mxEvent.CHANGE, this.refreshHandler);
  91. this.graph.addListener(mxEvent.REFRESH, this.refreshHandler);
  92. this.keyHandler = mxUtils.bind(this, function(e)
  93. {
  94. if (this.graph.container != null && this.graph.container.style.visibility != 'hidden' &&
  95. this.first != null && !this.suspended)
  96. {
  97. var clone = this.graph.isCloneEvent(e) &&
  98. this.graph.isCellsCloneable() &&
  99. this.isCloneEnabled();
  100. if (clone != this.cloning)
  101. {
  102. this.cloning = clone;
  103. this.checkPreview();
  104. this.updatePreview();
  105. }
  106. }
  107. });
  108. mxEvent.addListener(document, 'keydown', this.keyHandler);
  109. mxEvent.addListener(document, 'keyup', this.keyHandler);
  110. };
  111. /**
  112. * Variable: graph
  113. *
  114. * Reference to the enclosing <mxGraph>.
  115. */
  116. mxGraphHandler.prototype.graph = null;
  117. /**
  118. * Variable: maxCells
  119. *
  120. * Defines the maximum number of cells to paint subhandles
  121. * for. Default is 50 for Firefox and 20 for IE. Set this
  122. * to 0 if you want an unlimited number of handles to be
  123. * displayed. This is only recommended if the number of
  124. * cells in the graph is limited to a small number, eg.
  125. * 500.
  126. */
  127. mxGraphHandler.prototype.maxCells = (mxClient.IS_IE) ? 20 : 50;
  128. /**
  129. * Variable: enabled
  130. *
  131. * Specifies if events are handled. Default is true.
  132. */
  133. mxGraphHandler.prototype.enabled = true;
  134. /**
  135. * Variable: highlightEnabled
  136. *
  137. * Specifies if drop targets under the mouse should be enabled. Default is
  138. * true.
  139. */
  140. mxGraphHandler.prototype.highlightEnabled = true;
  141. /**
  142. * Variable: cloneEnabled
  143. *
  144. * Specifies if cloning by control-drag is enabled. Default is true.
  145. */
  146. mxGraphHandler.prototype.cloneEnabled = true;
  147. /**
  148. * Variable: moveEnabled
  149. *
  150. * Specifies if moving is enabled. Default is true.
  151. */
  152. mxGraphHandler.prototype.moveEnabled = true;
  153. /**
  154. * Variable: guidesEnabled
  155. *
  156. * Specifies if other cells should be used for snapping the right, center or
  157. * left side of the current selection. Default is false.
  158. */
  159. mxGraphHandler.prototype.guidesEnabled = false;
  160. /**
  161. * Variable: handlesVisible
  162. *
  163. * Whether the handles of the selection are currently visible.
  164. */
  165. mxGraphHandler.prototype.handlesVisible = true;
  166. /**
  167. * Variable: guide
  168. *
  169. * Holds the <mxGuide> instance that is used for alignment.
  170. */
  171. mxGraphHandler.prototype.guide = null;
  172. /**
  173. * Variable: currentDx
  174. *
  175. * Stores the x-coordinate of the current mouse move.
  176. */
  177. mxGraphHandler.prototype.currentDx = null;
  178. /**
  179. * Variable: currentDy
  180. *
  181. * Stores the y-coordinate of the current mouse move.
  182. */
  183. mxGraphHandler.prototype.currentDy = null;
  184. /**
  185. * Variable: updateCursor
  186. *
  187. * Specifies if a move cursor should be shown if the mouse is over a movable
  188. * cell. Default is true.
  189. */
  190. mxGraphHandler.prototype.updateCursor = true;
  191. /**
  192. * Variable: selectEnabled
  193. *
  194. * Specifies if selecting is enabled. Default is true.
  195. */
  196. mxGraphHandler.prototype.selectEnabled = true;
  197. /**
  198. * Variable: removeCellsFromParent
  199. *
  200. * Specifies if cells may be moved out of their parents. Default is true.
  201. */
  202. mxGraphHandler.prototype.removeCellsFromParent = true;
  203. /**
  204. * Variable: removeEmptyParents
  205. *
  206. * If empty parents should be removed from the model after all child cells
  207. * have been moved out. Default is true.
  208. */
  209. mxGraphHandler.prototype.removeEmptyParents = false;
  210. /**
  211. * Variable: connectOnDrop
  212. *
  213. * Specifies if drop events are interpreted as new connections if no other
  214. * drop action is defined. Default is false.
  215. */
  216. mxGraphHandler.prototype.connectOnDrop = false;
  217. /**
  218. * Variable: scrollOnMove
  219. *
  220. * Specifies if the view should be scrolled so that a moved cell is
  221. * visible. Default is true.
  222. */
  223. mxGraphHandler.prototype.scrollOnMove = true;
  224. /**
  225. * Variable: minimumSize
  226. *
  227. * Specifies the minimum number of pixels for the width and height of a
  228. * selection border. Default is 6.
  229. */
  230. mxGraphHandler.prototype.minimumSize = 6;
  231. /**
  232. * Variable: previewColor
  233. *
  234. * Specifies the color of the preview shape. Default is black.
  235. */
  236. mxGraphHandler.prototype.previewColor = 'black';
  237. /**
  238. * Variable: htmlPreview
  239. *
  240. * Specifies if the graph container should be used for preview. If this is used
  241. * then drop target detection relies entirely on <mxGraph.getCellAt> because
  242. * the HTML preview does not "let events through". Default is false.
  243. */
  244. mxGraphHandler.prototype.htmlPreview = false;
  245. /**
  246. * Variable: shape
  247. *
  248. * Reference to the <mxShape> that represents the preview.
  249. */
  250. mxGraphHandler.prototype.shape = null;
  251. /**
  252. * Variable: scaleGrid
  253. *
  254. * Specifies if the grid should be scaled. Default is false.
  255. */
  256. mxGraphHandler.prototype.scaleGrid = false;
  257. /**
  258. * Variable: rotationEnabled
  259. *
  260. * Specifies if the bounding box should allow for rotation. Default is true.
  261. */
  262. mxGraphHandler.prototype.rotationEnabled = true;
  263. /**
  264. * Variable: maxLivePreview
  265. *
  266. * Maximum number of cells for which live preview should be used. Default is 0
  267. * which means no live preview.
  268. */
  269. mxGraphHandler.prototype.maxLivePreview = 0;
  270. /**
  271. * Variable: allowLivePreview
  272. *
  273. * If live preview is allowed on this system. Default is true for systems with
  274. * SVG support.
  275. */
  276. mxGraphHandler.prototype.allowLivePreview = mxClient.IS_SVG;
  277. /**
  278. * Function: isEnabled
  279. *
  280. * Returns <enabled>.
  281. */
  282. mxGraphHandler.prototype.isEnabled = function()
  283. {
  284. return this.enabled;
  285. };
  286. /**
  287. * Function: setEnabled
  288. *
  289. * Sets <enabled>.
  290. */
  291. mxGraphHandler.prototype.setEnabled = function(value)
  292. {
  293. this.enabled = value;
  294. };
  295. /**
  296. * Function: isCloneEnabled
  297. *
  298. * Returns <cloneEnabled>.
  299. */
  300. mxGraphHandler.prototype.isCloneEnabled = function()
  301. {
  302. return this.cloneEnabled;
  303. };
  304. /**
  305. * Function: setCloneEnabled
  306. *
  307. * Sets <cloneEnabled>.
  308. *
  309. * Parameters:
  310. *
  311. * value - Boolean that specifies the new clone enabled state.
  312. */
  313. mxGraphHandler.prototype.setCloneEnabled = function(value)
  314. {
  315. this.cloneEnabled = value;
  316. };
  317. /**
  318. * Function: isMoveEnabled
  319. *
  320. * Returns <moveEnabled>.
  321. */
  322. mxGraphHandler.prototype.isMoveEnabled = function()
  323. {
  324. return this.moveEnabled;
  325. };
  326. /**
  327. * Function: setMoveEnabled
  328. *
  329. * Sets <moveEnabled>.
  330. */
  331. mxGraphHandler.prototype.setMoveEnabled = function(value)
  332. {
  333. this.moveEnabled = value;
  334. };
  335. /**
  336. * Function: isSelectEnabled
  337. *
  338. * Returns <selectEnabled>.
  339. */
  340. mxGraphHandler.prototype.isSelectEnabled = function()
  341. {
  342. return this.selectEnabled;
  343. };
  344. /**
  345. * Function: setSelectEnabled
  346. *
  347. * Sets <selectEnabled>.
  348. */
  349. mxGraphHandler.prototype.setSelectEnabled = function(value)
  350. {
  351. this.selectEnabled = value;
  352. };
  353. /**
  354. * Function: isRemoveCellsFromParent
  355. *
  356. * Returns <removeCellsFromParent>.
  357. */
  358. mxGraphHandler.prototype.isRemoveCellsFromParent = function()
  359. {
  360. return this.removeCellsFromParent;
  361. };
  362. /**
  363. * Function: setRemoveCellsFromParent
  364. *
  365. * Sets <removeCellsFromParent>.
  366. */
  367. mxGraphHandler.prototype.setRemoveCellsFromParent = function(value)
  368. {
  369. this.removeCellsFromParent = value;
  370. };
  371. /**
  372. * Function: isPropagateSelectionCell
  373. *
  374. * Returns true if the given cell and parent should propagate
  375. * selection state to the parent.
  376. */
  377. mxGraphHandler.prototype.isPropagateSelectionCell = function(cell, immediate, me)
  378. {
  379. var parent = this.graph.model.getParent(cell);
  380. if (immediate)
  381. {
  382. var geo = (this.graph.model.isEdge(cell)) ? null :
  383. this.graph.getCellGeometry(cell);
  384. return !this.graph.isSiblingSelected(cell) &&
  385. ((geo != null && geo.relative) ||
  386. !this.graph.isSwimlane(parent));
  387. }
  388. else
  389. {
  390. return (!this.graph.isToggleEvent(me.getEvent()) ||
  391. (!this.graph.isSiblingSelected(cell) &&
  392. !this.graph.isCellSelected(cell) &&
  393. (!this.graph.isSwimlane(parent)) ||
  394. this.graph.isCellSelected(parent))) &&
  395. (this.graph.isToggleEvent(me.getEvent()) ||
  396. !this.graph.isCellSelected(parent));
  397. }
  398. };
  399. /**
  400. * Function: getInitialCellForEvent
  401. *
  402. * Hook to return initial cell for the given event. This returns
  403. * the topmost cell that is not a swimlane or is selected.
  404. */
  405. mxGraphHandler.prototype.getInitialCellForEvent = function(me)
  406. {
  407. var state = me.getState();
  408. if ((!this.graph.isToggleEvent(me.getEvent()) || !mxEvent.isAltDown(me.getEvent())) &&
  409. state != null && !this.graph.isCellSelected(state.cell))
  410. {
  411. var model = this.graph.model;
  412. var next = this.graph.view.getState(model.getParent(state.cell));
  413. while (next != null && !this.graph.isCellSelected(next.cell) &&
  414. (model.isVertex(next.cell) || model.isEdge(next.cell)) &&
  415. this.isPropagateSelectionCell(state.cell, true, me))
  416. {
  417. state = next;
  418. next = this.graph.view.getState(this.graph.getModel().getParent(state.cell));
  419. }
  420. }
  421. return (state != null) ? state.cell : null;
  422. };
  423. /**
  424. * Function: isDelayedSelection
  425. *
  426. * Returns true if the cell or one of its ancestors is selected.
  427. */
  428. mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
  429. {
  430. if (!this.graph.isToggleEvent(me.getEvent()) || !mxEvent.isAltDown(me.getEvent()))
  431. {
  432. while (cell != null)
  433. {
  434. if (this.graph.selectionCellsHandler.isHandled(cell))
  435. {
  436. return this.graph.cellEditor.getEditingCell() != cell;
  437. }
  438. cell = this.graph.model.getParent(cell);
  439. }
  440. }
  441. return this.graph.isToggleEvent(me.getEvent()) && !mxEvent.isAltDown(me.getEvent());
  442. };
  443. /**
  444. * Function: selectDelayed
  445. *
  446. * Implements the delayed selection for the given mouse event.
  447. */
  448. mxGraphHandler.prototype.selectDelayed = function(me)
  449. {
  450. if (!this.graph.popupMenuHandler.isPopupTrigger(me))
  451. {
  452. var cell = me.getCell();
  453. if (cell == null)
  454. {
  455. cell = this.cell;
  456. }
  457. this.selectCellForEvent(cell, me);
  458. }
  459. };
  460. /**
  461. * Function: selectCellForEvent
  462. *
  463. * Selects the given cell for the given <mxMouseEvent>.
  464. */
  465. mxGraphHandler.prototype.selectCellForEvent = function(cell, me)
  466. {
  467. var state = this.graph.view.getState(cell);
  468. if (state != null)
  469. {
  470. if (me.isSource(state.control))
  471. {
  472. this.graph.selectCellForEvent(cell, me.getEvent());
  473. }
  474. else
  475. {
  476. if (!this.graph.isToggleEvent(me.getEvent()) ||
  477. !mxEvent.isAltDown(me.getEvent()))
  478. {
  479. var model = this.graph.getModel();
  480. var parent = model.getParent(cell);
  481. while (this.graph.view.getState(parent) != null &&
  482. (model.isVertex(parent) || model.isEdge(parent)) &&
  483. this.isPropagateSelectionCell(cell, false, me))
  484. {
  485. cell = parent;
  486. parent = model.getParent(cell);
  487. }
  488. }
  489. this.graph.selectCellForEvent(cell, me.getEvent());
  490. }
  491. }
  492. return cell;
  493. };
  494. /**
  495. * Function: consumeMouseEvent
  496. *
  497. * Consumes the given mouse event. NOTE: This may be used to enable click
  498. * events for links in labels on iOS as follows as consuming the initial
  499. * touchStart disables firing the subsequent click event on the link.
  500. *
  501. * <code>
  502. * mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
  503. * {
  504. * var source = mxEvent.getSource(me.getEvent());
  505. *
  506. * if (!mxEvent.isTouchEvent(me.getEvent()) || source.nodeName != 'A')
  507. * {
  508. * me.consume();
  509. * }
  510. * }
  511. * </code>
  512. */
  513. mxGraphHandler.prototype.consumeMouseEvent = function(evtName, me)
  514. {
  515. me.consume();
  516. };
  517. /**
  518. * Function: mouseDown
  519. *
  520. * Handles the event by selecing the given cell and creating a handle for
  521. * it. By consuming the event all subsequent events of the gesture are
  522. * redirected to this handler.
  523. */
  524. mxGraphHandler.prototype.mouseDown = function(sender, me)
  525. {
  526. if (!me.isConsumed() && this.isEnabled() && this.graph.isEnabled() &&
  527. me.getState() != null && !mxEvent.isMultiTouchEvent(me.getEvent()))
  528. {
  529. var cell = this.getInitialCellForEvent(me);
  530. this.delayedSelection = this.isDelayedSelection(cell, me);
  531. this.cell = null;
  532. if (this.isSelectEnabled() && !this.delayedSelection)
  533. {
  534. this.graph.selectCellForEvent(cell, me.getEvent());
  535. }
  536. if (this.isMoveEnabled())
  537. {
  538. var model = this.graph.model;
  539. var geo = model.getGeometry(cell);
  540. if (this.graph.isCellMovable(cell) && ((!model.isEdge(cell) || this.graph.getSelectionCount() > 1 ||
  541. (geo.points != null && geo.points.length > 0) || model.getTerminal(cell, true) == null ||
  542. model.getTerminal(cell, false) == null) || this.graph.allowDanglingEdges ||
  543. (this.graph.isCloneEvent(me.getEvent()) && this.graph.isCellsCloneable())))
  544. {
  545. this.start(cell, me.getX(), me.getY());
  546. }
  547. else if (this.delayedSelection)
  548. {
  549. this.cell = cell;
  550. }
  551. this.cellWasClicked = true;
  552. this.consumeMouseEvent(mxEvent.MOUSE_DOWN, me);
  553. }
  554. }
  555. };
  556. /**
  557. * Function: getGuideStates
  558. *
  559. * Creates an array of cell states which should be used as guides.
  560. */
  561. mxGraphHandler.prototype.getGuideStates = function()
  562. {
  563. var parent = this.graph.getDefaultParent();
  564. var model = this.graph.getModel();
  565. var filter = mxUtils.bind(this, function(cell)
  566. {
  567. return this.graph.view.getState(cell) != null &&
  568. model.isVertex(cell) &&
  569. model.getGeometry(cell) != null &&
  570. !model.getGeometry(cell).relative;
  571. });
  572. return this.graph.view.getCellStates(model.filterDescendants(filter, parent));
  573. };
  574. /**
  575. * Function: getCells
  576. *
  577. * Returns the cells to be modified by this handler. This implementation
  578. * returns all selection cells that are movable, or the given initial cell if
  579. * the given cell is not selected and movable. This handles the case of moving
  580. * unselectable or unselected cells.
  581. *
  582. * Parameters:
  583. *
  584. * initialCell - <mxCell> that triggered this handler.
  585. */
  586. mxGraphHandler.prototype.getCells = function(initialCell)
  587. {
  588. if (!this.delayedSelection && this.graph.isCellMovable(initialCell))
  589. {
  590. return [initialCell];
  591. }
  592. else
  593. {
  594. return this.graph.getMovableCells(this.graph.getSelectionCells());
  595. }
  596. };
  597. /**
  598. * Function: getPreviewBounds
  599. *
  600. * Returns the <mxRectangle> used as the preview bounds for
  601. * moving the given cells.
  602. */
  603. mxGraphHandler.prototype.getPreviewBounds = function(cells)
  604. {
  605. var bounds = this.getBoundingBox(cells);
  606. if (bounds != null)
  607. {
  608. // Corrects width and height
  609. bounds.width = Math.max(0, bounds.width - 1);
  610. bounds.height = Math.max(0, bounds.height - 1);
  611. if (bounds.width < this.minimumSize)
  612. {
  613. var dx = this.minimumSize - bounds.width;
  614. bounds.x -= dx / 2;
  615. bounds.width = this.minimumSize;
  616. }
  617. else
  618. {
  619. bounds.x = Math.round(bounds.x);
  620. bounds.width = Math.ceil(bounds.width);
  621. }
  622. var tr = this.graph.view.translate;
  623. var s = this.graph.view.scale;
  624. if (bounds.height < this.minimumSize)
  625. {
  626. var dy = this.minimumSize - bounds.height;
  627. bounds.y -= dy / 2;
  628. bounds.height = this.minimumSize;
  629. }
  630. else
  631. {
  632. bounds.y = Math.round(bounds.y);
  633. bounds.height = Math.ceil(bounds.height);
  634. }
  635. }
  636. return bounds;
  637. };
  638. /**
  639. * Function: getBoundingBox
  640. *
  641. * Returns the union of the <mxCellStates> for the given array of <mxCells>.
  642. * For vertices, this method uses the bounding box of the corresponding shape
  643. * if one exists. The bounding box of the corresponding text label and all
  644. * controls and overlays are ignored. See also: <mxGraphView.getBounds> and
  645. * <mxGraph.getBoundingBox>.
  646. *
  647. * Parameters:
  648. *
  649. * cells - Array of <mxCells> whose bounding box should be returned.
  650. */
  651. mxGraphHandler.prototype.getBoundingBox = function(cells)
  652. {
  653. var result = null;
  654. if (cells != null && cells.length > 0)
  655. {
  656. var model = this.graph.getModel();
  657. for (var i = 0; i < cells.length; i++)
  658. {
  659. if (model.isVertex(cells[i]) || model.isEdge(cells[i]))
  660. {
  661. var state = this.graph.view.getState(cells[i]);
  662. if (state != null)
  663. {
  664. var bbox = state;
  665. if (model.isVertex(cells[i]) && state.shape != null && state.shape.boundingBox != null)
  666. {
  667. bbox = state.shape.boundingBox;
  668. }
  669. if (result == null)
  670. {
  671. result = mxRectangle.fromRectangle(bbox);
  672. }
  673. else
  674. {
  675. result.add(bbox);
  676. }
  677. }
  678. }
  679. }
  680. }
  681. return result;
  682. };
  683. /**
  684. * Function: createPreviewShape
  685. *
  686. * Creates the shape used to draw the preview for the given bounds.
  687. */
  688. mxGraphHandler.prototype.createPreviewShape = function(bounds)
  689. {
  690. var shape = new mxRectangleShape(bounds, null, this.previewColor);
  691. shape.isDashed = true;
  692. if (this.htmlPreview)
  693. {
  694. shape.dialect = mxConstants.DIALECT_STRICTHTML;
  695. shape.init(this.graph.container);
  696. }
  697. else
  698. {
  699. // Makes sure to use either VML or SVG shapes in order to implement
  700. // event-transparency on the background area of the rectangle since
  701. // HTML shapes do not let mouseevents through even when transparent
  702. shape.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ?
  703. mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG;
  704. shape.init(this.graph.getView().getOverlayPane());
  705. shape.pointerEvents = false;
  706. // Workaround for artifacts on iOS
  707. if (mxClient.IS_IOS)
  708. {
  709. shape.getSvgScreenOffset = function()
  710. {
  711. return 0;
  712. };
  713. }
  714. }
  715. return shape;
  716. };
  717. /**
  718. * Function: start
  719. *
  720. * Starts the handling of the mouse gesture.
  721. */
  722. mxGraphHandler.prototype.start = function(cell, x, y, cells)
  723. {
  724. this.cell = cell;
  725. this.first = mxUtils.convertPoint(this.graph.container, x, y);
  726. this.cells = (cells != null) ? cells : this.getCells(this.cell);
  727. this.bounds = this.graph.getView().getBounds(this.cells);
  728. this.pBounds = this.getPreviewBounds(this.cells);
  729. this.allCells = new mxDictionary();
  730. this.cloning = false;
  731. this.cellCount = 0;
  732. for (var i = 0; i < this.cells.length; i++)
  733. {
  734. this.cellCount += this.addStates(this.cells[i], this.allCells);
  735. }
  736. if (this.guidesEnabled)
  737. {
  738. this.guide = new mxGuide(this.graph, this.getGuideStates());
  739. var parent = this.graph.model.getParent(cell);
  740. var ignore = this.graph.model.getChildCount(parent) < 2;
  741. // Uses connected states as guides
  742. var connected = new mxDictionary();
  743. var opps = this.graph.getOpposites(this.graph.getEdges(this.cell), this.cell);
  744. for (var i = 0; i < opps.length; i++)
  745. {
  746. var state = this.graph.view.getState(opps[i]);
  747. if (state != null && !connected.get(state))
  748. {
  749. connected.put(state, true);
  750. }
  751. }
  752. this.guide.isStateIgnored = mxUtils.bind(this, function(state)
  753. {
  754. var p = this.graph.model.getParent(state.cell);
  755. return state.cell != null && ((!this.cloning &&
  756. this.isCellMoving(state.cell)) ||
  757. (state.cell != (this.target || parent) && !ignore &&
  758. !connected.get(state) &&
  759. (this.target == null || this.graph.model.getChildCount(
  760. this.target) >= 2) && p != (this.target || parent)));
  761. });
  762. }
  763. };
  764. /**
  765. * Function: addStates
  766. *
  767. * Adds the states for the given cell recursively to the given dictionary.
  768. */
  769. mxGraphHandler.prototype.addStates = function(cell, dict)
  770. {
  771. var state = this.graph.view.getState(cell);
  772. var count = 0;
  773. if (state != null && dict.get(cell) == null)
  774. {
  775. dict.put(cell, state);
  776. count++;
  777. var childCount = this.graph.model.getChildCount(cell);
  778. for (var i = 0; i < childCount; i++)
  779. {
  780. count += this.addStates(this.graph.model.getChildAt(cell, i), dict);
  781. }
  782. }
  783. return count;
  784. };
  785. /**
  786. * Function: isCellMoving
  787. *
  788. * Returns true if the given cell is currently being moved.
  789. */
  790. mxGraphHandler.prototype.isCellMoving = function(cell)
  791. {
  792. return this.allCells.get(cell) != null;
  793. };
  794. /**
  795. * Function: useGuidesForEvent
  796. *
  797. * Returns true if the guides should be used for the given <mxMouseEvent>.
  798. * This implementation returns <mxGuide.isEnabledForEvent>.
  799. */
  800. mxGraphHandler.prototype.useGuidesForEvent = function(me)
  801. {
  802. return (this.guide != null) ? this.guide.isEnabledForEvent(me.getEvent()) &&
  803. !this.graph.isConstrainedEvent(me.getEvent()) : true;
  804. };
  805. /**
  806. * Function: snap
  807. *
  808. * Snaps the given vector to the grid and returns the given mxPoint instance.
  809. */
  810. mxGraphHandler.prototype.snap = function(vector)
  811. {
  812. var scale = (this.scaleGrid) ? this.graph.view.scale : 1;
  813. vector.x = this.graph.snap(vector.x / scale) * scale;
  814. vector.y = this.graph.snap(vector.y / scale) * scale;
  815. return vector;
  816. };
  817. /**
  818. * Function: getDelta
  819. *
  820. * Returns an <mxPoint> that represents the vector for moving the cells
  821. * for the given <mxMouseEvent>.
  822. */
  823. mxGraphHandler.prototype.getDelta = function(me)
  824. {
  825. var point = mxUtils.convertPoint(this.graph.container, me.getX(), me.getY());
  826. return new mxPoint(point.x - this.first.x - this.graph.panDx,
  827. point.y - this.first.y - this.graph.panDy);
  828. };
  829. /**
  830. * Function: updateHint
  831. *
  832. * Hook for subclassers do show details while the handler is active.
  833. */
  834. mxGraphHandler.prototype.updateHint = function(me) { };
  835. /**
  836. * Function: removeHint
  837. *
  838. * Hooks for subclassers to hide details when the handler gets inactive.
  839. */
  840. mxGraphHandler.prototype.removeHint = function() { };
  841. /**
  842. * Function: roundLength
  843. *
  844. * Hook for rounding the unscaled vector. Allows for half steps in the raster so
  845. * numbers coming in should be rounded if no half steps are allowed (ie for non
  846. * aligned standard moving where pixel steps should be preferred).
  847. */
  848. mxGraphHandler.prototype.roundLength = function(length)
  849. {
  850. return Math.round(length * 100) / 100;
  851. };
  852. /**
  853. * Function: isValidDropTarget
  854. *
  855. * Returns true if the given cell is a valid drop target.
  856. */
  857. mxGraphHandler.prototype.isValidDropTarget = function(target, me)
  858. {
  859. return this.graph.model.getParent(this.cell) != target;
  860. };
  861. /**
  862. * Function: checkPreview
  863. *
  864. * Updates the preview if cloning state has changed.
  865. */
  866. mxGraphHandler.prototype.checkPreview = function()
  867. {
  868. if (this.livePreviewActive && this.cloning)
  869. {
  870. this.resetLivePreview();
  871. this.livePreviewActive = false;
  872. }
  873. else if (this.maxLivePreview >= this.cellCount && !this.livePreviewActive && this.allowLivePreview)
  874. {
  875. if (!this.cloning || !this.livePreviewActive)
  876. {
  877. this.livePreviewActive = true;
  878. this.livePreviewUsed = true;
  879. }
  880. }
  881. else if (!this.livePreviewUsed && this.shape == null)
  882. {
  883. this.shape = this.createPreviewShape(this.bounds);
  884. }
  885. };
  886. /**
  887. * Function: mouseMove
  888. *
  889. * Handles the event by highlighting possible drop targets and updating the
  890. * preview.
  891. */
  892. mxGraphHandler.prototype.mouseMove = function(sender, me)
  893. {
  894. var graph = this.graph;
  895. if (!me.isConsumed() && graph.isMouseDown && this.cell != null &&
  896. this.first != null && this.bounds != null && !this.suspended)
  897. {
  898. // Stops moving if a multi touch event is received
  899. if (mxEvent.isMultiTouchEvent(me.getEvent()))
  900. {
  901. this.reset();
  902. return;
  903. }
  904. var delta = this.getDelta(me);
  905. var tol = graph.tolerance;
  906. if (this.shape != null || this.livePreviewActive || Math.abs(delta.x) > tol || Math.abs(delta.y) > tol)
  907. {
  908. // Highlight is used for highlighting drop targets
  909. if (this.highlight == null)
  910. {
  911. this.highlight = new mxCellHighlight(this.graph,
  912. mxConstants.DROP_TARGET_COLOR, 3);
  913. }
  914. var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
  915. var gridEnabled = graph.isGridEnabledEvent(me.getEvent());
  916. var cell = me.getCell();
  917. var hideGuide = true;
  918. var target = null;
  919. this.cloning = clone;
  920. if (graph.isDropEnabled() && this.highlightEnabled)
  921. {
  922. // Contains a call to getCellAt to find the cell under the mouse
  923. target = graph.getDropTarget(this.cells, me.getEvent(), cell, clone);
  924. }
  925. var state = graph.getView().getState(target);
  926. var highlight = false;
  927. if (state != null && (clone || this.isValidDropTarget(target, me)))
  928. {
  929. if (this.target != target)
  930. {
  931. this.target = target;
  932. this.setHighlightColor(mxConstants.DROP_TARGET_COLOR);
  933. }
  934. highlight = true;
  935. }
  936. else
  937. {
  938. this.target = null;
  939. if (this.connectOnDrop && cell != null && this.cells.length == 1 &&
  940. graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
  941. {
  942. state = graph.getView().getState(cell);
  943. if (state != null)
  944. {
  945. var error = graph.getEdgeValidationError(null, this.cell, cell);
  946. var color = (error == null) ?
  947. mxConstants.VALID_COLOR :
  948. mxConstants.INVALID_CONNECT_TARGET_COLOR;
  949. this.setHighlightColor(color);
  950. highlight = true;
  951. }
  952. }
  953. }
  954. if (state != null && highlight)
  955. {
  956. this.highlight.highlight(state);
  957. }
  958. else
  959. {
  960. this.highlight.hide();
  961. }
  962. if (this.guide != null && this.useGuidesForEvent(me))
  963. {
  964. delta = this.guide.move(this.bounds, delta, gridEnabled, clone);
  965. hideGuide = false;
  966. }
  967. else
  968. {
  969. delta = this.graph.snapDelta(delta, this.bounds, !gridEnabled, false, false);
  970. }
  971. if (this.guide != null && hideGuide)
  972. {
  973. this.guide.hide();
  974. }
  975. // Constrained movement if shift key is pressed
  976. if (graph.isConstrainedEvent(me.getEvent()))
  977. {
  978. if (Math.abs(delta.x) > Math.abs(delta.y))
  979. {
  980. delta.y = 0;
  981. }
  982. else
  983. {
  984. delta.x = 0;
  985. }
  986. }
  987. this.checkPreview();
  988. if (this.currentDx != delta.x || this.currentDy != delta.y)
  989. {
  990. this.currentDx = delta.x;
  991. this.currentDy = delta.y;
  992. this.updatePreview();
  993. }
  994. }
  995. this.updateHint(me);
  996. this.consumeMouseEvent(mxEvent.MOUSE_MOVE, me);
  997. // Cancels the bubbling of events to the container so
  998. // that the droptarget is not reset due to an mouseMove
  999. // fired on the container with no associated state.
  1000. mxEvent.consume(me.getEvent());
  1001. }
  1002. else if ((this.isMoveEnabled() || this.isCloneEnabled()) && this.updateCursor && !me.isConsumed() &&
  1003. (me.getState() != null || me.sourceState != null) && !graph.isMouseDown)
  1004. {
  1005. var cursor = graph.getCursorForMouseEvent(me);
  1006. if (cursor == null && graph.isEnabled() && graph.isCellMovable(me.getCell()))
  1007. {
  1008. if (graph.getModel().isEdge(me.getCell()))
  1009. {
  1010. cursor = mxConstants.CURSOR_MOVABLE_EDGE;
  1011. }
  1012. else
  1013. {
  1014. cursor = mxConstants.CURSOR_MOVABLE_VERTEX;
  1015. }
  1016. }
  1017. // Sets the cursor on the original source state under the mouse
  1018. // instead of the event source state which can be the parent
  1019. if (cursor != null && me.sourceState != null)
  1020. {
  1021. me.sourceState.setCursor(cursor);
  1022. }
  1023. }
  1024. };
  1025. /**
  1026. * Function: updatePreview
  1027. *
  1028. * Updates the bounds of the preview shape.
  1029. */
  1030. mxGraphHandler.prototype.updatePreview = function(remote)
  1031. {
  1032. if (this.livePreviewUsed && !remote)
  1033. {
  1034. if (this.cells != null)
  1035. {
  1036. this.setHandlesVisibleForCells(
  1037. this.graph.selectionCellsHandler.
  1038. getHandledSelectionCells(), false);
  1039. this.updateLivePreview(this.currentDx, this.currentDy);
  1040. }
  1041. }
  1042. else
  1043. {
  1044. this.updatePreviewShape();
  1045. }
  1046. };
  1047. /**
  1048. * Function: updatePreviewShape
  1049. *
  1050. * Updates the bounds of the preview shape.
  1051. */
  1052. mxGraphHandler.prototype.updatePreviewShape = function()
  1053. {
  1054. if (this.shape != null && this.pBounds != null)
  1055. {
  1056. this.shape.bounds = new mxRectangle(Math.round(this.pBounds.x + this.currentDx),
  1057. Math.round(this.pBounds.y + this.currentDy), this.pBounds.width, this.pBounds.height);
  1058. this.shape.redraw();
  1059. }
  1060. };
  1061. /**
  1062. * Function: updateLivePreview
  1063. *
  1064. * Updates the bounds of the preview shape.
  1065. */
  1066. mxGraphHandler.prototype.updateLivePreview = function(dx, dy)
  1067. {
  1068. if (!this.suspended)
  1069. {
  1070. var states = [];
  1071. if (this.allCells != null)
  1072. {
  1073. this.allCells.visit(mxUtils.bind(this, function(key, state)
  1074. {
  1075. var realState = this.graph.view.getState(state.cell);
  1076. // Checks if cell was removed or replaced
  1077. if (realState != state)
  1078. {
  1079. state.destroy();
  1080. if (realState != null)
  1081. {
  1082. this.allCells.put(state.cell, realState);
  1083. }
  1084. else
  1085. {
  1086. this.allCells.remove(state.cell);
  1087. }
  1088. state = realState;
  1089. }
  1090. if (state != null)
  1091. {
  1092. // Saves current state
  1093. var tempState = state.clone();
  1094. states.push([state, tempState]);
  1095. // Makes transparent for events to detect drop targets
  1096. if (state.shape != null)
  1097. {
  1098. if (state.shape.originalPointerEvents == null)
  1099. {
  1100. state.shape.originalPointerEvents = state.shape.pointerEvents;
  1101. }
  1102. state.shape.pointerEvents = false;
  1103. if (state.text != null)
  1104. {
  1105. if (state.text.originalPointerEvents == null)
  1106. {
  1107. state.text.originalPointerEvents = state.text.pointerEvents;
  1108. }
  1109. state.text.pointerEvents = false;
  1110. }
  1111. }
  1112. // Temporarily changes position
  1113. if (this.graph.model.isVertex(state.cell))
  1114. {
  1115. state.x += dx;
  1116. state.y += dy;
  1117. // Draws the live preview
  1118. if (!this.cloning)
  1119. {
  1120. state.view.graph.cellRenderer.redraw(state, true);
  1121. // Forces redraw of connected edges after all states
  1122. // have been updated but avoids update of state
  1123. state.view.invalidate(state.cell);
  1124. state.invalid = false;
  1125. // Hides folding icon
  1126. if (state.control != null && state.control.node != null)
  1127. {
  1128. state.control.node.style.visibility = 'hidden';
  1129. }
  1130. }
  1131. // Clone live preview may use text bounds
  1132. else if (state.text != null)
  1133. {
  1134. state.text.updateBoundingBox();
  1135. // Fixes preview box for edge labels
  1136. if (state.text.boundingBox != null)
  1137. {
  1138. state.text.boundingBox.x += dx;
  1139. state.text.boundingBox.y += dy;
  1140. }
  1141. if (state.text.unrotatedBoundingBox != null)
  1142. {
  1143. state.text.unrotatedBoundingBox.x += dx;
  1144. state.text.unrotatedBoundingBox.y += dy;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }));
  1150. }
  1151. // Resets the handler if everything was removed
  1152. if (states.length == 0)
  1153. {
  1154. this.reset();
  1155. }
  1156. else
  1157. {
  1158. // Redraws connected edges
  1159. var s = this.graph.view.scale;
  1160. for (var i = 0; i < states.length; i++)
  1161. {
  1162. var state = states[i][0];
  1163. if (this.graph.model.isEdge(state.cell))
  1164. {
  1165. var geometry = this.graph.getCellGeometry(state.cell);
  1166. var points = [];
  1167. if (geometry != null && geometry.points != null)
  1168. {
  1169. for (var j = 0; j < geometry.points.length; j++)
  1170. {
  1171. if (geometry.points[j] != null)
  1172. {
  1173. points.push(new mxPoint(
  1174. geometry.points[j].x + dx / s,
  1175. geometry.points[j].y + dy / s));
  1176. }
  1177. }
  1178. }
  1179. var source = state.visibleSourceState;
  1180. var target = state.visibleTargetState;
  1181. var pts = states[i][1].absolutePoints;
  1182. if (source == null || !this.isCellMoving(source.cell))
  1183. {
  1184. var pt0 = pts[0];
  1185. state.setAbsoluteTerminalPoint(new mxPoint(pt0.x + dx, pt0.y + dy), true);
  1186. source = null;
  1187. }
  1188. else
  1189. {
  1190. state.view.updateFixedTerminalPoint(state, source, true,
  1191. this.graph.getConnectionConstraint(state, source, true));
  1192. }
  1193. if (target == null || !this.isCellMoving(target.cell))
  1194. {
  1195. var ptn = pts[pts.length - 1];
  1196. state.setAbsoluteTerminalPoint(new mxPoint(ptn.x + dx, ptn.y + dy), false);
  1197. target = null;
  1198. }
  1199. else
  1200. {
  1201. state.view.updateFixedTerminalPoint(state, target, false,
  1202. this.graph.getConnectionConstraint(state, target, false));
  1203. }
  1204. state.view.updatePoints(state, points, source, target);
  1205. state.view.updateFloatingTerminalPoints(state, source, target);
  1206. state.view.updateEdgeLabelOffset(state);
  1207. state.invalid = false;
  1208. // Draws the live preview but avoids update of state
  1209. if (!this.cloning)
  1210. {
  1211. state.view.graph.cellRenderer.redraw(state, true);
  1212. }
  1213. }
  1214. }
  1215. this.graph.view.validate();
  1216. this.redrawHandles(states);
  1217. this.resetPreviewStates(states);
  1218. }
  1219. }
  1220. };
  1221. /**
  1222. * Function: redrawHandles
  1223. *
  1224. * Redraws the preview shape for the given states array.
  1225. */
  1226. mxGraphHandler.prototype.redrawHandles = function(states)
  1227. {
  1228. for (var i = 0; i < states.length; i++)
  1229. {
  1230. var handler = this.graph.selectionCellsHandler.getHandler(states[i][0].cell);
  1231. if (handler != null)
  1232. {
  1233. handler.redraw(true);
  1234. }
  1235. }
  1236. };
  1237. /**
  1238. * Function: resetPreviewStates
  1239. *
  1240. * Resets the given preview states array.
  1241. */
  1242. mxGraphHandler.prototype.resetPreviewStates = function(states)
  1243. {
  1244. for (var i = 0; i < states.length; i++)
  1245. {
  1246. states[i][0].setState(states[i][1]);
  1247. }
  1248. };
  1249. /**
  1250. * Function: suspend
  1251. *
  1252. * Suspends the livew preview.
  1253. */
  1254. mxGraphHandler.prototype.suspend = function()
  1255. {
  1256. if (!this.suspended)
  1257. {
  1258. if (this.livePreviewUsed)
  1259. {
  1260. this.updateLivePreview(0, 0);
  1261. }
  1262. if (this.shape != null)
  1263. {
  1264. this.shape.node.style.visibility = 'hidden';
  1265. }
  1266. if (this.guide != null)
  1267. {
  1268. this.guide.setVisible(false);
  1269. }
  1270. this.suspended = true;
  1271. }
  1272. };
  1273. /**
  1274. * Function: resume
  1275. *
  1276. * Suspends the livew preview.
  1277. */
  1278. mxGraphHandler.prototype.resume = function()
  1279. {
  1280. if (this.suspended)
  1281. {
  1282. this.suspended = null;
  1283. if (this.livePreviewUsed)
  1284. {
  1285. this.livePreviewActive = true;
  1286. }
  1287. if (this.shape != null)
  1288. {
  1289. this.shape.node.style.visibility = 'visible';
  1290. }
  1291. if (this.guide != null)
  1292. {
  1293. this.guide.setVisible(true);
  1294. }
  1295. }
  1296. };
  1297. /**
  1298. * Function: resetLivePreview
  1299. *
  1300. * Resets the livew preview.
  1301. */
  1302. mxGraphHandler.prototype.resetLivePreview = function()
  1303. {
  1304. if (this.allCells != null)
  1305. {
  1306. this.allCells.visit(mxUtils.bind(this, function(key, state)
  1307. {
  1308. // Restores event handling
  1309. if (state.shape != null && state.shape.originalPointerEvents != null)
  1310. {
  1311. state.shape.pointerEvents = state.shape.originalPointerEvents;
  1312. state.shape.originalPointerEvents = null;
  1313. // Forces repaint even if not moved to update pointer events
  1314. state.shape.bounds = null;
  1315. if (state.text != null)
  1316. {
  1317. state.text.pointerEvents = state.text.originalPointerEvents;
  1318. state.text.originalPointerEvents = null;
  1319. }
  1320. }
  1321. // Shows folding icon
  1322. if (state.control != null && state.control.node != null &&
  1323. state.control.node.style.visibility == 'hidden')
  1324. {
  1325. state.control.node.style.visibility = '';
  1326. }
  1327. // Fixes preview box for edge labels
  1328. if (!this.cloning)
  1329. {
  1330. if (state.text != null)
  1331. {
  1332. state.text.updateBoundingBox();
  1333. }
  1334. }
  1335. // Forces repaint of connected edges
  1336. state.view.invalidate(state.cell);
  1337. }));
  1338. // Repaints all invalid states
  1339. this.graph.view.validate();
  1340. }
  1341. };
  1342. /**
  1343. * Function: setHandlesVisibleForCells
  1344. *
  1345. * Sets wether the handles attached to the given cells are visible.
  1346. *
  1347. * Parameters:
  1348. *
  1349. * cells - Array of <mxCells>.
  1350. * visible - Boolean that specifies if the handles should be visible.
  1351. * force - Forces an update of the handler regardless of the last used value.
  1352. */
  1353. mxGraphHandler.prototype.setHandlesVisibleForCells = function(cells, visible, force)
  1354. {
  1355. if (force || this.handlesVisible != visible)
  1356. {
  1357. this.handlesVisible = visible;
  1358. for (var i = 0; i < cells.length; i++)
  1359. {
  1360. var handler = this.graph.selectionCellsHandler.getHandler(cells[i]);
  1361. if (handler != null)
  1362. {
  1363. handler.setHandlesVisible(visible);
  1364. if (visible)
  1365. {
  1366. handler.redraw();
  1367. }
  1368. }
  1369. }
  1370. }
  1371. };
  1372. /**
  1373. * Function: setHighlightColor
  1374. *
  1375. * Sets the color of the rectangle used to highlight drop targets.
  1376. *
  1377. * Parameters:
  1378. *
  1379. * color - String that represents the new highlight color.
  1380. */
  1381. mxGraphHandler.prototype.setHighlightColor = function(color)
  1382. {
  1383. if (this.highlight != null)
  1384. {
  1385. this.highlight.setHighlightColor(color);
  1386. }
  1387. };
  1388. /**
  1389. * Function: mouseUp
  1390. *
  1391. * Handles the event by applying the changes to the selection cells.
  1392. */
  1393. mxGraphHandler.prototype.mouseUp = function(sender, me)
  1394. {
  1395. if (!me.isConsumed())
  1396. {
  1397. if (this.livePreviewUsed)
  1398. {
  1399. this.resetLivePreview();
  1400. }
  1401. if (this.cell != null && this.first != null && (this.shape != null || this.livePreviewUsed) &&
  1402. this.currentDx != null && this.currentDy != null)
  1403. {
  1404. var graph = this.graph;
  1405. var cell = me.getCell();
  1406. if (this.connectOnDrop && this.target == null && cell != null && graph.getModel().isVertex(cell) &&
  1407. graph.isCellConnectable(cell) && graph.isEdgeValid(null, this.cell, cell))
  1408. {
  1409. graph.connectionHandler.connect(this.cell, cell, me.getEvent());
  1410. }
  1411. else
  1412. {
  1413. var clone = graph.isCloneEvent(me.getEvent()) && graph.isCellsCloneable() && this.isCloneEnabled();
  1414. var scale = graph.getView().scale;
  1415. var dx = this.roundLength(this.currentDx / scale);
  1416. var dy = this.roundLength(this.currentDy / scale);
  1417. var target = this.target;
  1418. if (graph.isSplitEnabled() && graph.isSplitTarget(target, this.cells, me.getEvent()))
  1419. {
  1420. graph.splitEdge(target, this.cells, null, dx, dy,
  1421. me.getGraphX(), me.getGraphY());
  1422. }
  1423. else
  1424. {
  1425. this.moveCells(this.cells, dx, dy, clone, this.target, me.getEvent());
  1426. }
  1427. }
  1428. }
  1429. else if (this.isSelectEnabled() && this.delayedSelection && this.cell != null)
  1430. {
  1431. this.selectDelayed(me);
  1432. }
  1433. }
  1434. // Consumes the event if a cell was initially clicked
  1435. if (this.cellWasClicked)
  1436. {
  1437. this.consumeMouseEvent(mxEvent.MOUSE_UP, me);
  1438. }
  1439. this.reset();
  1440. };
  1441. /**
  1442. * Function: reset
  1443. *
  1444. * Resets the state of this handler.
  1445. */
  1446. mxGraphHandler.prototype.reset = function()
  1447. {
  1448. if (this.livePreviewUsed)
  1449. {
  1450. this.resetLivePreview();
  1451. this.setHandlesVisibleForCells(
  1452. this.graph.selectionCellsHandler.
  1453. getHandledSelectionCells(), true);
  1454. }
  1455. this.destroyShapes();
  1456. this.removeHint();
  1457. this.delayedSelection = false;
  1458. this.livePreviewActive = null;
  1459. this.livePreviewUsed = null;
  1460. this.cellWasClicked = false;
  1461. this.suspended = null;
  1462. this.currentDx = null;
  1463. this.currentDy = null;
  1464. this.cellCount = null;
  1465. this.cloning = false;
  1466. this.allCells = null;
  1467. this.pBounds = null;
  1468. this.guides = null;
  1469. this.target = null;
  1470. this.first = null;
  1471. this.cells = null;
  1472. this.cell = null;
  1473. };
  1474. /**
  1475. * Function: shouldRemoveCellsFromParent
  1476. *
  1477. * Returns true if the given cells should be removed from the parent for the specified
  1478. * mousereleased event.
  1479. */
  1480. mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
  1481. {
  1482. if (this.graph.getModel().isVertex(parent))
  1483. {
  1484. var pState = this.graph.getView().getState(parent);
  1485. if (pState != null)
  1486. {
  1487. var pt = mxUtils.convertPoint(this.graph.container,
  1488. mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  1489. var alpha = mxUtils.toRadians(mxUtils.getValue(pState.style, mxConstants.STYLE_ROTATION) || 0);
  1490. if (alpha != 0)
  1491. {
  1492. var cos = Math.cos(-alpha);
  1493. var sin = Math.sin(-alpha);
  1494. var cx = new mxPoint(pState.getCenterX(), pState.getCenterY());
  1495. pt = mxUtils.getRotatedPoint(pt, cos, sin, cx);
  1496. }
  1497. return !mxUtils.contains(pState, pt.x, pt.y);
  1498. }
  1499. }
  1500. return false;
  1501. };
  1502. /**
  1503. * Function: moveCells
  1504. *
  1505. * Moves the given cells by the specified amount.
  1506. */
  1507. mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt)
  1508. {
  1509. if (clone)
  1510. {
  1511. cells = this.graph.getCloneableCells(cells);
  1512. }
  1513. // Removes cells from parent
  1514. var parent = this.graph.getModel().getParent(this.cell);
  1515. if (target == null && this.isRemoveCellsFromParent() &&
  1516. this.shouldRemoveCellsFromParent(parent, cells, evt))
  1517. {
  1518. target = this.graph.getDefaultParent();
  1519. }
  1520. // Cloning into locked cells is not allowed
  1521. clone = clone && !this.graph.isCellLocked(target || this.graph.getDefaultParent());
  1522. this.graph.getModel().beginUpdate();
  1523. try
  1524. {
  1525. var parents = [];
  1526. // Removes parent if all child cells are removed
  1527. if (!clone && target != null && this.removeEmptyParents)
  1528. {
  1529. // Collects all non-selected parents
  1530. var dict = new mxDictionary();
  1531. for (var i = 0; i < cells.length; i++)
  1532. {
  1533. dict.put(cells[i], true);
  1534. }
  1535. // LATER: Recurse up the cell hierarchy
  1536. for (var i = 0; i < cells.length; i++)
  1537. {
  1538. var par = this.graph.model.getParent(cells[i]);
  1539. if (par != null && !dict.get(par))
  1540. {
  1541. dict.put(par, true);
  1542. parents.push(par);
  1543. }
  1544. }
  1545. }
  1546. // Passes all selected cells in order to correctly clone or move into
  1547. // the target cell. The method checks for each cell if its movable.
  1548. cells = this.graph.moveCells(cells, dx, dy, clone, target, evt);
  1549. // Removes parent if all child cells are removed
  1550. var temp = [];
  1551. for (var i = 0; i < parents.length; i++)
  1552. {
  1553. if (this.shouldRemoveParent(parents[i]))
  1554. {
  1555. temp.push(parents[i]);
  1556. }
  1557. }
  1558. this.graph.removeCells(temp, false);
  1559. }
  1560. finally
  1561. {
  1562. this.graph.getModel().endUpdate();
  1563. }
  1564. // Selects the new cells if cells have been cloned
  1565. if (clone)
  1566. {
  1567. this.graph.setSelectionCells(cells);
  1568. }
  1569. if (this.isSelectEnabled() && this.scrollOnMove)
  1570. {
  1571. this.graph.scrollCellToVisible(cells[0]);
  1572. }
  1573. };
  1574. /**
  1575. * Function: shouldRemoveParent
  1576. *
  1577. * Returns true if the given parent should be removed after removal of child cells.
  1578. */
  1579. mxGraphHandler.prototype.shouldRemoveParent = function(parent)
  1580. {
  1581. var state = this.graph.view.getState(parent);
  1582. return state != null && (this.graph.model.isEdge(state.cell) || this.graph.model.isVertex(state.cell)) &&
  1583. this.graph.isCellDeletable(state.cell) && this.graph.model.getChildCount(state.cell) == 0 &&
  1584. this.graph.isTransparentState(state);
  1585. };
  1586. /**
  1587. * Function: destroyShapes
  1588. *
  1589. * Destroy the preview and highlight shapes.
  1590. */
  1591. mxGraphHandler.prototype.destroyShapes = function()
  1592. {
  1593. // Destroys the preview dashed rectangle
  1594. if (this.shape != null)
  1595. {
  1596. this.shape.destroy();
  1597. this.shape = null;
  1598. }
  1599. if (this.guide != null)
  1600. {
  1601. this.guide.destroy();
  1602. this.guide = null;
  1603. }
  1604. // Destroys the drop target highlight
  1605. if (this.highlight != null)
  1606. {
  1607. this.highlight.destroy();
  1608. this.highlight = null;
  1609. }
  1610. };
  1611. /**
  1612. * Function: destroy
  1613. *
  1614. * Destroys the handler and all its resources and DOM nodes.
  1615. */
  1616. mxGraphHandler.prototype.destroy = function()
  1617. {
  1618. this.graph.removeMouseListener(this);
  1619. this.graph.removeListener(this.panHandler);
  1620. if (this.escapeHandler != null)
  1621. {
  1622. this.graph.removeListener(this.escapeHandler);
  1623. this.escapeHandler = null;
  1624. }
  1625. if (this.refreshHandler != null)
  1626. {
  1627. this.graph.getModel().removeListener(this.refreshHandler);
  1628. this.graph.removeListener(this.refreshHandler);
  1629. this.refreshHandler = null;
  1630. }
  1631. mxEvent.removeListener(document, 'keydown', this.keyHandler);
  1632. mxEvent.removeListener(document, 'keyup', this.keyHandler);
  1633. this.destroyShapes();
  1634. this.removeHint();
  1635. };
  1636. __mxOutput.mxGraphHandler = typeof mxGraphHandler !== 'undefined' ? mxGraphHandler : undefined;