Menus.js 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462
  1. /**
  2. * Copyright (c) 2006-2012, JGraph Ltd
  3. */
  4. /**
  5. * Constructs a new graph editor
  6. */
  7. Menus = function(editorUi)
  8. {
  9. this.editorUi = editorUi;
  10. this.menus = new Object();
  11. this.init();
  12. // Pre-fetches checkmark image
  13. if (!mxClient.IS_SVG)
  14. {
  15. new Image().src = this.checkmarkImage;
  16. }
  17. };
  18. /**
  19. * Sets the default font family.
  20. */
  21. Menus.prototype.defaultFont = 'Helvetica';
  22. /**
  23. * Sets the default font size.
  24. */
  25. Menus.prototype.defaultFontSize = '12';
  26. /**
  27. * Sets the default font size.
  28. */
  29. Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras', 'help'];
  30. /**
  31. * Adds the label menu items to the given menu and parent.
  32. */
  33. Menus.prototype.defaultFonts = ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond', 'Comic Sans MS',
  34. 'Courier New', 'Georgia', 'Lucida Console', 'Tahoma'];
  35. /**
  36. * Adds the label menu items to the given menu and parent.
  37. */
  38. Menus.prototype.init = function()
  39. {
  40. var graph = this.editorUi.editor.graph;
  41. var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled);
  42. this.customFonts = [];
  43. this.customFontSizes = [];
  44. this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent)
  45. {
  46. var addItem = mxUtils.bind(this, function(fontname)
  47. {
  48. var tr = this.styleChange(menu, fontname, [mxConstants.STYLE_FONTFAMILY], [fontname], null, parent, function()
  49. {
  50. document.execCommand('fontname', false, fontname);
  51. }, function()
  52. {
  53. graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  54. {
  55. elt.removeAttribute('face');
  56. elt.style.fontFamily = null;
  57. if (elt.nodeName == 'PRE')
  58. {
  59. graph.replaceElement(elt, 'div');
  60. }
  61. });
  62. });
  63. tr.firstChild.nextSibling.style.fontFamily = fontname;
  64. });
  65. for (var i = 0; i < this.defaultFonts.length; i++)
  66. {
  67. addItem(this.defaultFonts[i]);
  68. }
  69. menu.addSeparator(parent);
  70. if (this.customFonts.length > 0)
  71. {
  72. for (var i = 0; i < this.customFonts.length; i++)
  73. {
  74. addItem(this.customFonts[i]);
  75. }
  76. menu.addSeparator(parent);
  77. menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  78. {
  79. this.customFonts = [];
  80. this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  81. }), parent);
  82. menu.addSeparator(parent);
  83. }
  84. this.promptChange(menu, mxResources.get('custom') + '...', '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent, true, mxUtils.bind(this, function(newValue)
  85. {
  86. if (mxUtils.indexOf(this.customFonts, newValue) < 0)
  87. {
  88. this.customFonts.push(newValue);
  89. this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  90. }
  91. }));
  92. })));
  93. this.put('formatBlock', new Menu(mxUtils.bind(this, function(menu, parent)
  94. {
  95. function addItem(label, tag)
  96. {
  97. return menu.addItem(label, null, mxUtils.bind(this, function()
  98. {
  99. // TODO: Check if visible
  100. if (graph.cellEditor.textarea != null)
  101. {
  102. graph.cellEditor.textarea.focus();
  103. document.execCommand('formatBlock', false, '<' + tag + '>');
  104. }
  105. }), parent);
  106. };
  107. addItem(mxResources.get('normal'), 'p');
  108. addItem('', 'h1').firstChild.nextSibling.innerHTML = '<h1 style="margin:0px;">' + mxResources.get('heading') + ' 1</h1>';
  109. addItem('', 'h2').firstChild.nextSibling.innerHTML = '<h2 style="margin:0px;">' + mxResources.get('heading') + ' 2</h2>';
  110. addItem('', 'h3').firstChild.nextSibling.innerHTML = '<h3 style="margin:0px;">' + mxResources.get('heading') + ' 3</h3>';
  111. addItem('', 'h4').firstChild.nextSibling.innerHTML = '<h4 style="margin:0px;">' + mxResources.get('heading') + ' 4</h4>';
  112. addItem('', 'h5').firstChild.nextSibling.innerHTML = '<h5 style="margin:0px;">' + mxResources.get('heading') + ' 5</h5>';
  113. addItem('', 'h6').firstChild.nextSibling.innerHTML = '<h6 style="margin:0px;">' + mxResources.get('heading') + ' 6</h6>';
  114. addItem('', 'pre').firstChild.nextSibling.innerHTML = '<pre style="margin:0px;">' + mxResources.get('formatted') + '</pre>';
  115. addItem('', 'blockquote').firstChild.nextSibling.innerHTML = '<blockquote style="margin-top:0px;margin-bottom:0px;">' + mxResources.get('blockquote') + '</blockquote>';
  116. })));
  117. this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent)
  118. {
  119. var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72];
  120. var addItem = mxUtils.bind(this, function(fontsize)
  121. {
  122. this.styleChange(menu, fontsize, [mxConstants.STYLE_FONTSIZE], [fontsize], null, parent, function()
  123. {
  124. if (graph.cellEditor.textarea != null)
  125. {
  126. // Creates an element with arbitrary size 3
  127. document.execCommand('fontSize', false, '3');
  128. // Changes the css font size of the first font element inside the in-place editor with size 3
  129. // hopefully the above element that we've just created. LATER: Check for new element using
  130. // previous result of getElementsByTagName (see other actions)
  131. var elts = graph.cellEditor.textarea.getElementsByTagName('font');
  132. for (var i = 0; i < elts.length; i++)
  133. {
  134. if (elts[i].getAttribute('size') == '3')
  135. {
  136. elts[i].removeAttribute('size');
  137. elts[i].style.fontSize = fontsize + 'px';
  138. break;
  139. }
  140. }
  141. }
  142. });
  143. });
  144. for (var i = 0; i < sizes.length; i++)
  145. {
  146. addItem(sizes[i]);
  147. }
  148. menu.addSeparator(parent);
  149. if (this.customFontSizes.length > 0)
  150. {
  151. for (var i = 0; i < this.customFontSizes.length; i++)
  152. {
  153. addItem(this.customFontSizes[i]);
  154. }
  155. menu.addSeparator(parent);
  156. menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  157. {
  158. this.customFontSizes = [];
  159. }), parent);
  160. menu.addSeparator(parent);
  161. }
  162. this.promptChange(menu, mxResources.get('custom') + '...', '(pt)', '12', mxConstants.STYLE_FONTSIZE, parent, true, mxUtils.bind(this, function(newValue)
  163. {
  164. this.customFontSizes.push(newValue);
  165. }));
  166. })));
  167. this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent)
  168. {
  169. menu.addItem(mxResources.get('flipH'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); }, parent);
  170. menu.addItem(mxResources.get('flipV'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); }, parent);
  171. this.addMenuItems(menu, ['-', 'rotation'], parent);
  172. })));
  173. this.put('align', new Menu(mxUtils.bind(this, function(menu, parent)
  174. {
  175. menu.addItem(mxResources.get('leftAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, parent);
  176. menu.addItem(mxResources.get('center'), null, function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, parent);
  177. menu.addItem(mxResources.get('rightAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, parent);
  178. menu.addSeparator(parent);
  179. menu.addItem(mxResources.get('topAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_TOP); }, parent);
  180. menu.addItem(mxResources.get('middle'), null, function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, parent);
  181. menu.addItem(mxResources.get('bottomAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, parent);
  182. })));
  183. this.put('distribute', new Menu(mxUtils.bind(this, function(menu, parent)
  184. {
  185. menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true); }, parent);
  186. menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false); }, parent);
  187. })));
  188. this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent)
  189. {
  190. var promptSpacing = mxUtils.bind(this, function(defaultValue, fn)
  191. {
  192. var dlg = new FilenameDialog(this.editorUi, defaultValue, mxResources.get('apply'), function(newValue)
  193. {
  194. fn(parseFloat(newValue));
  195. }, mxResources.get('spacing'));
  196. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  197. dlg.init();
  198. });
  199. menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function()
  200. {
  201. var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST);
  202. this.editorUi.executeLayout(function()
  203. {
  204. var selectionCells = graph.getSelectionCells();
  205. layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
  206. }, true);
  207. }), parent);
  208. menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function()
  209. {
  210. var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH);
  211. this.editorUi.executeLayout(function()
  212. {
  213. var selectionCells = graph.getSelectionCells();
  214. layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
  215. }, true);
  216. }), parent);
  217. menu.addSeparator(parent);
  218. menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function()
  219. {
  220. var tmp = graph.getSelectionCell();
  221. var roots = null;
  222. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  223. {
  224. if (graph.getModel().getEdgeCount(tmp) == 0)
  225. {
  226. roots = graph.findTreeRoots(graph.getDefaultParent());
  227. }
  228. }
  229. else
  230. {
  231. roots = graph.findTreeRoots(tmp);
  232. }
  233. if (roots != null && roots.length > 0)
  234. {
  235. tmp = roots[0];
  236. }
  237. if (tmp != null)
  238. {
  239. var layout = new mxCompactTreeLayout(graph, true);
  240. layout.edgeRouting = false;
  241. layout.levelDistance = 30;
  242. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  243. {
  244. layout.levelDistance = newValue;
  245. this.editorUi.executeLayout(function()
  246. {
  247. layout.execute(graph.getDefaultParent(), tmp);
  248. }, true);
  249. }));
  250. }
  251. }), parent);
  252. menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function()
  253. {
  254. var tmp = graph.getSelectionCell();
  255. var roots = null;
  256. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  257. {
  258. if (graph.getModel().getEdgeCount(tmp) == 0)
  259. {
  260. roots = graph.findTreeRoots(graph.getDefaultParent());
  261. }
  262. }
  263. else
  264. {
  265. roots = graph.findTreeRoots(tmp);
  266. }
  267. if (roots != null && roots.length > 0)
  268. {
  269. tmp = roots[0];
  270. }
  271. if (tmp != null)
  272. {
  273. var layout = new mxCompactTreeLayout(graph, false);
  274. layout.edgeRouting = false;
  275. layout.levelDistance = 30;
  276. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  277. {
  278. layout.levelDistance = newValue;
  279. this.editorUi.executeLayout(function()
  280. {
  281. layout.execute(graph.getDefaultParent(), tmp);
  282. }, true);
  283. }));
  284. }
  285. }), parent);
  286. menu.addItem(mxResources.get('radialTree'), null, mxUtils.bind(this, function()
  287. {
  288. var tmp = graph.getSelectionCell();
  289. var roots = null;
  290. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  291. {
  292. if (graph.getModel().getEdgeCount(tmp) == 0)
  293. {
  294. roots = graph.findTreeRoots(graph.getDefaultParent());
  295. }
  296. }
  297. else
  298. {
  299. roots = graph.findTreeRoots(tmp);
  300. }
  301. if (roots != null && roots.length > 0)
  302. {
  303. tmp = roots[0];
  304. }
  305. if (tmp != null)
  306. {
  307. var layout = new mxRadialTreeLayout(graph, false);
  308. layout.levelDistance = 80;
  309. layout.autoRadius = true;
  310. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue)
  311. {
  312. layout.levelDistance = newValue;
  313. this.editorUi.executeLayout(function()
  314. {
  315. layout.execute(graph.getDefaultParent(), tmp);
  316. if (!graph.isSelectionEmpty())
  317. {
  318. tmp = graph.getModel().getParent(tmp);
  319. if (graph.getModel().isVertex(tmp))
  320. {
  321. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  322. }
  323. }
  324. }, true);
  325. }));
  326. }
  327. }), parent);
  328. menu.addSeparator(parent);
  329. menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function()
  330. {
  331. var layout = new mxFastOrganicLayout(graph);
  332. promptSpacing(layout.forceConstant, mxUtils.bind(this, function(newValue)
  333. {
  334. layout.forceConstant = newValue;
  335. this.editorUi.executeLayout(function()
  336. {
  337. var tmp = graph.getSelectionCell();
  338. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  339. {
  340. tmp = graph.getDefaultParent();
  341. }
  342. layout.execute(tmp);
  343. if (graph.getModel().isVertex(tmp))
  344. {
  345. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  346. }
  347. }, true);
  348. }));
  349. }), parent);
  350. menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function()
  351. {
  352. var layout = new mxCircleLayout(graph);
  353. this.editorUi.executeLayout(function()
  354. {
  355. var tmp = graph.getSelectionCell();
  356. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  357. {
  358. tmp = graph.getDefaultParent();
  359. }
  360. layout.execute(tmp);
  361. if (graph.getModel().isVertex(tmp))
  362. {
  363. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  364. }
  365. }, true);
  366. }), parent);
  367. })));
  368. this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent)
  369. {
  370. this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse', '-', 'collapsible'], parent);
  371. })));
  372. this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent)
  373. {
  374. this.addMenuItems(menu, ['toFront', 'toBack', '-'], parent);
  375. this.addSubmenu('direction', menu, parent);
  376. this.addMenuItems(menu, ['turn', '-'], parent);
  377. this.addSubmenu('align', menu, parent);
  378. this.addSubmenu('distribute', menu, parent);
  379. menu.addSeparator(parent);
  380. this.addSubmenu('navigation', menu, parent);
  381. this.addSubmenu('insert', menu, parent);
  382. this.addSubmenu('layout', menu, parent);
  383. this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'clearWaypoints', 'autosize'], parent);
  384. }))).isEnabled = isGraphEnabled;
  385. this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent)
  386. {
  387. this.addMenuItems(menu, ['insertLink', 'insertImage'], parent);
  388. })));
  389. this.put('view', new Menu(mxUtils.bind(this, function(menu, parent)
  390. {
  391. this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []).
  392. concat(['outline', 'layers', '-', 'pageView', 'pageScale', '-', 'scrollbars', 'tooltips', '-',
  393. 'grid', 'guides', '-', 'connectionArrows', 'connectionPoints', '-',
  394. 'resetView', 'zoomIn', 'zoomOut'], parent));
  395. })));
  396. // Two special dropdowns that are only used in the toolbar
  397. this.put('viewPanels', new Menu(mxUtils.bind(this, function(menu, parent)
  398. {
  399. if (this.editorUi.format != null)
  400. {
  401. this.addMenuItems(menu, ['formatPanel'], parent);
  402. }
  403. this.addMenuItems(menu, ['outline', 'layers'], parent);
  404. })));
  405. this.put('viewZoom', new Menu(mxUtils.bind(this, function(menu, parent)
  406. {
  407. this.addMenuItems(menu, ['resetView', '-'], parent);
  408. var scales = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
  409. for (var i = 0; i < scales.length; i++)
  410. {
  411. (function(scale)
  412. {
  413. menu.addItem((scale * 100) + '%', null, function()
  414. {
  415. graph.zoomTo(scale);
  416. }, parent);
  417. })(scales[i]);
  418. }
  419. this.addMenuItems(menu, ['-', 'fitWindow', 'fitPageWidth', 'fitPage', 'fitTwoPages', '-', 'customZoom'], parent);
  420. })));
  421. this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
  422. {
  423. this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', 'export', '-', 'pageSetup', 'print'], parent);
  424. })));
  425. this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent)
  426. {
  427. this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'delete', '-', 'duplicate', '-',
  428. 'editData', 'editTooltip', '-', 'editStyle', '-', 'edit', '-', 'editLink', 'openLink', '-',
  429. 'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']);
  430. })));
  431. this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent)
  432. {
  433. this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'editDiagram']);
  434. })));
  435. this.put('help', new Menu(mxUtils.bind(this, function(menu, parent)
  436. {
  437. this.addMenuItems(menu, ['help', '-', 'about']);
  438. })));
  439. };
  440. /**
  441. * Adds the label menu items to the given menu and parent.
  442. */
  443. Menus.prototype.put = function(name, menu)
  444. {
  445. this.menus[name] = menu;
  446. return menu;
  447. };
  448. /**
  449. * Adds the label menu items to the given menu and parent.
  450. */
  451. Menus.prototype.get = function(name)
  452. {
  453. return this.menus[name];
  454. };
  455. /**
  456. * Adds the given submenu.
  457. */
  458. Menus.prototype.addSubmenu = function(name, menu, parent, label)
  459. {
  460. var entry = this.get(name);
  461. if (entry != null)
  462. {
  463. var enabled = entry.isEnabled();
  464. if (menu.showDisabled || enabled)
  465. {
  466. var submenu = menu.addItem(label || mxResources.get(name), null, null, parent, null, enabled);
  467. this.addMenu(name, menu, submenu);
  468. }
  469. }
  470. };
  471. /**
  472. * Adds the label menu items to the given menu and parent.
  473. */
  474. Menus.prototype.addMenu = function(name, popupMenu, parent)
  475. {
  476. var menu = this.get(name);
  477. if (menu != null && (popupMenu.showDisabled || menu.isEnabled()))
  478. {
  479. this.get(name).execute(popupMenu, parent);
  480. }
  481. };
  482. /**
  483. * Adds a menu item to insert a table cell.
  484. */
  485. Menus.prototype.addInsertTableCellItem = function(menu, parent)
  486. {
  487. var graph = this.editorUi.editor.graph;
  488. this.addInsertTableItem(menu, mxUtils.bind(this, function(evt, rows, cols)
  489. {
  490. var table = (mxEvent.isControlDown(evt) || mxEvent.isMetaDown(evt)) ?
  491. graph.createCrossFunctionalSwimlane(rows, cols) :
  492. graph.createTable(rows, cols, null, null,
  493. (mxEvent.isShiftDown(evt)) ? 'Table' : null);
  494. var pt = (mxEvent.isAltDown(evt)) ? graph.getFreeInsertPoint() :
  495. graph.getCenterInsertPoint(graph.getBoundingBoxFromGeometry([table], true));
  496. var select = graph.importCells([table], pt.x, pt.y);
  497. if (select != null && select.length > 0)
  498. {
  499. graph.scrollCellToVisible(select[0]);
  500. graph.setSelectionCells(select);
  501. }
  502. }), parent);
  503. };
  504. /**
  505. * Adds a menu item to insert a table.
  506. */
  507. Menus.prototype.addInsertTableItem = function(menu, insertFn, parent)
  508. {
  509. insertFn = (insertFn != null) ? insertFn : mxUtils.bind(this, function(evt, rows, cols)
  510. {
  511. var graph = this.editorUi.editor.graph;
  512. var td = graph.getParentByName(mxEvent.getSource(evt), 'TD');
  513. if (td != null && graph.cellEditor.textarea != null)
  514. {
  515. var row2 = graph.getParentByName(td, 'TR');
  516. // To find the new link, we create a list of all existing links first
  517. // LATER: Refactor for reuse with code for finding inserted image below
  518. var tmp = graph.cellEditor.textarea.getElementsByTagName('table');
  519. var oldTables = [];
  520. for (var i = 0; i < tmp.length; i++)
  521. {
  522. oldTables.push(tmp[i]);
  523. }
  524. // Finding the new table will work with insertHTML, but IE does not support that
  525. graph.container.focus();
  526. graph.pasteHtmlAtCaret(createTable(rows, cols));
  527. // Moves cursor to first table cell
  528. var newTables = graph.cellEditor.textarea.getElementsByTagName('table');
  529. if (newTables.length == oldTables.length + 1)
  530. {
  531. // Inverse order in favor of appended tables
  532. for (var i = newTables.length - 1; i >= 0; i--)
  533. {
  534. if (i == 0 || newTables[i] != oldTables[i - 1])
  535. {
  536. graph.selectNode(newTables[i].rows[0].cells[0]);
  537. break;
  538. }
  539. }
  540. }
  541. }
  542. });
  543. // KNOWN: Does not work in IE8 standards and quirks
  544. var graph = this.editorUi.editor.graph;
  545. var row2 = null;
  546. var td = null;
  547. function createTable(rows, cols)
  548. {
  549. var html = ['<table>'];
  550. for (var i = 0; i < rows; i++)
  551. {
  552. html.push('<tr>');
  553. for (var j = 0; j < cols; j++)
  554. {
  555. html.push('<td><br></td>');
  556. }
  557. html.push('</tr>');
  558. }
  559. html.push('</table>');
  560. return html.join('');
  561. };
  562. // Show table size dialog
  563. var elt2 = menu.addItem('', null, null, parent, null, null, null, true);
  564. // Quirks mode does not add cell padding if cell is empty, needs good old spacer solution
  565. var quirksCellHtml = '<img src="' + mxClient.imageBasePath + '/transparent.gif' + '" width="16" height="16"/>';
  566. function createPicker(rows, cols)
  567. {
  568. var table2 = document.createElement('table');
  569. table2.setAttribute('border', '1');
  570. table2.style.borderCollapse = 'collapse';
  571. table2.style.borderStyle = 'solid';
  572. if (!mxClient.IS_QUIRKS)
  573. {
  574. table2.setAttribute('cellPadding', '8');
  575. }
  576. for (var i = 0; i < rows; i++)
  577. {
  578. var row = table2.insertRow(i);
  579. for (var j = 0; j < cols; j++)
  580. {
  581. var cell = row.insertCell(-1);
  582. if (mxClient.IS_QUIRKS)
  583. {
  584. cell.innerHTML = quirksCellHtml;
  585. }
  586. }
  587. }
  588. return table2;
  589. };
  590. function extendPicker(picker, rows, cols)
  591. {
  592. for (var i = picker.rows.length; i < rows; i++)
  593. {
  594. var row = picker.insertRow(i);
  595. for (var j = 0; j < picker.rows[0].cells.length; j++)
  596. {
  597. var cell = row.insertCell(-1);
  598. if (mxClient.IS_QUIRKS)
  599. {
  600. cell.innerHTML = quirksCellHtml;
  601. }
  602. }
  603. }
  604. for (var i = 0; i < picker.rows.length; i++)
  605. {
  606. var row = picker.rows[i];
  607. for (var j = row.cells.length; j < cols; j++)
  608. {
  609. var cell = row.insertCell(-1);
  610. if (mxClient.IS_QUIRKS)
  611. {
  612. cell.innerHTML = quirksCellHtml;
  613. }
  614. }
  615. }
  616. };
  617. elt2.firstChild.innerHTML = '';
  618. var picker = createPicker(5, 5);
  619. elt2.firstChild.appendChild(picker);
  620. var label = document.createElement('div');
  621. label.style.padding = '4px';
  622. label.style.fontSize = Menus.prototype.defaultFontSize + 'px';
  623. label.innerHTML = '1x1';
  624. elt2.firstChild.appendChild(label);
  625. function mouseover(e)
  626. {
  627. td = graph.getParentByName(mxEvent.getSource(e), 'TD');
  628. var selected = false;
  629. if (td != null)
  630. {
  631. row2 = graph.getParentByName(td, 'TR');
  632. var ext = (mxEvent.isMouseEvent(e)) ? 2 : 4;
  633. extendPicker(picker, Math.min(20, row2.sectionRowIndex + ext), Math.min(20, td.cellIndex + ext));
  634. label.innerHTML = (td.cellIndex + 1) + 'x' + (row2.sectionRowIndex + 1);
  635. for (var i = 0; i < picker.rows.length; i++)
  636. {
  637. var r = picker.rows[i];
  638. for (var j = 0; j < r.cells.length; j++)
  639. {
  640. var cell = r.cells[j];
  641. if (i == row2.sectionRowIndex &&
  642. j == td.cellIndex)
  643. {
  644. selected = cell.style.backgroundColor == 'blue';
  645. }
  646. if (i <= row2.sectionRowIndex && j <= td.cellIndex)
  647. {
  648. cell.style.backgroundColor = 'blue';
  649. }
  650. else
  651. {
  652. cell.style.backgroundColor = 'transparent';
  653. }
  654. }
  655. }
  656. }
  657. mxEvent.consume(e);
  658. return selected;
  659. };
  660. mxEvent.addGestureListeners(picker, null, null, mxUtils.bind(this, function (e)
  661. {
  662. var selected = mouseover(e);
  663. if (td != null && row2 != null && selected)
  664. {
  665. insertFn(e, row2.sectionRowIndex + 1, td.cellIndex + 1);
  666. // Async required to block event for elements under menu
  667. window.setTimeout(mxUtils.bind(this, function()
  668. {
  669. this.editorUi.hideCurrentMenu();
  670. }), 0);
  671. }
  672. }));
  673. mxEvent.addListener(picker, 'mouseover', mouseover);
  674. };
  675. /**
  676. * Adds a style change item to the given menu.
  677. */
  678. Menus.prototype.edgeStyleChange = function(menu, label, keys, values, sprite, parent, reset)
  679. {
  680. return menu.addItem(label, null, mxUtils.bind(this, function()
  681. {
  682. var graph = this.editorUi.editor.graph;
  683. graph.stopEditing(false);
  684. graph.getModel().beginUpdate();
  685. try
  686. {
  687. var cells = graph.getSelectionCells();
  688. var edges = [];
  689. for (var i = 0; i < cells.length; i++)
  690. {
  691. var cell = cells[i];
  692. if (graph.getModel().isEdge(cell))
  693. {
  694. if (reset)
  695. {
  696. var geo = graph.getCellGeometry(cell);
  697. // Resets all edge points
  698. if (geo != null)
  699. {
  700. geo = geo.clone();
  701. geo.points = null;
  702. graph.getModel().setGeometry(cell, geo);
  703. }
  704. }
  705. for (var j = 0; j < keys.length; j++)
  706. {
  707. graph.setCellStyles(keys[j], values[j], [cell]);
  708. }
  709. edges.push(cell);
  710. }
  711. }
  712. this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys,
  713. 'values', values, 'cells', edges));
  714. }
  715. finally
  716. {
  717. graph.getModel().endUpdate();
  718. }
  719. }), parent, sprite);
  720. };
  721. /**
  722. * Adds a style change item to the given menu.
  723. */
  724. Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent, fn, post)
  725. {
  726. var apply = this.createStyleChangeFunction(keys, values);
  727. return menu.addItem(label, null, mxUtils.bind(this, function()
  728. {
  729. var graph = this.editorUi.editor.graph;
  730. if (fn != null && graph.cellEditor.isContentEditing())
  731. {
  732. fn();
  733. }
  734. else
  735. {
  736. apply(post);
  737. }
  738. }), parent, sprite);
  739. };
  740. /**
  741. *
  742. */
  743. Menus.prototype.createStyleChangeFunction = function(keys, values)
  744. {
  745. return mxUtils.bind(this, function(post)
  746. {
  747. var graph = this.editorUi.editor.graph;
  748. graph.stopEditing(false);
  749. graph.getModel().beginUpdate();
  750. try
  751. {
  752. var cells = graph.getSelectionCells();
  753. for (var i = 0; i < keys.length; i++)
  754. {
  755. graph.setCellStyles(keys[i], values[i], cells);
  756. // Removes CSS alignment to produce consistent output
  757. if (keys[i] == mxConstants.STYLE_ALIGN)
  758. {
  759. graph.updateLabelElements(cells, function(elt)
  760. {
  761. elt.removeAttribute('align');
  762. elt.style.textAlign = null;
  763. });
  764. }
  765. // Updates autosize after font changes
  766. if (keys[i] == mxConstants.STYLE_FONTFAMILY)
  767. {
  768. for (var j = 0; j < cells.length; j++)
  769. {
  770. if (graph.model.getChildCount(cells[j]) == 0)
  771. {
  772. graph.autoSizeCell(cells[j], false);
  773. }
  774. }
  775. }
  776. }
  777. if (post != null)
  778. {
  779. post();
  780. }
  781. this.editorUi.fireEvent(new mxEventObject('styleChanged',
  782. 'keys', keys, 'values', values, 'cells', cells));
  783. }
  784. finally
  785. {
  786. graph.getModel().endUpdate();
  787. }
  788. });
  789. };
  790. /**
  791. * Adds a style change item with a prompt to the given menu.
  792. */
  793. Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled, fn, sprite)
  794. {
  795. return menu.addItem(label, null, mxUtils.bind(this, function()
  796. {
  797. var graph = this.editorUi.editor.graph;
  798. var value = defaultValue;
  799. var state = graph.getView().getState(graph.getSelectionCell());
  800. if (state != null)
  801. {
  802. value = state.style[key] || value;
  803. }
  804. var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue)
  805. {
  806. if (newValue != null && newValue.length > 0)
  807. {
  808. graph.getModel().beginUpdate();
  809. try
  810. {
  811. graph.stopEditing(false);
  812. graph.setCellStyles(key, newValue);
  813. }
  814. finally
  815. {
  816. graph.getModel().endUpdate();
  817. }
  818. if (fn != null)
  819. {
  820. fn(newValue);
  821. }
  822. }
  823. }), mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : ''));
  824. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  825. dlg.init();
  826. }), parent, sprite, enabled);
  827. };
  828. /**
  829. * Adds a handler for showing a menu in the given element.
  830. */
  831. Menus.prototype.pickColor = function(key, cmd, defaultValue)
  832. {
  833. var graph = this.editorUi.editor.graph;
  834. var h = 226 + ((Math.ceil(ColorDialog.prototype.presetColors.length / 12) +
  835. Math.ceil(ColorDialog.prototype.defaultColors.length / 12)) * 17);
  836. if (cmd != null && graph.cellEditor.isContentEditing())
  837. {
  838. // Saves and restores text selection for in-place editor
  839. var selState = graph.cellEditor.saveSelection();
  840. var dlg = new ColorDialog(this.editorUi, defaultValue || '000000', mxUtils.bind(this, function(color)
  841. {
  842. graph.cellEditor.restoreSelection(selState);
  843. document.execCommand(cmd, false, (color != mxConstants.NONE) ? color : 'transparent');
  844. }), function()
  845. {
  846. graph.cellEditor.restoreSelection(selState);
  847. });
  848. this.editorUi.showDialog(dlg.container, 230, h, true, true);
  849. dlg.init();
  850. }
  851. else
  852. {
  853. if (this.colorDialog == null)
  854. {
  855. this.colorDialog = new ColorDialog(this.editorUi);
  856. }
  857. this.colorDialog.currentColorKey = key;
  858. var state = graph.getView().getState(graph.getSelectionCell());
  859. var color = 'none';
  860. if (state != null)
  861. {
  862. color = state.style[key] || color;
  863. }
  864. if (color == 'none')
  865. {
  866. color = 'ffffff';
  867. this.colorDialog.picker.fromString('ffffff');
  868. this.colorDialog.colorInput.value = 'none';
  869. }
  870. else
  871. {
  872. this.colorDialog.picker.fromString(color);
  873. }
  874. this.editorUi.showDialog(this.colorDialog.container, 230, h, true, true);
  875. this.colorDialog.init();
  876. }
  877. };
  878. /**
  879. * Adds a handler for showing a menu in the given element.
  880. */
  881. Menus.prototype.toggleStyle = function(key, defaultValue)
  882. {
  883. var graph = this.editorUi.editor.graph;
  884. var value = graph.toggleCellStyles(key, defaultValue);
  885. this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], 'values', [value],
  886. 'cells', graph.getSelectionCells()));
  887. };
  888. /**
  889. * Creates the keyboard event handler for the current graph and history.
  890. */
  891. Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label)
  892. {
  893. var action = this.editorUi.actions.get(key);
  894. if (action != null && (menu.showDisabled || action.isEnabled()) && action.visible)
  895. {
  896. var item = menu.addItem(label || action.label, null, function()
  897. {
  898. action.funct(trigger);
  899. }, parent, sprite, action.isEnabled());
  900. // Adds checkmark image
  901. if (action.toggleAction && action.isSelected())
  902. {
  903. menu.addCheckmark(item, Editor.checkmarkImage);
  904. }
  905. this.addShortcut(item, action);
  906. return item;
  907. }
  908. return null;
  909. };
  910. /**
  911. * Adds a checkmark to the given menuitem.
  912. */
  913. Menus.prototype.addShortcut = function(item, action)
  914. {
  915. if (action.shortcut != null)
  916. {
  917. var td = item.firstChild.nextSibling.nextSibling;
  918. var span = document.createElement('span');
  919. span.style.color = 'gray';
  920. mxUtils.write(span, action.shortcut);
  921. td.appendChild(span);
  922. }
  923. };
  924. /**
  925. * Creates the keyboard event handler for the current graph and history.
  926. */
  927. Menus.prototype.addMenuItems = function(menu, keys, parent, trigger, sprites)
  928. {
  929. for (var i = 0; i < keys.length; i++)
  930. {
  931. if (keys[i] == '-')
  932. {
  933. menu.addSeparator(parent);
  934. }
  935. else
  936. {
  937. this.addMenuItem(menu, keys[i], parent, trigger, (sprites != null) ? sprites[i] : null);
  938. }
  939. }
  940. };
  941. /**
  942. * Creates the keyboard event handler for the current graph and history.
  943. */
  944. Menus.prototype.createPopupMenu = function(menu, cell, evt)
  945. {
  946. menu.smartSeparators = true;
  947. this.addPopupMenuHistoryItems(menu, cell, evt);
  948. this.addPopupMenuEditItems(menu, cell, evt);
  949. this.addPopupMenuStyleItems(menu, cell, evt);
  950. this.addPopupMenuArrangeItems(menu, cell, evt);
  951. this.addPopupMenuCellItems(menu, cell, evt);
  952. this.addPopupMenuSelectionItems(menu, cell, evt);
  953. };
  954. /**
  955. * Creates the keyboard event handler for the current graph and history.
  956. */
  957. Menus.prototype.addPopupMenuHistoryItems = function(menu, cell, evt)
  958. {
  959. if (this.editorUi.editor.graph.isSelectionEmpty())
  960. {
  961. this.addMenuItems(menu, ['undo', 'redo'], null, evt);
  962. }
  963. };
  964. /**
  965. * Creates the keyboard event handler for the current graph and history.
  966. */
  967. Menus.prototype.addPopupMenuEditItems = function(menu, cell, evt)
  968. {
  969. if (this.editorUi.editor.graph.isSelectionEmpty())
  970. {
  971. this.addMenuItems(menu, ['pasteHere'], null, evt);
  972. }
  973. else
  974. {
  975. this.addMenuItems(menu, ['delete', '-', 'cut', 'copy', '-', 'duplicate'], null, evt);
  976. }
  977. };
  978. /**
  979. * Creates the keyboard event handler for the current graph and history.
  980. */
  981. Menus.prototype.addPopupMenuStyleItems = function(menu, cell, evt)
  982. {
  983. if (this.editorUi.editor.graph.getSelectionCount() == 1)
  984. {
  985. this.addMenuItems(menu, ['-', 'setAsDefaultStyle'], null, evt);
  986. }
  987. else if (this.editorUi.editor.graph.isSelectionEmpty())
  988. {
  989. this.addMenuItems(menu, ['-', 'clearDefaultStyle'], null, evt);
  990. }
  991. };
  992. /**
  993. * Creates the keyboard event handler for the current graph and history.
  994. */
  995. Menus.prototype.addPopupMenuArrangeItems = function(menu, cell, evt)
  996. {
  997. var graph = this.editorUi.editor.graph;
  998. if (!graph.isSelectionEmpty())
  999. {
  1000. this.addMenuItems(menu, ['-', 'toFront', 'toBack'], null, evt);
  1001. }
  1002. if (graph.getSelectionCount() > 1)
  1003. {
  1004. this.addMenuItems(menu, ['-', 'group'], null, evt);
  1005. }
  1006. else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) &&
  1007. !graph.isSwimlane(cell) && graph.getModel().getChildCount(cell) > 0)
  1008. {
  1009. this.addMenuItems(menu, ['-', 'ungroup'], null, evt);
  1010. }
  1011. };
  1012. /**
  1013. * Creates the keyboard event handler for the current graph and history.
  1014. */
  1015. Menus.prototype.addPopupMenuCellItems = function(menu, cell, evt)
  1016. {
  1017. var graph = this.editorUi.editor.graph;
  1018. cell = graph.getSelectionCell();
  1019. var state = graph.view.getState(cell);
  1020. menu.addSeparator();
  1021. if (state != null)
  1022. {
  1023. var hasWaypoints = false;
  1024. if (graph.getModel().isEdge(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) != 'entityRelationEdgeStyle' &&
  1025. mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) != 'arrow')
  1026. {
  1027. var handler = graph.selectionCellsHandler.getHandler(cell);
  1028. var isWaypoint = false;
  1029. if (handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2)
  1030. {
  1031. var index = handler.getHandleForEvent(graph.updateMouseEvent(new mxMouseEvent(evt)));
  1032. // Configures removeWaypoint action before execution
  1033. // Using trigger parameter is cleaner but have to find waypoint here anyway.
  1034. var rmWaypointAction = this.editorUi.actions.get('removeWaypoint');
  1035. rmWaypointAction.handler = handler;
  1036. rmWaypointAction.index = index;
  1037. isWaypoint = index > 0 && index < handler.bends.length - 1;
  1038. }
  1039. menu.addSeparator();
  1040. this.addMenuItem(menu, 'turn', null, evt, null, mxResources.get('reverse'));
  1041. this.addMenuItems(menu, [(isWaypoint) ? 'removeWaypoint' : 'addWaypoint'], null, evt);
  1042. // Adds reset waypoints option if waypoints exist
  1043. var geo = graph.getModel().getGeometry(cell);
  1044. hasWaypoints = geo != null && geo.points != null && geo.points.length > 0;
  1045. }
  1046. if (graph.getSelectionCount() == 1 && (hasWaypoints || (graph.getModel().isVertex(cell) &&
  1047. graph.getModel().getEdgeCount(cell) > 0)))
  1048. {
  1049. this.addMenuItems(menu, ['-', 'clearWaypoints'], null, evt);
  1050. }
  1051. if (graph.getSelectionCount() == 1)
  1052. {
  1053. this.addMenuItems(menu, ['-', 'editStyle', 'editData', 'editLink'], null, evt);
  1054. // Shows edit image action if there is an image in the style
  1055. if (graph.getModel().isVertex(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_IMAGE, null) != null)
  1056. {
  1057. menu.addSeparator();
  1058. this.addMenuItem(menu, 'image', null, evt).firstChild.nextSibling.innerHTML = mxResources.get('editImage') + '...';
  1059. }
  1060. }
  1061. }
  1062. };
  1063. /**
  1064. * Creates the keyboard event handler for the current graph and history.
  1065. */
  1066. Menus.prototype.addPopupMenuSelectionItems = function(menu, cell, evt)
  1067. {
  1068. if (this.editorUi.editor.graph.isSelectionEmpty())
  1069. {
  1070. this.addMenuItems(menu, ['-', 'selectVertices', 'selectEdges', 'selectAll'], null, evt);
  1071. }
  1072. };
  1073. /**
  1074. * Creates the keyboard event handler for the current graph and history.
  1075. */
  1076. Menus.prototype.createMenubar = function(container)
  1077. {
  1078. var menubar = new Menubar(this.editorUi, container);
  1079. var menus = this.defaultMenuItems;
  1080. for (var i = 0; i < menus.length; i++)
  1081. {
  1082. (mxUtils.bind(this, function(menu)
  1083. {
  1084. var elt = menubar.addMenu(mxResources.get(menus[i]), mxUtils.bind(this, function()
  1085. {
  1086. // Allows extensions of menu.funct
  1087. menu.funct.apply(this, arguments);
  1088. }));
  1089. this.menuCreated(menu, elt);
  1090. }))(this.get(menus[i]));
  1091. }
  1092. return menubar;
  1093. };
  1094. /**
  1095. * Creates the keyboard event handler for the current graph and history.
  1096. */
  1097. Menus.prototype.menuCreated = function(menu, elt, className)
  1098. {
  1099. if (elt != null)
  1100. {
  1101. className = (className != null) ? className : 'geItem';
  1102. menu.addListener('stateChanged', function()
  1103. {
  1104. elt.enabled = menu.enabled;
  1105. if (!menu.enabled)
  1106. {
  1107. elt.className = className + ' mxDisabled';
  1108. if (document.documentMode == 8)
  1109. {
  1110. elt.style.color = '#c3c3c3';
  1111. }
  1112. }
  1113. else
  1114. {
  1115. elt.className = className;
  1116. if (document.documentMode == 8)
  1117. {
  1118. elt.style.color = '';
  1119. }
  1120. }
  1121. });
  1122. }
  1123. };
  1124. /**
  1125. * Construcs a new menubar for the given editor.
  1126. */
  1127. function Menubar(editorUi, container)
  1128. {
  1129. this.editorUi = editorUi;
  1130. this.container = container;
  1131. };
  1132. /**
  1133. * Adds the menubar elements.
  1134. */
  1135. Menubar.prototype.hideMenu = function()
  1136. {
  1137. this.editorUi.hideCurrentMenu();
  1138. };
  1139. /**
  1140. * Adds a submenu to this menubar.
  1141. */
  1142. Menubar.prototype.addMenu = function(label, funct, before)
  1143. {
  1144. var elt = document.createElement('a');
  1145. elt.className = 'geItem';
  1146. mxUtils.write(elt, label);
  1147. this.addMenuHandler(elt, funct);
  1148. if (before != null)
  1149. {
  1150. this.container.insertBefore(elt, before);
  1151. }
  1152. else
  1153. {
  1154. this.container.appendChild(elt);
  1155. }
  1156. return elt;
  1157. };
  1158. /**
  1159. * Adds a handler for showing a menu in the given element.
  1160. */
  1161. Menubar.prototype.addMenuHandler = function(elt, funct)
  1162. {
  1163. if (funct != null)
  1164. {
  1165. var show = true;
  1166. var clickHandler = mxUtils.bind(this, function(evt)
  1167. {
  1168. if (show && elt.enabled == null || elt.enabled)
  1169. {
  1170. this.editorUi.editor.graph.popupMenuHandler.hideMenu();
  1171. var menu = new mxPopupMenu(funct);
  1172. menu.div.className += ' geMenubarMenu';
  1173. menu.smartSeparators = true;
  1174. menu.showDisabled = true;
  1175. menu.autoExpand = true;
  1176. // Disables autoexpand and destroys menu when hidden
  1177. menu.hideMenu = mxUtils.bind(this, function()
  1178. {
  1179. mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
  1180. this.editorUi.resetCurrentMenu();
  1181. menu.destroy();
  1182. });
  1183. var offset = mxUtils.getOffset(elt);
  1184. menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt);
  1185. this.editorUi.setCurrentMenu(menu, elt);
  1186. }
  1187. mxEvent.consume(evt);
  1188. });
  1189. // Shows menu automatically while in expanded state
  1190. mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt)
  1191. {
  1192. if (this.editorUi.currentMenu != null && this.editorUi.currentMenuElt != elt)
  1193. {
  1194. this.editorUi.hideCurrentMenu();
  1195. clickHandler(evt);
  1196. }
  1197. }));
  1198. // Hides menu if already showing and prevents focus
  1199. mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
  1200. mxUtils.bind(this, function(evt)
  1201. {
  1202. show = this.currentElt != elt;
  1203. evt.preventDefault();
  1204. }));
  1205. mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt)
  1206. {
  1207. clickHandler(evt);
  1208. show = true;
  1209. }));
  1210. }
  1211. };
  1212. /**
  1213. * Creates the keyboard event handler for the current graph and history.
  1214. */
  1215. Menubar.prototype.destroy = function()
  1216. {
  1217. // do nothing
  1218. };
  1219. /**
  1220. * Constructs a new action for the given parameters.
  1221. */
  1222. function Menu(funct, enabled)
  1223. {
  1224. mxEventSource.call(this);
  1225. this.funct = funct;
  1226. this.enabled = (enabled != null) ? enabled : true;
  1227. };
  1228. // Menu inherits from mxEventSource
  1229. mxUtils.extend(Menu, mxEventSource);
  1230. /**
  1231. * Sets the enabled state of the action and fires a stateChanged event.
  1232. */
  1233. Menu.prototype.isEnabled = function()
  1234. {
  1235. return this.enabled;
  1236. };
  1237. /**
  1238. * Sets the enabled state of the action and fires a stateChanged event.
  1239. */
  1240. Menu.prototype.setEnabled = function(value)
  1241. {
  1242. if (this.enabled != value)
  1243. {
  1244. this.enabled = value;
  1245. this.fireEvent(new mxEventObject('stateChanged'));
  1246. }
  1247. };
  1248. /**
  1249. * Sets the enabled state of the action and fires a stateChanged event.
  1250. */
  1251. Menu.prototype.execute = function(menu, parent)
  1252. {
  1253. this.funct(menu, parent);
  1254. };
  1255. /**
  1256. * "Installs" menus in EditorUi.
  1257. */
  1258. EditorUi.prototype.createMenus = function()
  1259. {
  1260. return new Menus(this);
  1261. };