mxDefaultToolbar.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxDefaultToolbar
  7. *
  8. * Toolbar for the editor. This modifies the state of the graph
  9. * or inserts new cells upon mouse clicks.
  10. *
  11. * Example:
  12. *
  13. * Create a toolbar with a button to copy the selection into the clipboard,
  14. * and a combo box with one action to paste the selection from the clipboard
  15. * into the graph.
  16. *
  17. * (code)
  18. * var toolbar = new mxDefaultToolbar(container, editor);
  19. * toolbar.addItem('Copy', null, 'copy');
  20. *
  21. * var combo = toolbar.addActionCombo('More actions...');
  22. * toolbar.addActionOption(combo, 'Paste', 'paste');
  23. * (end)
  24. *
  25. * Codec:
  26. *
  27. * This class uses the <mxDefaultToolbarCodec> to read configuration
  28. * data into an existing instance. See <mxDefaultToolbarCodec> for a
  29. * description of the configuration format.
  30. *
  31. * Constructor: mxDefaultToolbar
  32. *
  33. * Constructs a new toolbar for the given container and editor. The
  34. * container and editor may be null if a prototypical instance for a
  35. * <mxDefaultKeyHandlerCodec> is created.
  36. *
  37. * Parameters:
  38. *
  39. * container - DOM node that contains the toolbar.
  40. * editor - Reference to the enclosing <mxEditor>.
  41. */
  42. function mxDefaultToolbar(container, editor)
  43. {
  44. this.editor = editor;
  45. if (container != null && editor != null)
  46. {
  47. this.init(container);
  48. }
  49. };
  50. /**
  51. * Variable: editor
  52. *
  53. * Reference to the enclosing <mxEditor>.
  54. */
  55. mxDefaultToolbar.prototype.editor = null;
  56. /**
  57. * Variable: toolbar
  58. *
  59. * Holds the internal <mxToolbar>.
  60. */
  61. mxDefaultToolbar.prototype.toolbar = null;
  62. /**
  63. * Variable: resetHandler
  64. *
  65. * Reference to the function used to reset the <toolbar>.
  66. */
  67. mxDefaultToolbar.prototype.resetHandler = null;
  68. /**
  69. * Variable: spacing
  70. *
  71. * Defines the spacing between existing and new vertices in
  72. * gridSize units when a new vertex is dropped on an existing
  73. * cell. Default is 4 (40 pixels).
  74. */
  75. mxDefaultToolbar.prototype.spacing = 4;
  76. /**
  77. * Variable: connectOnDrop
  78. *
  79. * Specifies if elements should be connected if new cells are dropped onto
  80. * connectable elements. Default is false.
  81. */
  82. mxDefaultToolbar.prototype.connectOnDrop = false;
  83. /**
  84. * Function: init
  85. *
  86. * Constructs the <toolbar> for the given container and installs a listener
  87. * that updates the <mxEditor.insertFunction> on <editor> if an item is
  88. * selected in the toolbar. This assumes that <editor> is not null.
  89. *
  90. * Parameters:
  91. *
  92. * container - DOM node that contains the toolbar.
  93. */
  94. mxDefaultToolbar.prototype.init = function(container)
  95. {
  96. if (container != null)
  97. {
  98. this.toolbar = new mxToolbar(container);
  99. // Installs the insert function in the editor if an item is
  100. // selected in the toolbar
  101. this.toolbar.addListener(mxEvent.SELECT, mxUtils.bind(this, function(sender, evt)
  102. {
  103. var funct = evt.getProperty('function');
  104. if (funct != null)
  105. {
  106. this.editor.insertFunction = mxUtils.bind(this, function()
  107. {
  108. funct.apply(this, arguments);
  109. this.toolbar.resetMode();
  110. });
  111. }
  112. else
  113. {
  114. this.editor.insertFunction = null;
  115. }
  116. }));
  117. // Resets the selected tool after a doubleclick or escape keystroke
  118. this.resetHandler = mxUtils.bind(this, function()
  119. {
  120. if (this.toolbar != null)
  121. {
  122. this.toolbar.resetMode(true);
  123. }
  124. });
  125. this.editor.graph.addListener(mxEvent.DOUBLE_CLICK, this.resetHandler);
  126. this.editor.addListener(mxEvent.ESCAPE, this.resetHandler);
  127. }
  128. };
  129. /**
  130. * Function: addItem
  131. *
  132. * Adds a new item that executes the given action in <editor>. The title,
  133. * icon and pressedIcon are used to display the toolbar item.
  134. *
  135. * Parameters:
  136. *
  137. * title - String that represents the title (tooltip) for the item.
  138. * icon - URL of the icon to be used for displaying the item.
  139. * action - Name of the action to execute when the item is clicked.
  140. * pressed - Optional URL of the icon for the pressed state.
  141. */
  142. mxDefaultToolbar.prototype.addItem = function(title, icon, action, pressed)
  143. {
  144. var clickHandler = mxUtils.bind(this, function()
  145. {
  146. if (action != null && action.length > 0)
  147. {
  148. this.editor.execute(action);
  149. }
  150. });
  151. return this.toolbar.addItem(title, icon, clickHandler, pressed);
  152. };
  153. /**
  154. * Function: addSeparator
  155. *
  156. * Adds a vertical separator using the optional icon.
  157. *
  158. * Parameters:
  159. *
  160. * icon - Optional URL of the icon that represents the vertical separator.
  161. * Default is <mxClient.imageBasePath> + '/separator.gif'.
  162. */
  163. mxDefaultToolbar.prototype.addSeparator = function(icon)
  164. {
  165. icon = icon || mxClient.imageBasePath + '/separator.gif';
  166. this.toolbar.addSeparator(icon);
  167. };
  168. /**
  169. * Function: addCombo
  170. *
  171. * Helper method to invoke <mxToolbar.addCombo> on <toolbar> and return the
  172. * resulting DOM node.
  173. */
  174. mxDefaultToolbar.prototype.addCombo = function()
  175. {
  176. return this.toolbar.addCombo();
  177. };
  178. /**
  179. * Function: addActionCombo
  180. *
  181. * Helper method to invoke <mxToolbar.addActionCombo> on <toolbar> using
  182. * the given title and return the resulting DOM node.
  183. *
  184. * Parameters:
  185. *
  186. * title - String that represents the title of the combo.
  187. */
  188. mxDefaultToolbar.prototype.addActionCombo = function(title)
  189. {
  190. return this.toolbar.addActionCombo(title);
  191. };
  192. /**
  193. * Function: addActionOption
  194. *
  195. * Binds the given action to a option with the specified label in the
  196. * given combo. Combo is an object returned from an earlier call to
  197. * <addCombo> or <addActionCombo>.
  198. *
  199. * Parameters:
  200. *
  201. * combo - DOM node that represents the combo box.
  202. * title - String that represents the title of the combo.
  203. * action - Name of the action to execute in <editor>.
  204. */
  205. mxDefaultToolbar.prototype.addActionOption = function(combo, title, action)
  206. {
  207. var clickHandler = mxUtils.bind(this, function()
  208. {
  209. this.editor.execute(action);
  210. });
  211. this.addOption(combo, title, clickHandler);
  212. };
  213. /**
  214. * Function: addOption
  215. *
  216. * Helper method to invoke <mxToolbar.addOption> on <toolbar> and return
  217. * the resulting DOM node that represents the option.
  218. *
  219. * Parameters:
  220. *
  221. * combo - DOM node that represents the combo box.
  222. * title - String that represents the title of the combo.
  223. * value - Object that represents the value of the option.
  224. */
  225. mxDefaultToolbar.prototype.addOption = function(combo, title, value)
  226. {
  227. return this.toolbar.addOption(combo, title, value);
  228. };
  229. /**
  230. * Function: addMode
  231. *
  232. * Creates an item for selecting the given mode in the <editor>'s graph.
  233. * Supported modenames are select, connect and pan.
  234. *
  235. * Parameters:
  236. *
  237. * title - String that represents the title of the item.
  238. * icon - URL of the icon that represents the item.
  239. * mode - String that represents the mode name to be used in
  240. * <mxEditor.setMode>.
  241. * pressed - Optional URL of the icon that represents the pressed state.
  242. * funct - Optional JavaScript function that takes the <mxEditor> as the
  243. * first and only argument that is executed after the mode has been
  244. * selected.
  245. */
  246. mxDefaultToolbar.prototype.addMode = function(title, icon, mode, pressed, funct)
  247. {
  248. var clickHandler = mxUtils.bind(this, function()
  249. {
  250. this.editor.setMode(mode);
  251. if (funct != null)
  252. {
  253. funct(this.editor);
  254. }
  255. });
  256. return this.toolbar.addSwitchMode(title, icon, clickHandler, pressed);
  257. };
  258. /**
  259. * Function: addPrototype
  260. *
  261. * Creates an item for inserting a clone of the specified prototype cell into
  262. * the <editor>'s graph. The ptype may either be a cell or a function that
  263. * returns a cell.
  264. *
  265. * Parameters:
  266. *
  267. * title - String that represents the title of the item.
  268. * icon - URL of the icon that represents the item.
  269. * ptype - Function or object that represents the prototype cell. If ptype
  270. * is a function then it is invoked with no arguments to create new
  271. * instances.
  272. * pressed - Optional URL of the icon that represents the pressed state.
  273. * insert - Optional JavaScript function that handles an insert of the new
  274. * cell. This function takes the <mxEditor>, new cell to be inserted, mouse
  275. * event and optional <mxCell> under the mouse pointer as arguments.
  276. * toggle - Optional boolean that specifies if the item can be toggled.
  277. * Default is true.
  278. */
  279. mxDefaultToolbar.prototype.addPrototype = function(title, icon, ptype, pressed, insert, toggle)
  280. {
  281. // Creates a wrapper function that is in charge of constructing
  282. // the new cell instance to be inserted into the graph
  283. var factory = mxUtils.bind(this, function()
  284. {
  285. if (typeof(ptype) == 'function')
  286. {
  287. return ptype();
  288. }
  289. else if (ptype != null)
  290. {
  291. return this.editor.graph.cloneCell(ptype);
  292. }
  293. return null;
  294. });
  295. // Defines the function for a click event on the graph
  296. // after this item has been selected in the toolbar
  297. var clickHandler = mxUtils.bind(this, function(evt, cell)
  298. {
  299. if (typeof(insert) == 'function')
  300. {
  301. insert(this.editor, factory(), evt, cell);
  302. }
  303. else
  304. {
  305. this.drop(factory(), evt, cell);
  306. }
  307. this.toolbar.resetMode();
  308. mxEvent.consume(evt);
  309. });
  310. var img = this.toolbar.addMode(title, icon, clickHandler, pressed, null, toggle);
  311. // Creates a wrapper function that calls the click handler without
  312. // the graph argument
  313. var dropHandler = function(graph, evt, cell)
  314. {
  315. clickHandler(evt, cell);
  316. };
  317. this.installDropHandler(img, dropHandler);
  318. return img;
  319. };
  320. /**
  321. * Function: drop
  322. *
  323. * Handles a drop from a toolbar item to the graph. The given vertex
  324. * represents the new cell to be inserted. This invokes <insert> or
  325. * <connect> depending on the given target cell.
  326. *
  327. * Parameters:
  328. *
  329. * vertex - <mxCell> to be inserted.
  330. * evt - Mouse event that represents the drop.
  331. * target - Optional <mxCell> that represents the drop target.
  332. */
  333. mxDefaultToolbar.prototype.drop = function(vertex, evt, target)
  334. {
  335. var graph = this.editor.graph;
  336. var model = graph.getModel();
  337. if (target == null ||
  338. model.isEdge(target) ||
  339. !this.connectOnDrop ||
  340. !graph.isCellConnectable(target))
  341. {
  342. while (target != null &&
  343. !graph.isValidDropTarget(target, [vertex], evt))
  344. {
  345. target = model.getParent(target);
  346. }
  347. this.insert(vertex, evt, target);
  348. }
  349. else
  350. {
  351. this.connect(vertex, evt, target);
  352. }
  353. };
  354. /**
  355. * Function: insert
  356. *
  357. * Handles a drop by inserting the given vertex into the given parent cell
  358. * or the default parent if no parent is specified.
  359. *
  360. * Parameters:
  361. *
  362. * vertex - <mxCell> to be inserted.
  363. * evt - Mouse event that represents the drop.
  364. * parent - Optional <mxCell> that represents the parent.
  365. */
  366. mxDefaultToolbar.prototype.insert = function(vertex, evt, target)
  367. {
  368. var graph = this.editor.graph;
  369. if (graph.canImportCell(vertex))
  370. {
  371. var x = mxEvent.getClientX(evt);
  372. var y = mxEvent.getClientY(evt);
  373. var pt = mxUtils.convertPoint(graph.container, x, y);
  374. // Splits the target edge or inserts into target group
  375. if (graph.isSplitEnabled() &&
  376. graph.isSplitTarget(target, [vertex], evt))
  377. {
  378. return graph.splitEdge(target, [vertex], null, pt.x, pt.y);
  379. }
  380. else
  381. {
  382. return this.editor.addVertex(target, vertex, pt.x, pt.y);
  383. }
  384. }
  385. return null;
  386. };
  387. /**
  388. * Function: connect
  389. *
  390. * Handles a drop by connecting the given vertex to the given source cell.
  391. *
  392. * vertex - <mxCell> to be inserted.
  393. * evt - Mouse event that represents the drop.
  394. * source - Optional <mxCell> that represents the source terminal.
  395. */
  396. mxDefaultToolbar.prototype.connect = function(vertex, evt, source)
  397. {
  398. var graph = this.editor.graph;
  399. var model = graph.getModel();
  400. if (source != null &&
  401. graph.isCellConnectable(vertex) &&
  402. graph.isEdgeValid(null, source, vertex))
  403. {
  404. var edge = null;
  405. model.beginUpdate();
  406. try
  407. {
  408. var geo = model.getGeometry(source);
  409. var g = model.getGeometry(vertex).clone();
  410. // Moves the vertex away from the drop target that will
  411. // be used as the source for the new connection
  412. g.x = geo.x + (geo.width - g.width) / 2;
  413. g.y = geo.y + (geo.height - g.height) / 2;
  414. var step = this.spacing * graph.gridSize;
  415. var dist = model.getDirectedEdgeCount(source, true) * 20;
  416. if (this.editor.horizontalFlow)
  417. {
  418. g.x += (g.width + geo.width) / 2 + step + dist;
  419. }
  420. else
  421. {
  422. g.y += (g.height + geo.height) / 2 + step + dist;
  423. }
  424. vertex.setGeometry(g);
  425. // Fires two add-events with the code below - should be fixed
  426. // to only fire one add event for both inserts
  427. var parent = model.getParent(source);
  428. graph.addCell(vertex, parent);
  429. graph.constrainChild(vertex);
  430. // Creates the edge using the editor instance and calls
  431. // the second function that fires an add event
  432. edge = this.editor.createEdge(source, vertex);
  433. if (model.getGeometry(edge) == null)
  434. {
  435. var edgeGeometry = new mxGeometry();
  436. edgeGeometry.relative = true;
  437. model.setGeometry(edge, edgeGeometry);
  438. }
  439. graph.addEdge(edge, parent, source, vertex);
  440. }
  441. finally
  442. {
  443. model.endUpdate();
  444. }
  445. graph.setSelectionCells([vertex, edge]);
  446. graph.scrollCellToVisible(vertex);
  447. }
  448. };
  449. /**
  450. * Function: installDropHandler
  451. *
  452. * Makes the given img draggable using the given function for handling a
  453. * drop event.
  454. *
  455. * Parameters:
  456. *
  457. * img - DOM node that represents the image.
  458. * dropHandler - Function that handles a drop of the image.
  459. */
  460. mxDefaultToolbar.prototype.installDropHandler = function (img, dropHandler)
  461. {
  462. var sprite = document.createElement('img');
  463. sprite.setAttribute('src', img.getAttribute('src'));
  464. // Handles delayed loading of the images
  465. var loader = mxUtils.bind(this, function(evt)
  466. {
  467. // Preview uses the image node with double size. Later this can be
  468. // changed to use a separate preview and guides, but for this the
  469. // dropHandler must use the additional x- and y-arguments and the
  470. // dragsource which makeDraggable returns much be configured to
  471. // use guides via mxDragSource.isGuidesEnabled.
  472. sprite.style.width = (2 * img.offsetWidth) + 'px';
  473. sprite.style.height = (2 * img.offsetHeight) + 'px';
  474. mxUtils.makeDraggable(img, this.editor.graph, dropHandler,
  475. sprite);
  476. mxEvent.removeListener(sprite, 'load', loader);
  477. });
  478. if (mxClient.IS_IE)
  479. {
  480. loader();
  481. }
  482. else
  483. {
  484. mxEvent.addListener(sprite, 'load', loader);
  485. }
  486. };
  487. /**
  488. * Function: destroy
  489. *
  490. * Destroys the <toolbar> associated with this object and removes all
  491. * installed listeners. This does normally not need to be called, the
  492. * <toolbar> is destroyed automatically when the window unloads (in IE) by
  493. * <mxEditor>.
  494. */
  495. mxDefaultToolbar.prototype.destroy = function ()
  496. {
  497. if (this.resetHandler != null)
  498. {
  499. this.editor.graph.removeListener('dblclick', this.resetHandler);
  500. this.editor.removeListener('escape', this.resetHandler);
  501. this.resetHandler = null;
  502. }
  503. if (this.toolbar != null)
  504. {
  505. this.toolbar.destroy();
  506. this.toolbar = null;
  507. }
  508. };
  509. __mxOutput.mxDefaultToolbar = typeof mxDefaultToolbar !== 'undefined' ? mxDefaultToolbar : undefined;