Actions.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597
  1. /**
  2. * Copyright (c) 2006-2020, JGraph Ltd
  3. * Copyright (c) 2006-2020, draw.io AG
  4. *
  5. * Constructs the actions object for the given UI.
  6. */
  7. function Actions(editorUi)
  8. {
  9. this.editorUi = editorUi;
  10. this.actions = new Object();
  11. this.init();
  12. };
  13. /**
  14. * Adds the default actions.
  15. */
  16. Actions.prototype.init = function()
  17. {
  18. var ui = this.editorUi;
  19. var editor = ui.editor;
  20. var graph = editor.graph;
  21. var isGraphEnabled = function()
  22. {
  23. return Action.prototype.isEnabled.apply(this, arguments) && graph.isEnabled();
  24. };
  25. // File actions
  26. this.addAction('new...', function() { graph.openLink(ui.getUrl()); });
  27. this.addAction('open...', function()
  28. {
  29. window.openNew = true;
  30. window.openKey = 'open';
  31. ui.openFile();
  32. });
  33. this.addAction('import...', function()
  34. {
  35. window.openNew = false;
  36. window.openKey = 'import';
  37. // Closes dialog after open
  38. window.openFile = new OpenFile(mxUtils.bind(this, function()
  39. {
  40. ui.hideDialog();
  41. }));
  42. window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename)
  43. {
  44. try
  45. {
  46. var doc = mxUtils.parseXml(xml);
  47. editor.graph.setSelectionCells(editor.graph.importGraphModel(doc.documentElement));
  48. }
  49. catch (e)
  50. {
  51. mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message);
  52. }
  53. }));
  54. // Removes openFile if dialog is closed
  55. ui.showDialog(new OpenDialog(this).container, 320, 220, true, true, function()
  56. {
  57. window.openFile = null;
  58. });
  59. }).isEnabled = isGraphEnabled;
  60. this.addAction('save', function() { ui.saveFile(false); }, null, null, Editor.ctrlKey + '+S').isEnabled = isGraphEnabled;
  61. this.addAction('saveAs...', function() { ui.saveFile(true); }, null, null, Editor.ctrlKey + '+Shift+S').isEnabled = isGraphEnabled;
  62. this.addAction('export...', function() { ui.showDialog(new ExportDialog(ui).container, 300, 296, true, true); });
  63. this.addAction('editDiagram...', function()
  64. {
  65. var dlg = new EditDiagramDialog(ui);
  66. ui.showDialog(dlg.container, 620, 420, true, false);
  67. dlg.init();
  68. });
  69. this.addAction('pageSetup...', function() { ui.showDialog(new PageSetupDialog(ui).container, 320, 220, true, true); }).isEnabled = isGraphEnabled;
  70. this.addAction('print...', function() { ui.showDialog(new PrintDialog(ui).container, 300, 180, true, true); }, null, 'sprite-print', Editor.ctrlKey + '+P');
  71. this.addAction('preview', function() { mxUtils.show(graph, null, 10, 10); });
  72. // Edit actions
  73. this.addAction('undo', function() { ui.undo(); }, null, 'sprite-undo', Editor.ctrlKey + '+Z');
  74. this.addAction('redo', function() { ui.redo(); }, null, 'sprite-redo', (!mxClient.IS_WIN) ? Editor.ctrlKey + '+Shift+Z' : Editor.ctrlKey + '+Y');
  75. this.addAction('cut', function() { mxClipboard.cut(graph); }, null, 'sprite-cut', Editor.ctrlKey + '+X');
  76. this.addAction('copy', function()
  77. {
  78. try
  79. {
  80. mxClipboard.copy(graph);
  81. }
  82. catch (e)
  83. {
  84. ui.handleError(e);
  85. }
  86. }, null, 'sprite-copy', Editor.ctrlKey + '+C');
  87. this.addAction('paste', function()
  88. {
  89. if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
  90. {
  91. mxClipboard.paste(graph);
  92. }
  93. }, false, 'sprite-paste', Editor.ctrlKey + '+V');
  94. this.addAction('pasteHere', function(evt)
  95. {
  96. if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
  97. {
  98. graph.getModel().beginUpdate();
  99. try
  100. {
  101. var cells = mxClipboard.paste(graph);
  102. if (cells != null)
  103. {
  104. var includeEdges = true;
  105. for (var i = 0; i < cells.length && includeEdges; i++)
  106. {
  107. includeEdges = includeEdges && graph.model.isEdge(cells[i]);
  108. }
  109. var t = graph.view.translate;
  110. var s = graph.view.scale;
  111. var dx = t.x;
  112. var dy = t.y;
  113. var bb = null;
  114. if (cells.length == 1 && includeEdges)
  115. {
  116. var geo = graph.getCellGeometry(cells[0]);
  117. if (geo != null)
  118. {
  119. bb = geo.getTerminalPoint(true);
  120. }
  121. }
  122. bb = (bb != null) ? bb : graph.getBoundingBoxFromGeometry(cells, includeEdges);
  123. if (bb != null)
  124. {
  125. var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx));
  126. var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy));
  127. graph.cellsMoved(cells, x - bb.x, y - bb.y);
  128. }
  129. }
  130. }
  131. finally
  132. {
  133. graph.getModel().endUpdate();
  134. }
  135. }
  136. });
  137. this.addAction('copySize', function(evt)
  138. {
  139. var cell = graph.getSelectionCell();
  140. if (graph.isEnabled() && cell != null && graph.getModel().isVertex(cell))
  141. {
  142. var geo = graph.getCellGeometry(cell);
  143. if (geo != null)
  144. {
  145. ui.copiedSize = new mxRectangle(geo.x, geo.y, geo.width, geo.height);
  146. }
  147. }
  148. }, null, null, 'Alt+Shift+X');
  149. this.addAction('pasteSize', function(evt)
  150. {
  151. if (graph.isEnabled() && !graph.isSelectionEmpty() && ui.copiedSize != null)
  152. {
  153. graph.getModel().beginUpdate();
  154. try
  155. {
  156. var cells = graph.getSelectionCells();
  157. for (var i = 0; i < cells.length; i++)
  158. {
  159. if (graph.getModel().isVertex(cells[i]))
  160. {
  161. var geo = graph.getCellGeometry(cells[i]);
  162. if (geo != null)
  163. {
  164. geo = geo.clone();
  165. geo.width = ui.copiedSize.width;
  166. geo.height = ui.copiedSize.height;
  167. graph.getModel().setGeometry(cells[i], geo);
  168. }
  169. }
  170. }
  171. }
  172. finally
  173. {
  174. graph.getModel().endUpdate();
  175. }
  176. }
  177. }, null, null, 'Alt+Shift+V');
  178. function deleteCells(includeEdges)
  179. {
  180. // Cancels interactive operations
  181. graph.escape();
  182. var select = graph.deleteCells(graph.getDeletableCells(graph.getSelectionCells()), includeEdges);
  183. if (select != null)
  184. {
  185. graph.setSelectionCells(select);
  186. }
  187. };
  188. this.addAction('delete', function(evt)
  189. {
  190. deleteCells(evt != null && mxEvent.isControlDown(evt));
  191. }, null, null, 'Delete');
  192. this.addAction('deleteAll', function()
  193. {
  194. if (!graph.isSelectionEmpty())
  195. {
  196. graph.getModel().beginUpdate();
  197. try
  198. {
  199. var cells = graph.getSelectionCells();
  200. for (var i = 0; i < cells.length; i++)
  201. {
  202. graph.cellLabelChanged(cells[i], '');
  203. }
  204. }
  205. finally
  206. {
  207. graph.getModel().endUpdate();
  208. }
  209. }
  210. });
  211. this.addAction('deleteLabels', function()
  212. {
  213. if (!graph.isSelectionEmpty())
  214. {
  215. graph.getModel().beginUpdate();
  216. try
  217. {
  218. var cells = graph.getSelectionCells();
  219. for (var i = 0; i < cells.length; i++)
  220. {
  221. graph.cellLabelChanged(cells[i], '');
  222. }
  223. }
  224. finally
  225. {
  226. graph.getModel().endUpdate();
  227. }
  228. }
  229. }, null, null, Editor.ctrlKey + '+Delete');
  230. this.addAction('duplicate', function()
  231. {
  232. try
  233. {
  234. graph.setSelectionCells(graph.duplicateCells());
  235. graph.scrollCellToVisible(graph.getSelectionCell());
  236. }
  237. catch (e)
  238. {
  239. ui.handleError(e);
  240. }
  241. }, null, null, Editor.ctrlKey + '+D');
  242. this.put('turn', new Action(mxResources.get('turn') + ' / ' + mxResources.get('reverse'), function(evt)
  243. {
  244. graph.turnShapes(graph.getSelectionCells(), (evt != null) ? mxEvent.isShiftDown(evt) : false);
  245. }, null, null, Editor.ctrlKey + '+R'));
  246. this.addAction('selectVertices', function() { graph.selectVertices(null, true); }, null, null, Editor.ctrlKey + '+Shift+I');
  247. this.addAction('selectEdges', function() { graph.selectEdges(); }, null, null, Editor.ctrlKey + '+Shift+E');
  248. this.addAction('selectAll', function() { graph.selectAll(null, true); }, null, null, Editor.ctrlKey + '+A');
  249. this.addAction('selectNone', function() { graph.clearSelection(); }, null, null, Editor.ctrlKey + '+Shift+A');
  250. this.addAction('lockUnlock', function()
  251. {
  252. if (!graph.isSelectionEmpty())
  253. {
  254. graph.getModel().beginUpdate();
  255. try
  256. {
  257. var defaultValue = graph.isCellMovable(graph.getSelectionCell()) ? 1 : 0;
  258. graph.toggleCellStyles(mxConstants.STYLE_MOVABLE, defaultValue);
  259. graph.toggleCellStyles(mxConstants.STYLE_RESIZABLE, defaultValue);
  260. graph.toggleCellStyles(mxConstants.STYLE_ROTATABLE, defaultValue);
  261. graph.toggleCellStyles(mxConstants.STYLE_DELETABLE, defaultValue);
  262. graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, defaultValue);
  263. graph.toggleCellStyles('connectable', defaultValue);
  264. }
  265. finally
  266. {
  267. graph.getModel().endUpdate();
  268. }
  269. }
  270. }, null, null, Editor.ctrlKey + '+L');
  271. // Navigation actions
  272. this.addAction('home', function() { graph.home(); }, null, null, 'Shift+Home');
  273. this.addAction('exitGroup', function() { graph.exitGroup(); }, null, null, Editor.ctrlKey + '+Shift+Home');
  274. this.addAction('enterGroup', function() { graph.enterGroup(); }, null, null, Editor.ctrlKey + '+Shift+End');
  275. this.addAction('collapse', function() { graph.foldCells(true); }, null, null, Editor.ctrlKey + '+Home');
  276. this.addAction('expand', function() { graph.foldCells(false); }, null, null, Editor.ctrlKey + '+End');
  277. // Arrange actions
  278. this.addAction('toFront', function() { graph.orderCells(false); }, null, null, Editor.ctrlKey + '+Shift+F');
  279. this.addAction('toBack', function() { graph.orderCells(true); }, null, null, Editor.ctrlKey + '+Shift+B');
  280. this.addAction('group', function()
  281. {
  282. if (graph.isEnabled())
  283. {
  284. var cells = mxUtils.sortCells(graph.getSelectionCells(), true);
  285. if (cells.length == 1 && !graph.isTable(cells[0]) && !graph.isTableRow(cells[0]))
  286. {
  287. graph.setCellStyles('container', '1');
  288. }
  289. else
  290. {
  291. cells = graph.getCellsForGroup(cells);
  292. if (cells.length > 1)
  293. {
  294. graph.setSelectionCell(graph.groupCells(null, 0, cells));
  295. }
  296. }
  297. }
  298. }, null, null, Editor.ctrlKey + '+G');
  299. this.addAction('ungroup', function()
  300. {
  301. if (graph.isEnabled())
  302. {
  303. var cells = graph.getSelectionCells();
  304. graph.model.beginUpdate();
  305. try
  306. {
  307. var temp = graph.ungroupCells();
  308. // Clears container flag for remaining cells
  309. if (cells != null)
  310. {
  311. for (var i = 0; i < cells.length; i++)
  312. {
  313. if (graph.model.contains(cells[i]))
  314. {
  315. if (graph.model.getChildCount(cells[i]) == 0 &&
  316. graph.model.isVertex(cells[i]))
  317. {
  318. graph.setCellStyles('container', '0', [cells[i]]);
  319. }
  320. temp.push(cells[i]);
  321. }
  322. }
  323. }
  324. }
  325. finally
  326. {
  327. graph.model.endUpdate();
  328. }
  329. graph.setSelectionCells(temp);
  330. }
  331. }, null, null, Editor.ctrlKey + '+Shift+U');
  332. this.addAction('removeFromGroup', function()
  333. {
  334. if (graph.isEnabled())
  335. {
  336. var cells = graph.getSelectionCells();
  337. // Removes table rows and cells
  338. if (cells != null)
  339. {
  340. var temp = [];
  341. for (var i = 0; i < cells.length; i++)
  342. {
  343. if (!graph.isTableRow(cells[i]) &&
  344. !graph.isTableCell(cells[i]))
  345. {
  346. temp.push(cells[i]);
  347. }
  348. }
  349. graph.removeCellsFromParent(temp);
  350. }
  351. }
  352. });
  353. // Adds action
  354. this.addAction('edit', function()
  355. {
  356. if (graph.isEnabled())
  357. {
  358. graph.startEditingAtCell();
  359. }
  360. }, null, null, 'F2/Enter');
  361. this.addAction('editData...', function()
  362. {
  363. var cell = graph.getSelectionCell() || graph.getModel().getRoot();
  364. ui.showDataDialog(cell);
  365. }, null, null, Editor.ctrlKey + '+M');
  366. this.addAction('editTooltip...', function()
  367. {
  368. if (graph.isEnabled() && !graph.isSelectionEmpty())
  369. {
  370. var cell = graph.getSelectionCell();
  371. var tooltip = '';
  372. if (mxUtils.isNode(cell.value))
  373. {
  374. var tmp = null;
  375. if (Graph.translateDiagram && Graph.diagramLanguage != null &&
  376. cell.value.hasAttribute('tooltip_' + Graph.diagramLanguage))
  377. {
  378. tmp = cell.value.getAttribute('tooltip_' + Graph.diagramLanguage);
  379. }
  380. if (tmp == null)
  381. {
  382. tmp = cell.value.getAttribute('tooltip');
  383. }
  384. if (tmp != null)
  385. {
  386. tooltip = tmp;
  387. }
  388. }
  389. var dlg = new TextareaDialog(ui, mxResources.get('editTooltip') + ':', tooltip, function(newValue)
  390. {
  391. graph.setTooltipForCell(cell, newValue);
  392. });
  393. ui.showDialog(dlg.container, 320, 200, true, true);
  394. dlg.init();
  395. }
  396. }, null, null, 'Alt+Shift+T');
  397. this.addAction('openLink', function()
  398. {
  399. var link = graph.getLinkForCell(graph.getSelectionCell());
  400. if (link != null)
  401. {
  402. graph.openLink(link);
  403. }
  404. });
  405. this.addAction('editLink...', function()
  406. {
  407. if (graph.isEnabled() && !graph.isSelectionEmpty())
  408. {
  409. var cell = graph.getSelectionCell();
  410. var value = graph.getLinkForCell(cell) || '';
  411. ui.showLinkDialog(value, mxResources.get('apply'), function(link)
  412. {
  413. link = mxUtils.trim(link);
  414. graph.setLinkForCell(cell, (link.length > 0) ? link : null);
  415. });
  416. }
  417. }, null, null, 'Alt+Shift+L');
  418. this.put('insertImage', new Action(mxResources.get('image') + '...', function()
  419. {
  420. if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
  421. {
  422. graph.clearSelection();
  423. ui.actions.get('image').funct();
  424. }
  425. })).isEnabled = isGraphEnabled;
  426. this.put('insertLink', new Action(mxResources.get('link') + '...', function()
  427. {
  428. if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
  429. {
  430. ui.showLinkDialog('', mxResources.get('insert'), function(link, docs)
  431. {
  432. link = mxUtils.trim(link);
  433. if (link.length > 0)
  434. {
  435. var icon = null;
  436. var title = graph.getLinkTitle(link);
  437. if (docs != null && docs.length > 0)
  438. {
  439. icon = docs[0].iconUrl;
  440. title = docs[0].name || docs[0].type;
  441. title = title.charAt(0).toUpperCase() + title.substring(1);
  442. if (title.length > 30)
  443. {
  444. title = title.substring(0, 30) + '...';
  445. }
  446. }
  447. var linkCell = new mxCell(title, new mxGeometry(0, 0, 100, 40),
  448. 'fontColor=#0000EE;fontStyle=4;rounded=1;overflow=hidden;' + ((icon != null) ?
  449. 'shape=label;imageWidth=16;imageHeight=16;spacingLeft=26;align=left;image=' + icon :
  450. 'spacing=10;'));
  451. linkCell.vertex = true;
  452. var pt = graph.getCenterInsertPoint(graph.getBoundingBoxFromGeometry([linkCell], true));
  453. linkCell.geometry.x = pt.x;
  454. linkCell.geometry.y = pt.y;
  455. graph.setLinkForCell(linkCell, link);
  456. graph.cellSizeUpdated(linkCell, true);
  457. graph.getModel().beginUpdate();
  458. try
  459. {
  460. linkCell = graph.addCell(linkCell);
  461. graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [linkCell]));
  462. }
  463. finally
  464. {
  465. graph.getModel().endUpdate();
  466. }
  467. graph.setSelectionCell(linkCell);
  468. graph.scrollCellToVisible(graph.getSelectionCell());
  469. }
  470. });
  471. }
  472. })).isEnabled = isGraphEnabled;
  473. this.addAction('link...', mxUtils.bind(this, function()
  474. {
  475. if (graph.isEnabled())
  476. {
  477. if (graph.cellEditor.isContentEditing())
  478. {
  479. var elt = graph.getSelectedElement();
  480. var link = graph.getParentByName(elt, 'A', graph.cellEditor.textarea);
  481. var oldValue = '';
  482. // Workaround for FF returning the outermost selected element after double
  483. // click on a DOM hierarchy with a link inside (but not as topmost element)
  484. if (link == null && elt != null && elt.getElementsByTagName != null)
  485. {
  486. // Finds all links in the selected DOM and uses the link
  487. // where the selection text matches its text content
  488. var links = elt.getElementsByTagName('a');
  489. for (var i = 0; i < links.length && link == null; i++)
  490. {
  491. if (links[i].textContent == elt.textContent)
  492. {
  493. link = links[i];
  494. }
  495. }
  496. }
  497. if (link != null && link.nodeName == 'A')
  498. {
  499. oldValue = link.getAttribute('href') || '';
  500. graph.selectNode(link);
  501. }
  502. var selState = graph.cellEditor.saveSelection();
  503. ui.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value)
  504. {
  505. graph.cellEditor.restoreSelection(selState);
  506. if (value != null)
  507. {
  508. graph.insertLink(value);
  509. }
  510. }));
  511. }
  512. else if (graph.isSelectionEmpty())
  513. {
  514. this.get('insertLink').funct();
  515. }
  516. else
  517. {
  518. this.get('editLink').funct();
  519. }
  520. }
  521. })).isEnabled = isGraphEnabled;
  522. this.addAction('autosize', function()
  523. {
  524. var cells = graph.getSelectionCells();
  525. if (cells != null)
  526. {
  527. graph.getModel().beginUpdate();
  528. try
  529. {
  530. for (var i = 0; i < cells.length; i++)
  531. {
  532. var cell = cells[i];
  533. if (graph.getModel().getChildCount(cell))
  534. {
  535. graph.updateGroupBounds([cell], 20);
  536. }
  537. else
  538. {
  539. var state = graph.view.getState(cell);
  540. var geo = graph.getCellGeometry(cell);
  541. if (graph.getModel().isVertex(cell) && state != null && state.text != null &&
  542. geo != null && graph.isWrapping(cell))
  543. {
  544. geo = geo.clone();
  545. geo.height = state.text.boundingBox.height / graph.view.scale;
  546. graph.getModel().setGeometry(cell, geo);
  547. }
  548. else
  549. {
  550. graph.updateCellSize(cell);
  551. }
  552. }
  553. }
  554. }
  555. finally
  556. {
  557. graph.getModel().endUpdate();
  558. }
  559. }
  560. }, null, null, Editor.ctrlKey + '+Shift+Y');
  561. this.addAction('formattedText', function()
  562. {
  563. var refState = graph.getView().getState(graph.getSelectionCell());
  564. if (refState != null)
  565. {
  566. graph.stopEditing();
  567. var value = (refState.style['html'] == '1') ? null : '1';
  568. graph.getModel().beginUpdate();
  569. try
  570. {
  571. var cells = graph.getSelectionCells();
  572. for (var i = 0; i < cells.length; i++)
  573. {
  574. state = graph.getView().getState(cells[i]);
  575. if (state != null)
  576. {
  577. var html = mxUtils.getValue(state.style, 'html', '0');
  578. if (html == '1' && value == null)
  579. {
  580. var label = graph.convertValueToString(state.cell);
  581. if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0')
  582. {
  583. // Removes newlines from HTML and converts breaks to newlines
  584. // to match the HTML output in plain text
  585. label = label.replace(/\n/g, '').replace(/<br\s*.?>/g, '\n');
  586. }
  587. // Removes HTML tags
  588. var temp = document.createElement('div');
  589. temp.innerHTML = graph.sanitizeHtml(label);
  590. label = mxUtils.extractTextWithWhitespace(temp.childNodes);
  591. graph.cellLabelChanged(state.cell, label);
  592. graph.setCellStyles('html', value, [cells[i]]);
  593. }
  594. else if (html == '0' && value == '1')
  595. {
  596. // Converts HTML tags to text
  597. var label = mxUtils.htmlEntities(graph.convertValueToString(state.cell), false);
  598. if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0')
  599. {
  600. // Converts newlines in plain text to breaks in HTML
  601. // to match the plain text output
  602. label = label.replace(/\n/g, '<br/>');
  603. }
  604. graph.cellLabelChanged(state.cell, graph.sanitizeHtml(label));
  605. graph.setCellStyles('html', value, [cells[i]]);
  606. }
  607. }
  608. }
  609. ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['html'],
  610. 'values', [(value != null) ? value : '0'], 'cells', cells));
  611. }
  612. finally
  613. {
  614. graph.getModel().endUpdate();
  615. }
  616. }
  617. });
  618. this.addAction('wordWrap', function()
  619. {
  620. var state = graph.getView().getState(graph.getSelectionCell());
  621. var value = 'wrap';
  622. graph.stopEditing();
  623. if (state != null && state.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap')
  624. {
  625. value = null;
  626. }
  627. graph.setCellStyles(mxConstants.STYLE_WHITE_SPACE, value);
  628. });
  629. this.addAction('rotation', function()
  630. {
  631. var value = '0';
  632. var state = graph.getView().getState(graph.getSelectionCell());
  633. if (state != null)
  634. {
  635. value = state.style[mxConstants.STYLE_ROTATION] || value;
  636. }
  637. var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), function(newValue)
  638. {
  639. if (newValue != null && newValue.length > 0)
  640. {
  641. graph.setCellStyles(mxConstants.STYLE_ROTATION, newValue);
  642. }
  643. }, mxResources.get('enterValue') + ' (' + mxResources.get('rotation') + ' 0-360)');
  644. ui.showDialog(dlg.container, 375, 80, true, true);
  645. dlg.init();
  646. });
  647. // View actions
  648. this.addAction('resetView', function()
  649. {
  650. graph.zoomTo(1);
  651. ui.resetScrollbars();
  652. }, null, null, 'Home');
  653. this.addAction('zoomIn', function(evt)
  654. {
  655. if (graph.isFastZoomEnabled())
  656. {
  657. graph.lazyZoom(true, true, ui.buttonZoomDelay);
  658. }
  659. else
  660. {
  661. graph.zoomIn();
  662. }
  663. }, null, null, Editor.ctrlKey + ' + (Numpad) / Alt+Mousewheel');
  664. this.addAction('zoomOut', function(evt)
  665. {
  666. if (graph.isFastZoomEnabled())
  667. {
  668. graph.lazyZoom(false, true, ui.buttonZoomDelay);
  669. }
  670. else
  671. {
  672. graph.zoomOut();
  673. }
  674. }, null, null, Editor.ctrlKey + ' - (Numpad) / Alt+Mousewheel');
  675. this.addAction('fitWindow', function()
  676. {
  677. var bounds = (graph.isSelectionEmpty()) ? graph.getGraphBounds() :
  678. graph.getBoundingBox(graph.getSelectionCells())
  679. var t = graph.view.translate;
  680. var s = graph.view.scale;
  681. bounds.x = bounds.x / s - t.x;
  682. bounds.y = bounds.y / s - t.y;
  683. bounds.width /= s;
  684. bounds.height /= s;
  685. if (graph.backgroundImage != null)
  686. {
  687. bounds.add(new mxRectangle(0, 0, graph.backgroundImage.width, graph.backgroundImage.height));
  688. }
  689. if (bounds.width == 0 || bounds.height == 0)
  690. {
  691. graph.zoomTo(1);
  692. ui.resetScrollbars();
  693. }
  694. else
  695. {
  696. graph.fitWindow(bounds);
  697. }
  698. }, null, null, Editor.ctrlKey + '+Shift+H');
  699. this.addAction('fitPage', mxUtils.bind(this, function()
  700. {
  701. if (!graph.pageVisible)
  702. {
  703. this.get('pageView').funct();
  704. }
  705. var fmt = graph.pageFormat;
  706. var ps = graph.pageScale;
  707. var cw = graph.container.clientWidth - 10;
  708. var ch = graph.container.clientHeight - 10;
  709. var scale = Math.floor(20 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 20;
  710. graph.zoomTo(scale);
  711. if (mxUtils.hasScrollbars(graph.container))
  712. {
  713. var pad = graph.getPagePadding();
  714. graph.container.scrollTop = pad.y * graph.view.scale - 1;
  715. graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, (graph.container.scrollWidth - graph.container.clientWidth) / 2) - 1;
  716. }
  717. }), null, null, Editor.ctrlKey + '+J');
  718. this.addAction('fitTwoPages', mxUtils.bind(this, function()
  719. {
  720. if (!graph.pageVisible)
  721. {
  722. this.get('pageView').funct();
  723. }
  724. var fmt = graph.pageFormat;
  725. var ps = graph.pageScale;
  726. var cw = graph.container.clientWidth - 10;
  727. var ch = graph.container.clientHeight - 10;
  728. var scale = Math.floor(20 * Math.min(cw / (2 * fmt.width) / ps, ch / fmt.height / ps)) / 20;
  729. graph.zoomTo(scale);
  730. if (mxUtils.hasScrollbars(graph.container))
  731. {
  732. var pad = graph.getPagePadding();
  733. graph.container.scrollTop = Math.min(pad.y, (graph.container.scrollHeight - graph.container.clientHeight) / 2);
  734. graph.container.scrollLeft = Math.min(pad.x, (graph.container.scrollWidth - graph.container.clientWidth) / 2);
  735. }
  736. }), null, null, Editor.ctrlKey + '+Shift+J');
  737. this.addAction('fitPageWidth', mxUtils.bind(this, function()
  738. {
  739. if (!graph.pageVisible)
  740. {
  741. this.get('pageView').funct();
  742. }
  743. var fmt = graph.pageFormat;
  744. var ps = graph.pageScale;
  745. var cw = graph.container.clientWidth - 10;
  746. var scale = Math.floor(20 * cw / fmt.width / ps) / 20;
  747. graph.zoomTo(scale);
  748. if (mxUtils.hasScrollbars(graph.container))
  749. {
  750. var pad = graph.getPagePadding();
  751. graph.container.scrollLeft = Math.min(pad.x * graph.view.scale,
  752. (graph.container.scrollWidth - graph.container.clientWidth) / 2);
  753. }
  754. }));
  755. this.put('customZoom', new Action(mxResources.get('custom') + '...', mxUtils.bind(this, function()
  756. {
  757. var dlg = new FilenameDialog(this.editorUi, parseInt(graph.getView().getScale() * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue)
  758. {
  759. var val = parseInt(newValue);
  760. if (!isNaN(val) && val > 0)
  761. {
  762. graph.zoomTo(val / 100);
  763. }
  764. }), mxResources.get('zoom') + ' (%)');
  765. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  766. dlg.init();
  767. }), null, null, Editor.ctrlKey + '+0'));
  768. this.addAction('pageScale...', mxUtils.bind(this, function()
  769. {
  770. var dlg = new FilenameDialog(this.editorUi, parseInt(graph.pageScale * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue)
  771. {
  772. var val = parseInt(newValue);
  773. if (!isNaN(val) && val > 0)
  774. {
  775. var change = new ChangePageSetup(ui, null, null, null, val / 100);
  776. change.ignoreColor = true;
  777. change.ignoreImage = true;
  778. graph.model.execute(change);
  779. }
  780. }), mxResources.get('pageScale') + ' (%)');
  781. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  782. dlg.init();
  783. }));
  784. // Option actions
  785. var action = null;
  786. action = this.addAction('grid', function()
  787. {
  788. graph.setGridEnabled(!graph.isGridEnabled());
  789. ui.fireEvent(new mxEventObject('gridEnabledChanged'));
  790. }, null, null, Editor.ctrlKey + '+Shift+G');
  791. action.setToggleAction(true);
  792. action.setSelectedCallback(function() { return graph.isGridEnabled(); });
  793. action.setEnabled(false);
  794. action = this.addAction('guides', function()
  795. {
  796. graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled;
  797. ui.fireEvent(new mxEventObject('guidesEnabledChanged'));
  798. });
  799. action.setToggleAction(true);
  800. action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; });
  801. action.setEnabled(false);
  802. action = this.addAction('tooltips', function()
  803. {
  804. graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled());
  805. ui.fireEvent(new mxEventObject('tooltipsEnabledChanged'));
  806. });
  807. action.setToggleAction(true);
  808. action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); });
  809. action = this.addAction('collapseExpand', function()
  810. {
  811. var change = new ChangePageSetup(ui);
  812. change.ignoreColor = true;
  813. change.ignoreImage = true;
  814. change.foldingEnabled = !graph.foldingEnabled;
  815. graph.model.execute(change);
  816. });
  817. action.setToggleAction(true);
  818. action.setSelectedCallback(function() { return graph.foldingEnabled; });
  819. action.isEnabled = isGraphEnabled;
  820. action = this.addAction('scrollbars', function()
  821. {
  822. ui.setScrollbars(!ui.hasScrollbars());
  823. });
  824. action.setToggleAction(true);
  825. action.setSelectedCallback(function() { return graph.scrollbars; });
  826. action = this.addAction('pageView', mxUtils.bind(this, function()
  827. {
  828. ui.setPageVisible(!graph.pageVisible);
  829. }));
  830. action.setToggleAction(true);
  831. action.setSelectedCallback(function() { return graph.pageVisible; });
  832. action = this.addAction('connectionArrows', function()
  833. {
  834. graph.connectionArrowsEnabled = !graph.connectionArrowsEnabled;
  835. ui.fireEvent(new mxEventObject('connectionArrowsChanged'));
  836. }, null, null, 'Alt+Shift+A');
  837. action.setToggleAction(true);
  838. action.setSelectedCallback(function() { return graph.connectionArrowsEnabled; });
  839. action = this.addAction('connectionPoints', function()
  840. {
  841. graph.setConnectable(!graph.connectionHandler.isEnabled());
  842. ui.fireEvent(new mxEventObject('connectionPointsChanged'));
  843. }, null, null, 'Alt+Shift+P');
  844. action.setToggleAction(true);
  845. action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); });
  846. action = this.addAction('copyConnect', function()
  847. {
  848. graph.connectionHandler.setCreateTarget(!graph.connectionHandler.isCreateTarget());
  849. ui.fireEvent(new mxEventObject('copyConnectChanged'));
  850. });
  851. action.setToggleAction(true);
  852. action.setSelectedCallback(function() { return graph.connectionHandler.isCreateTarget(); });
  853. action.isEnabled = isGraphEnabled;
  854. action = this.addAction('autosave', function()
  855. {
  856. ui.editor.setAutosave(!ui.editor.autosave);
  857. });
  858. action.setToggleAction(true);
  859. action.setSelectedCallback(function() { return ui.editor.autosave; });
  860. action.isEnabled = isGraphEnabled;
  861. action.visible = false;
  862. // Help actions
  863. this.addAction('help', function()
  864. {
  865. var ext = '';
  866. if (mxResources.isLanguageSupported(mxClient.language))
  867. {
  868. ext = '_' + mxClient.language;
  869. }
  870. graph.openLink(RESOURCES_PATH + '/help' + ext + '.html');
  871. });
  872. var showingAbout = false;
  873. this.put('about', new Action(mxResources.get('about') + ' Graph Editor...', function()
  874. {
  875. if (!showingAbout)
  876. {
  877. ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true, function()
  878. {
  879. showingAbout = false;
  880. });
  881. showingAbout = true;
  882. }
  883. }));
  884. // Font style actions
  885. var toggleFontStyle = mxUtils.bind(this, function(key, style, fn, shortcut)
  886. {
  887. return this.addAction(key, function()
  888. {
  889. if (fn != null && graph.cellEditor.isContentEditing())
  890. {
  891. fn();
  892. }
  893. else
  894. {
  895. graph.stopEditing(false);
  896. graph.getModel().beginUpdate();
  897. try
  898. {
  899. var cells = graph.getSelectionCells();
  900. graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style, cells);
  901. // Removes bold and italic tags and CSS styles inside labels
  902. if ((style & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
  903. {
  904. graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  905. {
  906. elt.style.fontWeight = null;
  907. if (elt.nodeName == 'B')
  908. {
  909. graph.replaceElement(elt);
  910. }
  911. });
  912. }
  913. else if ((style & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
  914. {
  915. graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  916. {
  917. elt.style.fontStyle = null;
  918. if (elt.nodeName == 'I')
  919. {
  920. graph.replaceElement(elt);
  921. }
  922. });
  923. }
  924. else if ((style & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
  925. {
  926. graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  927. {
  928. elt.style.textDecoration = null;
  929. if (elt.nodeName == 'U')
  930. {
  931. graph.replaceElement(elt);
  932. }
  933. });
  934. }
  935. for (var i = 0; i < cells.length; i++)
  936. {
  937. if (graph.model.getChildCount(cells[i]) == 0)
  938. {
  939. graph.autoSizeCell(cells[i], false);
  940. }
  941. }
  942. }
  943. finally
  944. {
  945. graph.getModel().endUpdate();
  946. }
  947. }
  948. }, null, null, shortcut);
  949. });
  950. toggleFontStyle('bold', mxConstants.FONT_BOLD, function() { document.execCommand('bold', false, null); }, Editor.ctrlKey + '+B');
  951. toggleFontStyle('italic', mxConstants.FONT_ITALIC, function() { document.execCommand('italic', false, null); }, Editor.ctrlKey + '+I');
  952. toggleFontStyle('underline', mxConstants.FONT_UNDERLINE, function() { document.execCommand('underline', false, null); }, Editor.ctrlKey + '+U');
  953. // Color actions
  954. this.addAction('fontColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR, 'forecolor', '000000'); });
  955. this.addAction('strokeColor...', function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); });
  956. this.addAction('fillColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); });
  957. this.addAction('gradientColor...', function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); });
  958. this.addAction('backgroundColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'backcolor'); });
  959. this.addAction('borderColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); });
  960. // Format actions
  961. this.addAction('vertical', function() { ui.menus.toggleStyle(mxConstants.STYLE_HORIZONTAL, true); });
  962. this.addAction('shadow', function() { ui.menus.toggleStyle(mxConstants.STYLE_SHADOW); });
  963. this.addAction('solid', function()
  964. {
  965. graph.getModel().beginUpdate();
  966. try
  967. {
  968. graph.setCellStyles(mxConstants.STYLE_DASHED, null);
  969. graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null);
  970. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  971. 'values', [null, null], 'cells', graph.getSelectionCells()));
  972. }
  973. finally
  974. {
  975. graph.getModel().endUpdate();
  976. }
  977. });
  978. this.addAction('dashed', function()
  979. {
  980. graph.getModel().beginUpdate();
  981. try
  982. {
  983. graph.setCellStyles(mxConstants.STYLE_DASHED, '1');
  984. graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null);
  985. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  986. 'values', ['1', null], 'cells', graph.getSelectionCells()));
  987. }
  988. finally
  989. {
  990. graph.getModel().endUpdate();
  991. }
  992. });
  993. this.addAction('dotted', function()
  994. {
  995. graph.getModel().beginUpdate();
  996. try
  997. {
  998. graph.setCellStyles(mxConstants.STYLE_DASHED, '1');
  999. graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, '1 4');
  1000. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN],
  1001. 'values', ['1', '1 4'], 'cells', graph.getSelectionCells()));
  1002. }
  1003. finally
  1004. {
  1005. graph.getModel().endUpdate();
  1006. }
  1007. });
  1008. this.addAction('sharp', function()
  1009. {
  1010. graph.getModel().beginUpdate();
  1011. try
  1012. {
  1013. graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0');
  1014. graph.setCellStyles(mxConstants.STYLE_CURVED, '0');
  1015. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED],
  1016. 'values', ['0', '0'], 'cells', graph.getSelectionCells()));
  1017. }
  1018. finally
  1019. {
  1020. graph.getModel().endUpdate();
  1021. }
  1022. });
  1023. this.addAction('rounded', function()
  1024. {
  1025. graph.getModel().beginUpdate();
  1026. try
  1027. {
  1028. graph.setCellStyles(mxConstants.STYLE_ROUNDED, '1');
  1029. graph.setCellStyles(mxConstants.STYLE_CURVED, '0');
  1030. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED],
  1031. 'values', ['1', '0'], 'cells', graph.getSelectionCells()));
  1032. }
  1033. finally
  1034. {
  1035. graph.getModel().endUpdate();
  1036. }
  1037. });
  1038. this.addAction('toggleRounded', function()
  1039. {
  1040. if (!graph.isSelectionEmpty() && graph.isEnabled())
  1041. {
  1042. graph.getModel().beginUpdate();
  1043. try
  1044. {
  1045. var cells = graph.getSelectionCells();
  1046. var style = graph.getCurrentCellStyle(cells[0]);
  1047. var value = (mxUtils.getValue(style, mxConstants.STYLE_ROUNDED, '0') == '1') ? '0' : '1';
  1048. graph.setCellStyles(mxConstants.STYLE_ROUNDED, value);
  1049. graph.setCellStyles(mxConstants.STYLE_CURVED, null);
  1050. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED],
  1051. 'values', [value, '0'], 'cells', graph.getSelectionCells()));
  1052. }
  1053. finally
  1054. {
  1055. graph.getModel().endUpdate();
  1056. }
  1057. }
  1058. });
  1059. this.addAction('curved', function()
  1060. {
  1061. graph.getModel().beginUpdate();
  1062. try
  1063. {
  1064. graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0');
  1065. graph.setCellStyles(mxConstants.STYLE_CURVED, '1');
  1066. ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED],
  1067. 'values', ['0', '1'], 'cells', graph.getSelectionCells()));
  1068. }
  1069. finally
  1070. {
  1071. graph.getModel().endUpdate();
  1072. }
  1073. });
  1074. this.addAction('collapsible', function()
  1075. {
  1076. var state = graph.view.getState(graph.getSelectionCell());
  1077. var value = '1';
  1078. if (state != null && graph.getFoldingImage(state) != null)
  1079. {
  1080. value = '0';
  1081. }
  1082. graph.setCellStyles('collapsible', value);
  1083. ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['collapsible'],
  1084. 'values', [value], 'cells', graph.getSelectionCells()));
  1085. });
  1086. this.addAction('editStyle...', mxUtils.bind(this, function()
  1087. {
  1088. var cells = graph.getSelectionCells();
  1089. if (cells != null && cells.length > 0)
  1090. {
  1091. var model = graph.getModel();
  1092. var dlg = new TextareaDialog(this.editorUi, mxResources.get('editStyle') + ':',
  1093. model.getStyle(cells[0]) || '', function(newValue)
  1094. {
  1095. if (newValue != null)
  1096. {
  1097. graph.setCellStyle(mxUtils.trim(newValue), cells);
  1098. }
  1099. }, null, null, 400, 220);
  1100. this.editorUi.showDialog(dlg.container, 420, 300, true, true);
  1101. dlg.init();
  1102. }
  1103. }), null, null, Editor.ctrlKey + '+E');
  1104. this.addAction('setAsDefaultStyle', function()
  1105. {
  1106. if (graph.isEnabled() && !graph.isSelectionEmpty())
  1107. {
  1108. ui.setDefaultStyle(graph.getSelectionCell());
  1109. }
  1110. }, null, null, Editor.ctrlKey + '+Shift+D');
  1111. this.addAction('clearDefaultStyle', function()
  1112. {
  1113. if (graph.isEnabled())
  1114. {
  1115. ui.clearDefaultStyle();
  1116. }
  1117. }, null, null, Editor.ctrlKey + '+Shift+R');
  1118. this.addAction('addWaypoint', function()
  1119. {
  1120. var cell = graph.getSelectionCell();
  1121. if (cell != null && graph.getModel().isEdge(cell))
  1122. {
  1123. var handler = editor.graph.selectionCellsHandler.getHandler(cell);
  1124. if (handler instanceof mxEdgeHandler)
  1125. {
  1126. var t = graph.view.translate;
  1127. var s = graph.view.scale;
  1128. var dx = t.x;
  1129. var dy = t.y;
  1130. var parent = graph.getModel().getParent(cell);
  1131. var pgeo = graph.getCellGeometry(parent);
  1132. while (graph.getModel().isVertex(parent) && pgeo != null)
  1133. {
  1134. dx += pgeo.x;
  1135. dy += pgeo.y;
  1136. parent = graph.getModel().getParent(parent);
  1137. pgeo = graph.getCellGeometry(parent);
  1138. }
  1139. var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx));
  1140. var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy));
  1141. handler.addPointAt(handler.state, x, y);
  1142. }
  1143. }
  1144. });
  1145. this.addAction('removeWaypoint', function()
  1146. {
  1147. // TODO: Action should run with "this" set to action
  1148. var rmWaypointAction = ui.actions.get('removeWaypoint');
  1149. if (rmWaypointAction.handler != null)
  1150. {
  1151. // NOTE: Popupevent handled and action updated in Menus.createPopupMenu
  1152. rmWaypointAction.handler.removePoint(rmWaypointAction.handler.state, rmWaypointAction.index);
  1153. }
  1154. });
  1155. this.addAction('clearWaypoints', function()
  1156. {
  1157. var cells = graph.getSelectionCells();
  1158. if (cells != null)
  1159. {
  1160. cells = graph.addAllEdges(cells);
  1161. graph.getModel().beginUpdate();
  1162. try
  1163. {
  1164. for (var i = 0; i < cells.length; i++)
  1165. {
  1166. var cell = cells[i];
  1167. if (graph.getModel().isEdge(cell))
  1168. {
  1169. var geo = graph.getCellGeometry(cell);
  1170. if (geo != null)
  1171. {
  1172. geo = geo.clone();
  1173. geo.points = null;
  1174. graph.getModel().setGeometry(cell, geo);
  1175. }
  1176. }
  1177. }
  1178. }
  1179. finally
  1180. {
  1181. graph.getModel().endUpdate();
  1182. }
  1183. }
  1184. }, null, null, 'Alt+Shift+C');
  1185. action = this.addAction('subscript', mxUtils.bind(this, function()
  1186. {
  1187. if (graph.cellEditor.isContentEditing())
  1188. {
  1189. document.execCommand('subscript', false, null);
  1190. }
  1191. }), null, null, Editor.ctrlKey + '+,');
  1192. action = this.addAction('superscript', mxUtils.bind(this, function()
  1193. {
  1194. if (graph.cellEditor.isContentEditing())
  1195. {
  1196. document.execCommand('superscript', false, null);
  1197. }
  1198. }), null, null, Editor.ctrlKey + '+.');
  1199. action = this.addAction('indent', mxUtils.bind(this, function()
  1200. {
  1201. // NOTE: Alt+Tab for outdent implemented via special code in
  1202. // keyHandler.getFunction in EditorUi.js. Ctrl+Tab is reserved.
  1203. if (graph.cellEditor.isContentEditing())
  1204. {
  1205. document.execCommand('indent', false, null);
  1206. }
  1207. }), null, null, 'Shift+Tab');
  1208. this.addAction('image...', function()
  1209. {
  1210. if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))
  1211. {
  1212. var title = mxResources.get('image') + ' (' + mxResources.get('url') + '):';
  1213. var state = graph.getView().getState(graph.getSelectionCell());
  1214. var value = '';
  1215. if (state != null)
  1216. {
  1217. value = state.style[mxConstants.STYLE_IMAGE] || value;
  1218. }
  1219. var selectionState = graph.cellEditor.saveSelection();
  1220. ui.showImageDialog(title, value, function(newValue, w, h)
  1221. {
  1222. // Inserts image into HTML text
  1223. if (graph.cellEditor.isContentEditing())
  1224. {
  1225. graph.cellEditor.restoreSelection(selectionState);
  1226. graph.insertImage(newValue, w, h);
  1227. }
  1228. else
  1229. {
  1230. var cells = graph.getSelectionCells();
  1231. if (newValue != null && (newValue.length > 0 || cells.length > 0))
  1232. {
  1233. var select = null;
  1234. graph.getModel().beginUpdate();
  1235. try
  1236. {
  1237. // Inserts new cell if no cell is selected
  1238. if (cells.length == 0)
  1239. {
  1240. cells = [graph.insertVertex(graph.getDefaultParent(), null, '', 0, 0, w, h,
  1241. 'shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;')];
  1242. var pt = graph.getCenterInsertPoint(graph.getBoundingBoxFromGeometry(cells, true));
  1243. cells[0].geometry.x = pt.x;
  1244. cells[0].geometry.y = pt.y;
  1245. select = cells;
  1246. graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select));
  1247. }
  1248. graph.setCellStyles(mxConstants.STYLE_IMAGE, (newValue.length > 0) ? newValue : null, cells);
  1249. // Sets shape only if not already shape with image (label or image)
  1250. var style = graph.getCurrentCellStyle(cells[0]);
  1251. if (style[mxConstants.STYLE_SHAPE] != 'image' && style[mxConstants.STYLE_SHAPE] != 'label')
  1252. {
  1253. graph.setCellStyles(mxConstants.STYLE_SHAPE, 'image', cells);
  1254. }
  1255. else if (newValue.length == 0)
  1256. {
  1257. graph.setCellStyles(mxConstants.STYLE_SHAPE, null, cells);
  1258. }
  1259. if (graph.getSelectionCount() == 1)
  1260. {
  1261. if (w != null && h != null)
  1262. {
  1263. var cell = cells[0];
  1264. var geo = graph.getModel().getGeometry(cell);
  1265. if (geo != null)
  1266. {
  1267. geo = geo.clone();
  1268. geo.width = w;
  1269. geo.height = h;
  1270. graph.getModel().setGeometry(cell, geo);
  1271. }
  1272. }
  1273. }
  1274. }
  1275. finally
  1276. {
  1277. graph.getModel().endUpdate();
  1278. }
  1279. if (select != null)
  1280. {
  1281. graph.setSelectionCells(select);
  1282. graph.scrollCellToVisible(select[0]);
  1283. }
  1284. }
  1285. }
  1286. }, graph.cellEditor.isContentEditing(), !graph.cellEditor.isContentEditing());
  1287. }
  1288. }).isEnabled = isGraphEnabled;
  1289. action = this.addAction('layers', mxUtils.bind(this, function()
  1290. {
  1291. if (this.layersWindow == null)
  1292. {
  1293. // LATER: Check outline window for initial placement
  1294. this.layersWindow = new LayersWindow(ui, document.body.offsetWidth - 280, 120, 220, 196);
  1295. this.layersWindow.window.addListener('show', function()
  1296. {
  1297. ui.fireEvent(new mxEventObject('layers'));
  1298. });
  1299. this.layersWindow.window.addListener('hide', function()
  1300. {
  1301. ui.fireEvent(new mxEventObject('layers'));
  1302. });
  1303. this.layersWindow.window.setVisible(true);
  1304. ui.fireEvent(new mxEventObject('layers'));
  1305. this.layersWindow.init();
  1306. }
  1307. else
  1308. {
  1309. this.layersWindow.window.setVisible(!this.layersWindow.window.isVisible());
  1310. }
  1311. }), null, null, Editor.ctrlKey + '+Shift+L');
  1312. action.setToggleAction(true);
  1313. action.setSelectedCallback(mxUtils.bind(this, function() { return this.layersWindow != null && this.layersWindow.window.isVisible(); }));
  1314. action = this.addAction('formatPanel', mxUtils.bind(this, function()
  1315. {
  1316. ui.toggleFormatPanel();
  1317. }), null, null, Editor.ctrlKey + '+Shift+P');
  1318. action.setToggleAction(true);
  1319. action.setSelectedCallback(mxUtils.bind(this, function() { return ui.formatWidth > 0; }));
  1320. action = this.addAction('outline', mxUtils.bind(this, function()
  1321. {
  1322. if (this.outlineWindow == null)
  1323. {
  1324. // LATER: Check layers window for initial placement
  1325. this.outlineWindow = new OutlineWindow(ui, document.body.offsetWidth - 260, 100, 180, 180);
  1326. this.outlineWindow.window.addListener('show', function()
  1327. {
  1328. ui.fireEvent(new mxEventObject('outline'));
  1329. });
  1330. this.outlineWindow.window.addListener('hide', function()
  1331. {
  1332. ui.fireEvent(new mxEventObject('outline'));
  1333. });
  1334. this.outlineWindow.window.setVisible(true);
  1335. ui.fireEvent(new mxEventObject('outline'));
  1336. }
  1337. else
  1338. {
  1339. this.outlineWindow.window.setVisible(!this.outlineWindow.window.isVisible());
  1340. }
  1341. }), null, null, Editor.ctrlKey + '+Shift+O');
  1342. action.setToggleAction(true);
  1343. action.setSelectedCallback(mxUtils.bind(this, function() { return this.outlineWindow != null && this.outlineWindow.window.isVisible(); }));
  1344. };
  1345. /**
  1346. * Registers the given action under the given name.
  1347. */
  1348. Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut)
  1349. {
  1350. var title;
  1351. if (key.substring(key.length - 3) == '...')
  1352. {
  1353. key = key.substring(0, key.length - 3);
  1354. title = mxResources.get(key) + '...';
  1355. }
  1356. else
  1357. {
  1358. title = mxResources.get(key);
  1359. }
  1360. return this.put(key, new Action(title, funct, enabled, iconCls, shortcut));
  1361. };
  1362. /**
  1363. * Registers the given action under the given name.
  1364. */
  1365. Actions.prototype.put = function(name, action)
  1366. {
  1367. this.actions[name] = action;
  1368. return action;
  1369. };
  1370. /**
  1371. * Returns the action for the given name or null if no such action exists.
  1372. */
  1373. Actions.prototype.get = function(name)
  1374. {
  1375. return this.actions[name];
  1376. };
  1377. /**
  1378. * Constructs a new action for the given parameters.
  1379. */
  1380. function Action(label, funct, enabled, iconCls, shortcut)
  1381. {
  1382. mxEventSource.call(this);
  1383. this.label = label;
  1384. this.funct = this.createFunction(funct);
  1385. this.enabled = (enabled != null) ? enabled : true;
  1386. this.iconCls = iconCls;
  1387. this.shortcut = shortcut;
  1388. this.visible = true;
  1389. };
  1390. // Action inherits from mxEventSource
  1391. mxUtils.extend(Action, mxEventSource);
  1392. /**
  1393. * Sets the enabled state of the action and fires a stateChanged event.
  1394. */
  1395. Action.prototype.createFunction = function(funct)
  1396. {
  1397. return funct;
  1398. };
  1399. /**
  1400. * Sets the enabled state of the action and fires a stateChanged event.
  1401. */
  1402. Action.prototype.setEnabled = function(value)
  1403. {
  1404. if (this.enabled != value)
  1405. {
  1406. this.enabled = value;
  1407. this.fireEvent(new mxEventObject('stateChanged'));
  1408. }
  1409. };
  1410. /**
  1411. * Sets the enabled state of the action and fires a stateChanged event.
  1412. */
  1413. Action.prototype.isEnabled = function()
  1414. {
  1415. return this.enabled;
  1416. };
  1417. /**
  1418. * Sets the enabled state of the action and fires a stateChanged event.
  1419. */
  1420. Action.prototype.setToggleAction = function(value)
  1421. {
  1422. this.toggleAction = value;
  1423. };
  1424. /**
  1425. * Sets the enabled state of the action and fires a stateChanged event.
  1426. */
  1427. Action.prototype.setSelectedCallback = function(funct)
  1428. {
  1429. this.selectedCallback = funct;
  1430. };
  1431. /**
  1432. * Sets the enabled state of the action and fires a stateChanged event.
  1433. */
  1434. Action.prototype.isSelected = function()
  1435. {
  1436. return this.selectedCallback();
  1437. };