Actions.js 43 KB

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