mxGraphHandler.js 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865
  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. };