Menus.js 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022
  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.autoPopup = true;
  39. /**
  40. * Adds the label menu items to the given menu and parent.
  41. */
  42. Menus.prototype.init = function()
  43. {
  44. var ui = this.editorUi;
  45. var graph = ui.editor.graph;
  46. var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled);
  47. this.customFonts = [];
  48. this.customFontSizes = [];
  49. this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent)
  50. {
  51. var addItem = mxUtils.bind(this, function(fontFamily)
  52. {
  53. var tr = this.styleChange(menu, fontFamily, [mxConstants.STYLE_FONTFAMILY],
  54. [fontFamily], null, parent, function()
  55. {
  56. document.execCommand('fontname', false, fontFamily);
  57. ui.fireEvent(new mxEventObject('styleChanged',
  58. 'keys', [mxConstants.STYLE_FONTFAMILY],
  59. 'values', [fontFamily],
  60. 'cells', [graph.cellEditor.getEditingCell()]));
  61. }, function()
  62. {
  63. graph.updateLabelElements(graph.getSelectionCells(), function(elt)
  64. {
  65. elt.removeAttribute('face');
  66. elt.style.fontFamily = null;
  67. if (elt.nodeName == 'PRE')
  68. {
  69. graph.replaceElement(elt, 'div');
  70. }
  71. });
  72. });
  73. tr.firstChild.nextSibling.style.fontFamily = fontFamily;
  74. });
  75. for (var i = 0; i < this.defaultFonts.length; i++)
  76. {
  77. addItem(this.defaultFonts[i]);
  78. }
  79. menu.addSeparator(parent);
  80. if (this.customFonts.length > 0)
  81. {
  82. for (var i = 0; i < this.customFonts.length; i++)
  83. {
  84. addItem(this.customFonts[i]);
  85. }
  86. menu.addSeparator(parent);
  87. menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  88. {
  89. this.customFonts = [];
  90. this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  91. }), parent);
  92. menu.addSeparator(parent);
  93. }
  94. this.promptChange(menu, mxResources.get('custom') + '...', '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent, true, mxUtils.bind(this, function(newValue)
  95. {
  96. if (mxUtils.indexOf(this.customFonts, newValue) < 0)
  97. {
  98. this.customFonts.push(newValue);
  99. this.editorUi.fireEvent(new mxEventObject('customFontsChanged'));
  100. }
  101. }));
  102. })));
  103. this.put('formatBlock', new Menu(mxUtils.bind(this, function(menu, parent)
  104. {
  105. function addItem(label, tag)
  106. {
  107. return menu.addItem(label, null, mxUtils.bind(this, function()
  108. {
  109. // TODO: Check if visible
  110. if (graph.cellEditor.textarea != null)
  111. {
  112. graph.cellEditor.textarea.focus();
  113. document.execCommand('formatBlock', false, '<' + tag + '>');
  114. }
  115. }), parent);
  116. };
  117. addItem(mxResources.get('normal'), 'p');
  118. addItem('', 'h1').firstChild.nextSibling.innerHTML = '<h1 style="margin:0px;">' + mxResources.get('heading') + ' 1</h1>';
  119. addItem('', 'h2').firstChild.nextSibling.innerHTML = '<h2 style="margin:0px;">' + mxResources.get('heading') + ' 2</h2>';
  120. addItem('', 'h3').firstChild.nextSibling.innerHTML = '<h3 style="margin:0px;">' + mxResources.get('heading') + ' 3</h3>';
  121. addItem('', 'h4').firstChild.nextSibling.innerHTML = '<h4 style="margin:0px;">' + mxResources.get('heading') + ' 4</h4>';
  122. addItem('', 'h5').firstChild.nextSibling.innerHTML = '<h5 style="margin:0px;">' + mxResources.get('heading') + ' 5</h5>';
  123. addItem('', 'h6').firstChild.nextSibling.innerHTML = '<h6 style="margin:0px;">' + mxResources.get('heading') + ' 6</h6>';
  124. addItem('', 'pre').firstChild.nextSibling.innerHTML = '<pre style="margin:0px;">' + mxResources.get('formatted') + '</pre>';
  125. addItem('', 'blockquote').firstChild.nextSibling.innerHTML = '<blockquote style="margin-top:0px;margin-bottom:0px;">' + mxResources.get('blockquote') + '</blockquote>';
  126. })));
  127. this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent)
  128. {
  129. var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72];
  130. if (mxUtils.indexOf(sizes, this.defaultFontSize) < 0)
  131. {
  132. sizes.push(this.defaultFontSize);
  133. sizes.sort(function(a, b)
  134. {
  135. return a - b;
  136. });
  137. }
  138. var setFontSize = mxUtils.bind(this, function(fontSize)
  139. {
  140. if (graph.cellEditor.textarea != null)
  141. {
  142. // Creates an element with arbitrary size 3
  143. document.execCommand('fontSize', false, '3');
  144. // Changes the css font size of the first font element inside the in-place editor with size 3
  145. // hopefully the above element that we've just created. LATER: Check for new element using
  146. // previous result of getElementsByTagName (see other actions)
  147. var elts = graph.cellEditor.textarea.getElementsByTagName('font');
  148. for (var i = 0; i < elts.length; i++)
  149. {
  150. if (elts[i].getAttribute('size') == '3')
  151. {
  152. elts[i].removeAttribute('size');
  153. elts[i].style.fontSize = fontSize + 'px';
  154. break;
  155. }
  156. }
  157. ui.fireEvent(new mxEventObject('styleChanged',
  158. 'keys', [mxConstants.STYLE_FONTSIZE],
  159. 'values', [fontSize],
  160. 'cells', [graph.cellEditor.getEditingCell()]));
  161. }
  162. });
  163. var addItem = mxUtils.bind(this, function(fontSize)
  164. {
  165. this.styleChange(menu, fontSize, [mxConstants.STYLE_FONTSIZE],
  166. [fontSize], null, parent, function()
  167. {
  168. setFontSize(fontSize);
  169. });
  170. });
  171. for (var i = 0; i < sizes.length; i++)
  172. {
  173. addItem(sizes[i]);
  174. }
  175. menu.addSeparator(parent);
  176. if (this.customFontSizes.length > 0)
  177. {
  178. var counter = 0;
  179. for (var i = 0; i < this.customFontSizes.length; i++)
  180. {
  181. if (mxUtils.indexOf(sizes, this.customFontSizes[i]) < 0)
  182. {
  183. addItem(this.customFontSizes[i]);
  184. counter++;
  185. }
  186. }
  187. if (counter > 0)
  188. {
  189. menu.addSeparator(parent);
  190. }
  191. menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function()
  192. {
  193. this.customFontSizes = [];
  194. }), parent);
  195. menu.addSeparator(parent);
  196. }
  197. var selState = null;
  198. this.promptChange(menu, mxResources.get('custom') + '...',
  199. '(' + mxResources.get('points') + ')', this.defaultFontSize,
  200. mxConstants.STYLE_FONTSIZE, parent, true,
  201. mxUtils.bind(this, function(newValue)
  202. {
  203. if (selState != null && graph.cellEditor.textarea != null)
  204. {
  205. graph.cellEditor.textarea.focus();
  206. graph.cellEditor.restoreSelection(selState);
  207. }
  208. if (newValue != null && newValue.length > 0)
  209. {
  210. this.customFontSizes.push(newValue);
  211. setFontSize(newValue);
  212. }
  213. }), null, function()
  214. {
  215. selState = graph.cellEditor.saveSelection();
  216. return false;
  217. });
  218. })));
  219. this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent)
  220. {
  221. menu.addItem(mxResources.get('flipH'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); }, parent);
  222. menu.addItem(mxResources.get('flipV'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); }, parent);
  223. this.addMenuItems(menu, ['-', 'rotation'], parent);
  224. })));
  225. this.put('align', new Menu(mxUtils.bind(this, function(menu, parent)
  226. {
  227. var ss = this.editorUi.getSelectionState();
  228. var enabled = ss.vertices.length > 1;
  229. menu.addItem(mxResources.get('leftAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, parent, null, enabled);
  230. menu.addItem(mxResources.get('center'), null, function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, parent, null, enabled);
  231. menu.addItem(mxResources.get('rightAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, parent, null, enabled);
  232. menu.addSeparator(parent);
  233. menu.addItem(mxResources.get('topAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_TOP); }, parent, null, enabled);
  234. menu.addItem(mxResources.get('middle'), null, function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, parent, null, enabled);
  235. menu.addItem(mxResources.get('bottomAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, parent, null, enabled);
  236. this.addMenuItems(menu, ['-', 'snapToGrid'], parent);
  237. })));
  238. this.put('distribute', new Menu(mxUtils.bind(this, function(menu, parent)
  239. {
  240. menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true); }, parent);
  241. menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false); }, parent);
  242. menu.addSeparator(parent);
  243. this.addSubmenu('distributeSpacing', menu, parent, mxResources.get('spacing'));
  244. })));
  245. this.put('distributeSpacing', new Menu(mxUtils.bind(this, function(menu, parent)
  246. {
  247. menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true, null, true); }, parent);
  248. menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false, null, true); }, parent);
  249. })));
  250. this.put('line', new Menu(mxUtils.bind(this, function(menu, parent)
  251. {
  252. var state = graph.view.getState(graph.getSelectionCell());
  253. if (state != null)
  254. {
  255. var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE);
  256. if (shape != 'arrow')
  257. {
  258. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  259. [null, null, null], 'geIcon geSprite geSprite-straight', parent, true).setAttribute('title', mxResources.get('straight'));
  260. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  261. ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', parent, true).setAttribute('title', mxResources.get('orthogonal'));
  262. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  263. ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', parent, true).setAttribute('title', mxResources.get('simple'));
  264. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  265. ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', parent, true).setAttribute('title', mxResources.get('simple'));
  266. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  267. ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', parent, true).setAttribute('title', mxResources.get('isometric'));
  268. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  269. ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', parent, true).setAttribute('title', mxResources.get('isometric'));
  270. if (shape == 'connector')
  271. {
  272. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  273. ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', parent, true).setAttribute('title', mxResources.get('curved'));
  274. }
  275. this.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE],
  276. ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', parent, true).setAttribute('title', mxResources.get('entityRelation'));
  277. }
  278. menu.addSeparator(parent);
  279. this.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'],
  280. [null, null, null, null], 'geIcon geSprite geSprite-connection', parent, null, null, true).setAttribute('title', mxResources.get('line'));
  281. this.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'],
  282. ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', parent, null, null, true).setAttribute('title', mxResources.get('link'));
  283. this.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'],
  284. ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', parent, null, null, true).setAttribute('title', mxResources.get('arrow'));
  285. this.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'],
  286. ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', parent, null, null, true).setAttribute('title', mxResources.get('simpleArrow'));
  287. }
  288. })));
  289. this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent)
  290. {
  291. var promptSpacing = mxUtils.bind(this, function(defaultValue, fn)
  292. {
  293. this.editorUi.prompt(mxResources.get('spacing'), defaultValue, fn);
  294. });
  295. var runTreeLayout = mxUtils.bind(this, function(layout)
  296. {
  297. this.editorUi.tryAndHandle(mxUtils.bind(this, function()
  298. {
  299. var tmp = graph.getSelectionCell();
  300. var roots = null;
  301. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  302. {
  303. if (graph.getModel().getEdgeCount(tmp) == 0)
  304. {
  305. roots = graph.findTreeRoots(graph.getDefaultParent());
  306. }
  307. }
  308. else
  309. {
  310. roots = graph.findTreeRoots(tmp);
  311. }
  312. if (roots != null && roots.length > 0)
  313. {
  314. tmp = roots[0];
  315. }
  316. if (tmp != null)
  317. {
  318. this.editorUi.executeLayout(function()
  319. {
  320. layout.execute(graph.getDefaultParent(), tmp);
  321. if (!graph.isSelectionEmpty())
  322. {
  323. tmp = graph.getModel().getParent(tmp);
  324. if (graph.getModel().isVertex(tmp))
  325. {
  326. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  327. }
  328. }
  329. }, true);
  330. }
  331. }));
  332. });
  333. menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function()
  334. {
  335. this.editorUi.tryAndHandle(mxUtils.bind(this, function()
  336. {
  337. var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST);
  338. this.editorUi.executeLayout(function()
  339. {
  340. var selectionCells = graph.getSelectionCells();
  341. layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
  342. }, true);
  343. }));
  344. }), parent);
  345. menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function()
  346. {
  347. this.editorUi.tryAndHandle(mxUtils.bind(this, function()
  348. {
  349. var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH);
  350. this.editorUi.executeLayout(function()
  351. {
  352. var selectionCells = graph.getSelectionCells();
  353. layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells);
  354. }, true);
  355. }));
  356. }), parent);
  357. menu.addSeparator(parent);
  358. menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function()
  359. {
  360. var layout = new mxCompactTreeLayout(graph, true);
  361. layout.edgeRouting = false;
  362. layout.levelDistance = 30;
  363. layout.sortEdges = true;
  364. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(spacing)
  365. {
  366. if (!isNaN(spacing))
  367. {
  368. layout.levelDistance = spacing;
  369. runTreeLayout(layout);
  370. }
  371. }));
  372. }), parent);
  373. menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function()
  374. {
  375. var layout = new mxCompactTreeLayout(graph, false);
  376. layout.edgeRouting = false;
  377. layout.levelDistance = 30;
  378. layout.sortEdges = true;
  379. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(spacing)
  380. {
  381. if (!isNaN(spacing))
  382. {
  383. layout.levelDistance = spacing;
  384. runTreeLayout(layout);
  385. }
  386. }));
  387. }), parent);
  388. menu.addItem(mxResources.get('radialTree'), null, mxUtils.bind(this, function()
  389. {
  390. var layout = new mxRadialTreeLayout(graph);
  391. layout.levelDistance = 80;
  392. layout.autoRadius = true;
  393. promptSpacing(layout.levelDistance, mxUtils.bind(this, function(spacing)
  394. {
  395. if (!isNaN(spacing))
  396. {
  397. layout.levelDistance = spacing;
  398. runTreeLayout(layout);
  399. }
  400. }));
  401. }), parent);
  402. menu.addSeparator(parent);
  403. menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function()
  404. {
  405. var layout = new mxFastOrganicLayout(graph);
  406. promptSpacing(layout.forceConstant, mxUtils.bind(this, function(newValue)
  407. {
  408. this.editorUi.tryAndHandle(mxUtils.bind(this, function()
  409. {
  410. layout.forceConstant = newValue;
  411. this.editorUi.executeLayout(function()
  412. {
  413. var tmp = graph.getSelectionCell();
  414. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  415. {
  416. tmp = graph.getDefaultParent();
  417. }
  418. layout.execute(tmp);
  419. if (graph.getModel().isVertex(tmp))
  420. {
  421. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  422. }
  423. }, true);
  424. }));
  425. }));
  426. }), parent);
  427. menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function()
  428. {
  429. this.editorUi.tryAndHandle(mxUtils.bind(this, function()
  430. {
  431. var layout = new mxCircleLayout(graph);
  432. this.editorUi.executeLayout(function()
  433. {
  434. var tmp = graph.getSelectionCell();
  435. if (tmp == null || graph.getModel().getChildCount(tmp) == 0)
  436. {
  437. tmp = graph.getDefaultParent();
  438. }
  439. layout.execute(tmp);
  440. if (graph.getModel().isVertex(tmp))
  441. {
  442. graph.updateGroupBounds([tmp], graph.gridSize * 2, true);
  443. }
  444. }, true);
  445. }));
  446. }), parent);
  447. })));
  448. this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent)
  449. {
  450. this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse', '-', 'collapsible'], parent);
  451. })));
  452. this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent)
  453. {
  454. this.addMenuItems(menu, ['toFront', 'toBack', 'bringForward', 'sendBackward', '-'], parent);
  455. this.addSubmenu('direction', menu, parent);
  456. this.addMenuItems(menu, ['turn', '-'], parent);
  457. this.addSubmenu('align', menu, parent);
  458. this.addSubmenu('distribute', menu, parent);
  459. menu.addSeparator(parent);
  460. this.addSubmenu('navigation', menu, parent);
  461. this.addSubmenu('insert', menu, parent);
  462. this.addSubmenu('layout', menu, parent);
  463. this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'clearWaypoints', 'autosize'], parent);
  464. }))).isEnabled = isGraphEnabled;
  465. this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent)
  466. {
  467. this.addMenuItems(menu, ['insertLink', 'insertImage'], parent);
  468. })));
  469. this.put('view', new Menu(mxUtils.bind(this, function(menu, parent)
  470. {
  471. this.addMenuItems(menu, ((this.editorUi.format != null) ? ['format'] : []).
  472. concat(['outline', 'layers', '-', 'pageView', 'pageScale', '-', 'tooltips',
  473. 'grid', 'guides', '-', 'connectionArrows', 'connectionPoints', '-',
  474. 'resetView', 'zoomIn', 'zoomOut'], parent));
  475. })));
  476. // Two special dropdowns that are only used in the toolbar
  477. this.put('viewPanels', new Menu(mxUtils.bind(this, function(menu, parent)
  478. {
  479. if (this.editorUi.format != null)
  480. {
  481. this.addMenuItems(menu, ['format'], parent);
  482. }
  483. this.addMenuItems(menu, ['outline', 'layers'], parent);
  484. })));
  485. this.put('viewZoom', new Menu(mxUtils.bind(this, function(menu, parent)
  486. {
  487. this.addMenuItems(menu, ['smartFit', '-'], parent);
  488. var scales = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
  489. for (var i = 0; i < scales.length; i++)
  490. {
  491. (function(scale)
  492. {
  493. menu.addItem((scale * 100) + '%', null, function()
  494. {
  495. graph.zoomTo(scale);
  496. }, parent);
  497. })(scales[i]);
  498. }
  499. this.addMenuItems(menu, ['-', 'fitWindow', 'fitPageWidth', 'fitPage', 'fitTwoPages', '-', 'customZoom'], parent);
  500. })));
  501. this.put('file', new Menu(mxUtils.bind(this, function(menu, parent)
  502. {
  503. this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', 'export', '-', 'pageSetup', 'print'], parent);
  504. })));
  505. this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent)
  506. {
  507. this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'delete', '-', 'duplicate', '-',
  508. 'editData', 'editTooltip', '-', 'editStyle', '-', 'edit', '-', 'editLink', 'openLink', '-',
  509. 'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']);
  510. })));
  511. this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent)
  512. {
  513. this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'editDiagram']);
  514. })));
  515. this.put('help', new Menu(mxUtils.bind(this, function(menu, parent)
  516. {
  517. this.addMenuItems(menu, ['help', '-', 'about']);
  518. })));
  519. };
  520. /**
  521. * Adds the label menu items to the given menu and parent.
  522. */
  523. Menus.prototype.put = function(name, menu)
  524. {
  525. this.menus[name] = menu;
  526. return menu;
  527. };
  528. /**
  529. * Adds the label menu items to the given menu and parent.
  530. */
  531. Menus.prototype.get = function(name)
  532. {
  533. return this.menus[name];
  534. };
  535. /**
  536. * Adds the given submenu.
  537. */
  538. Menus.prototype.addSubmenu = function(name, menu, parent, label)
  539. {
  540. var entry = this.get(name);
  541. var submenu = null;
  542. if (entry != null)
  543. {
  544. var enabled = entry.isEnabled();
  545. if (menu.showDisabled || enabled)
  546. {
  547. submenu = menu.addItem(label || mxResources.get(name),
  548. null, null, parent, null, enabled);
  549. this.addMenu(name, menu, submenu);
  550. }
  551. }
  552. return submenu;
  553. };
  554. /**
  555. * Adds the label menu items to the given menu and parent.
  556. */
  557. Menus.prototype.addMenu = function(name, popupMenu, parent)
  558. {
  559. var menu = this.get(name);
  560. if (menu != null && (popupMenu.showDisabled || menu.isEnabled()))
  561. {
  562. menu.execute(popupMenu, parent);
  563. }
  564. };
  565. /**
  566. * Adds a menu item to insert a table cell.
  567. */
  568. Menus.prototype.addInsertTableCellItem = function(menu, parent)
  569. {
  570. var graph = this.editorUi.editor.graph;
  571. var cell = graph.getSelectionCell();
  572. var style = graph.getCurrentCellStyle(cell);
  573. if (graph.getSelectionCount() > 1)
  574. {
  575. if (graph.isTableCell(cell))
  576. {
  577. cell = graph.model.getParent(cell);
  578. }
  579. if (graph.isTableRow(cell))
  580. {
  581. cell = graph.model.getParent(cell);
  582. }
  583. }
  584. var isTable = graph.isTable(cell) ||
  585. graph.isTableRow(cell) ||
  586. graph.isTableCell(cell);
  587. var isStack = graph.isStack(cell) ||
  588. graph.isStackChild(cell);
  589. var showCols = isTable;
  590. var showRows = isTable;
  591. if (isStack)
  592. {
  593. var style = (graph.isStack(cell)) ? style :
  594. graph.getCellStyle(graph.model.getParent(cell));
  595. showRows = style['horizontalStack'] == '0';
  596. showCols = !showRows;
  597. }
  598. if (parent != null || (!isTable && !isStack))
  599. {
  600. this.addInsertTableItem(menu, mxUtils.bind(this, function(evt, rows, cols, title, container)
  601. {
  602. var table = (container || mxEvent.isControlDown(evt) || mxEvent.isMetaDown(evt)) ?
  603. graph.createCrossFunctionalSwimlane(rows, cols, null, null,
  604. (title || mxEvent.isShiftDown(evt)) ? 'Cross-Functional Flowchart' : null) :
  605. graph.createTable(rows, cols, null, null,
  606. (title || mxEvent.isShiftDown(evt)) ? 'Table' : null);
  607. var pt = (mxEvent.isAltDown(evt)) ? graph.getFreeInsertPoint() :
  608. graph.getCenterInsertPoint(graph.getBoundingBoxFromGeometry([table], true));
  609. var select = null;
  610. graph.getModel().beginUpdate();
  611. try
  612. {
  613. select = graph.importCells([table], pt.x, pt.y);
  614. graph.fireEvent(new mxEventObject('cellsInserted', 'cells',
  615. graph.model.getDescendants(select[0])));
  616. }
  617. finally
  618. {
  619. graph.getModel().endUpdate();
  620. }
  621. if (select != null && select.length > 0)
  622. {
  623. graph.scrollCellToVisible(select[0]);
  624. graph.setSelectionCells(select);
  625. }
  626. }), parent);
  627. }
  628. else
  629. {
  630. if (showCols)
  631. {
  632. var elt = menu.addItem(mxResources.get('insertColumnBefore'), null, mxUtils.bind(this, function()
  633. {
  634. try
  635. {
  636. if (isStack)
  637. {
  638. graph.insertLane(cell, true);
  639. }
  640. else
  641. {
  642. graph.insertTableColumn(cell, true);
  643. }
  644. }
  645. catch (e)
  646. {
  647. this.editorUi.handleError(e);
  648. }
  649. }), null, 'geIcon geSprite geSprite-insertcolumnbefore');
  650. elt.setAttribute('title', mxResources.get('insertColumnBefore'));
  651. elt = menu.addItem(mxResources.get('insertColumnAfter'), null, mxUtils.bind(this, function()
  652. {
  653. try
  654. {
  655. if (isStack)
  656. {
  657. graph.insertLane(cell, false);
  658. }
  659. else
  660. {
  661. graph.insertTableColumn(cell, false);
  662. }
  663. }
  664. catch (e)
  665. {
  666. this.editorUi.handleError(e);
  667. }
  668. }), null, 'geIcon geSprite geSprite-insertcolumnafter');
  669. elt.setAttribute('title', mxResources.get('insertColumnAfter'));
  670. elt = menu.addItem(mxResources.get('deleteColumn'), null, mxUtils.bind(this, function()
  671. {
  672. if (cell != null)
  673. {
  674. try
  675. {
  676. if (isStack)
  677. {
  678. graph.deleteLane(cell);
  679. }
  680. else
  681. {
  682. graph.deleteTableColumn(cell);
  683. }
  684. }
  685. catch (e)
  686. {
  687. this.editorUi.handleError(e);
  688. }
  689. }
  690. }), null, 'geIcon geSprite geSprite-deletecolumn');
  691. elt.setAttribute('title', mxResources.get('deleteColumn'));
  692. }
  693. if (showRows)
  694. {
  695. elt = menu.addItem(mxResources.get('insertRowBefore'), null, mxUtils.bind(this, function()
  696. {
  697. try
  698. {
  699. if (isStack)
  700. {
  701. graph.insertLane(cell, true);
  702. }
  703. else
  704. {
  705. graph.insertTableRow(cell, true);
  706. }
  707. }
  708. catch (e)
  709. {
  710. this.editorUi.handleError(e);
  711. }
  712. }), null, 'geIcon geSprite geSprite-insertrowbefore');
  713. elt.setAttribute('title', mxResources.get('insertRowBefore'));
  714. elt = menu.addItem(mxResources.get('insertRowAfter'), null, mxUtils.bind(this, function()
  715. {
  716. try
  717. {
  718. if (isStack)
  719. {
  720. graph.insertLane(cell, false);
  721. }
  722. else
  723. {
  724. graph.insertTableRow(cell, false);
  725. }
  726. }
  727. catch (e)
  728. {
  729. this.editorUi.handleError(e);
  730. }
  731. }), null, 'geIcon geSprite geSprite-insertrowafter');
  732. elt.setAttribute('title', mxResources.get('insertRowAfter'));
  733. elt = menu.addItem(mxResources.get('deleteRow'), null, mxUtils.bind(this, function()
  734. {
  735. try
  736. {
  737. if (isStack)
  738. {
  739. graph.deleteLane(cell);
  740. }
  741. else
  742. {
  743. graph.deleteTableRow(cell);
  744. }
  745. }
  746. catch (e)
  747. {
  748. this.editorUi.handleError(e);
  749. }
  750. }), null, 'geIcon geSprite geSprite-deleterow');
  751. elt.setAttribute('title', mxResources.get('deleteRow'));
  752. var ss = this.editorUi.getSelectionState();
  753. if (ss.mergeCell != null)
  754. {
  755. this.addMenuItem(menu, 'mergeCells');
  756. }
  757. else if (ss.style['colspan'] > 1 || ss.style['rowspan'] > 1)
  758. {
  759. this.addMenuItem(menu, 'unmergeCells');
  760. }
  761. }
  762. }
  763. };
  764. /**
  765. * Adds a menu item to insert a table.
  766. */
  767. Menus.prototype.addInsertTableItem = function(menu, insertFn, parent, showOptions)
  768. {
  769. showOptions = (showOptions != null) ? showOptions : true;
  770. insertFn = (insertFn != null) ? insertFn : mxUtils.bind(this, function(evt, rows, cols)
  771. {
  772. var graph = this.editorUi.editor.graph;
  773. var td = graph.getParentByName(mxEvent.getSource(evt), 'TD');
  774. if (td != null && graph.cellEditor.textarea != null)
  775. {
  776. // To find the new link, we create a list of all existing links first
  777. // LATER: Refactor for reuse with code for finding inserted image below
  778. var tmp = graph.cellEditor.textarea.getElementsByTagName('table');
  779. var oldTables = [];
  780. for (var i = 0; i < tmp.length; i++)
  781. {
  782. oldTables.push(tmp[i]);
  783. }
  784. // Finding the new table will work with insertHTML, but IE does not support that
  785. graph.container.focus();
  786. graph.pasteHtmlAtCaret(createTable(rows, cols));
  787. // Moves cursor to first table cell
  788. var newTables = graph.cellEditor.textarea.getElementsByTagName('table');
  789. if (newTables.length == oldTables.length + 1)
  790. {
  791. // Inverse order in favor of appended tables
  792. for (var i = newTables.length - 1; i >= 0; i--)
  793. {
  794. if (i == 0 || newTables[i] != oldTables[i - 1])
  795. {
  796. graph.selectNode(newTables[i].rows[0].cells[0]);
  797. break;
  798. }
  799. }
  800. }
  801. }
  802. });
  803. // KNOWN: Does not work in IE8 standards and quirks
  804. var graph = this.editorUi.editor.graph;
  805. var row2 = null;
  806. var td = null;
  807. function createTable(rows, cols)
  808. {
  809. var html = ['<table>'];
  810. for (var i = 0; i < rows; i++)
  811. {
  812. html.push('<tr>');
  813. for (var j = 0; j < cols; j++)
  814. {
  815. html.push('<td><br></td>');
  816. }
  817. html.push('</tr>');
  818. }
  819. html.push('</table>');
  820. return html.join('');
  821. };
  822. if (parent == null)
  823. {
  824. menu.div.className += ' geToolbarMenu';
  825. menu.labels = false;
  826. }
  827. var elt2 = menu.addItem('', null, null, parent, null, null, null, true);
  828. elt2.firstChild.style.fontSize = Menus.prototype.defaultFontSize + 'px';
  829. // Hide empty rows in menu item
  830. var tds = elt2.getElementsByTagName('td');
  831. if (tds.length > 1)
  832. {
  833. tds[1].style.display = 'none';
  834. tds[2].style.display = 'none';
  835. }
  836. function createPicker(rows, cols)
  837. {
  838. var table2 = document.createElement('table');
  839. table2.className = 'geInsertTablePicker';
  840. table2.setAttribute('border', '1');
  841. table2.style.borderCollapse = 'collapse';
  842. table2.style.borderStyle = 'solid';
  843. table2.style.marginBottom = '4px';
  844. table2.style.marginTop = '8px';
  845. table2.setAttribute('cellPadding', '8');
  846. for (var i = 0; i < rows; i++)
  847. {
  848. var row = table2.insertRow(i);
  849. for (var j = 0; j < cols; j++)
  850. {
  851. var cell = row.insertCell(-1);
  852. }
  853. }
  854. return table2;
  855. };
  856. function extendPicker(picker, rows, cols)
  857. {
  858. for (var i = picker.rows.length; i < rows; i++)
  859. {
  860. var row = picker.insertRow(i);
  861. for (var j = 0; j < picker.rows[0].cells.length; j++)
  862. {
  863. var cell = row.insertCell(-1);
  864. }
  865. }
  866. for (var i = 0; i < picker.rows.length; i++)
  867. {
  868. var row = picker.rows[i];
  869. for (var j = row.cells.length; j < cols; j++)
  870. {
  871. var cell = row.insertCell(-1);
  872. }
  873. }
  874. };
  875. elt2.firstChild.innerText = '';
  876. var titleOption = document.createElement('input');
  877. titleOption.setAttribute('id', 'geTitleOption');
  878. titleOption.setAttribute('type', 'checkbox');
  879. titleOption.style.verticalAlign = 'middle';
  880. var titleLbl = document.createElement('label');
  881. mxUtils.write(titleLbl, mxResources.get('title'));
  882. titleLbl.setAttribute('for', 'geTitleOption');
  883. titleLbl.style.verticalAlign = 'middle';
  884. mxEvent.addGestureListeners(titleLbl, null, null, mxUtils.bind(this, function(e)
  885. {
  886. mxEvent.consume(e);
  887. }));
  888. mxEvent.addGestureListeners(titleOption, null, null, mxUtils.bind(this, function(e)
  889. {
  890. mxEvent.consume(e);
  891. }));
  892. var containerOption = document.createElement('input');
  893. containerOption.setAttribute('id', 'geContainerOption');
  894. containerOption.setAttribute('type', 'checkbox');
  895. containerOption.style.verticalAlign = 'middle';
  896. var containerLbl = document.createElement('label');
  897. mxUtils.write(containerLbl, mxResources.get('container'));
  898. containerLbl.setAttribute('for', 'geContainerOption');
  899. containerLbl.style.verticalAlign = 'middle';
  900. mxEvent.addGestureListeners(containerLbl, null, null, mxUtils.bind(this, function(e)
  901. {
  902. mxEvent.consume(e);
  903. }));
  904. mxEvent.addGestureListeners(containerOption, null, null, mxUtils.bind(this, function(e)
  905. {
  906. mxEvent.consume(e);
  907. }));
  908. if (showOptions)
  909. {
  910. elt2.firstChild.appendChild(titleOption);
  911. elt2.firstChild.appendChild(titleLbl);
  912. mxUtils.br(elt2.firstChild);
  913. elt2.firstChild.appendChild(containerOption);
  914. elt2.firstChild.appendChild(containerLbl);
  915. mxUtils.br(elt2.firstChild);
  916. }
  917. var picker = createPicker(5, 5);
  918. elt2.firstChild.appendChild(picker);
  919. var label = document.createElement('div');
  920. label.style.textAlign = 'center';
  921. label.style.padding = '4px';
  922. label.style.width = '100%';
  923. label.innerHTML = '1x1';
  924. elt2.firstChild.appendChild(label);
  925. function mouseover(e)
  926. {
  927. td = graph.getParentByName(mxEvent.getSource(e), 'TD');
  928. var selected = false;
  929. if (td != null)
  930. {
  931. row2 = graph.getParentByName(td, 'TR');
  932. var ext = (mxEvent.isMouseEvent(e)) ? 2 : 4;
  933. extendPicker(picker, Math.min(20, row2.sectionRowIndex + ext), Math.min(20, td.cellIndex + ext));
  934. label.innerHTML = (td.cellIndex + 1) + 'x' + (row2.sectionRowIndex + 1);
  935. for (var i = 0; i < picker.rows.length; i++)
  936. {
  937. var r = picker.rows[i];
  938. for (var j = 0; j < r.cells.length; j++)
  939. {
  940. var cell = r.cells[j];
  941. if (i == row2.sectionRowIndex &&
  942. j == td.cellIndex)
  943. {
  944. selected = cell.style.backgroundColor == 'blue';
  945. }
  946. if (i <= row2.sectionRowIndex && j <= td.cellIndex)
  947. {
  948. cell.style.backgroundColor = 'blue';
  949. }
  950. else
  951. {
  952. cell.style.backgroundColor = 'transparent';
  953. }
  954. }
  955. }
  956. }
  957. mxEvent.consume(e);
  958. return selected;
  959. };
  960. mxEvent.addGestureListeners(picker, null, null, mxUtils.bind(this, function(e)
  961. {
  962. var selected = mouseover(e);
  963. if (td != null && row2 != null && selected)
  964. {
  965. insertFn(e, row2.sectionRowIndex + 1, td.cellIndex + 1,
  966. titleOption.checked, containerOption.checked);
  967. // Async required to block event for elements under menu
  968. window.setTimeout(mxUtils.bind(this, function()
  969. {
  970. this.editorUi.hideCurrentMenu();
  971. }), 0);
  972. }
  973. }));
  974. mxEvent.addListener(picker, 'mouseover', mouseover);
  975. };
  976. /**
  977. * Adds a style change item to the given menu.
  978. */
  979. Menus.prototype.edgeStyleChange = function(menu, label, keys, values, sprite, parent, reset, image)
  980. {
  981. return this.showIconOnly(menu.addItem(label, image, mxUtils.bind(this, function()
  982. {
  983. var graph = this.editorUi.editor.graph;
  984. graph.stopEditing(false);
  985. graph.getModel().beginUpdate();
  986. try
  987. {
  988. var cells = graph.getSelectionCells();
  989. var edges = [];
  990. for (var i = 0; i < cells.length; i++)
  991. {
  992. var cell = cells[i];
  993. if (graph.getModel().isEdge(cell))
  994. {
  995. if (reset)
  996. {
  997. var geo = graph.getCellGeometry(cell);
  998. // Resets all edge points
  999. if (geo != null)
  1000. {
  1001. geo = geo.clone();
  1002. geo.points = null;
  1003. graph.getModel().setGeometry(cell, geo);
  1004. }
  1005. }
  1006. for (var j = 0; j < keys.length; j++)
  1007. {
  1008. graph.setCellStyles(keys[j], values[j], [cell]);
  1009. }
  1010. edges.push(cell);
  1011. }
  1012. }
  1013. this.editorUi.fireEvent(new mxEventObject(
  1014. 'styleChanged', 'cells', edges,
  1015. 'keys', keys, 'values', values,
  1016. 'force', cells.length == 0));
  1017. }
  1018. finally
  1019. {
  1020. graph.getModel().endUpdate();
  1021. }
  1022. }), parent, sprite));
  1023. };
  1024. /**
  1025. * Adds a style change item to the given menu.
  1026. */
  1027. Menus.prototype.showIconOnly = function(elt)
  1028. {
  1029. var td = elt.getElementsByTagName('td');
  1030. for (i = 0; i < td.length; i++)
  1031. {
  1032. if (td[i].getAttribute('class') == 'mxPopupMenuItem')
  1033. {
  1034. td[i].style.display = 'none';
  1035. }
  1036. }
  1037. return elt;
  1038. };
  1039. /**
  1040. * Adds a style change item to the given menu.
  1041. */
  1042. Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent, fn, post, iconOnly)
  1043. {
  1044. var apply = this.createStyleChangeFunction(keys, values);
  1045. var elt = menu.addItem(label, null, mxUtils.bind(this, function()
  1046. {
  1047. var graph = this.editorUi.editor.graph;
  1048. if (fn != null && graph.cellEditor.isContentEditing())
  1049. {
  1050. fn();
  1051. }
  1052. else
  1053. {
  1054. apply(post);
  1055. }
  1056. }), parent, sprite);
  1057. if (iconOnly)
  1058. {
  1059. this.showIconOnly(elt);
  1060. }
  1061. return elt;
  1062. };
  1063. /**
  1064. *
  1065. */
  1066. Menus.prototype.createStyleChangeFunction = function(keys, values)
  1067. {
  1068. return mxUtils.bind(this, function(post)
  1069. {
  1070. var graph = this.editorUi.editor.graph;
  1071. graph.stopEditing(false);
  1072. graph.getModel().beginUpdate();
  1073. try
  1074. {
  1075. var cells = graph.getEditableCells(graph.getSelectionCells());
  1076. var autoSizeCells = false;
  1077. for (var i = 0; i < keys.length; i++)
  1078. {
  1079. graph.setCellStyles(keys[i], values[i], cells);
  1080. // Removes CSS alignment to produce consistent output
  1081. if (keys[i] == mxConstants.STYLE_ALIGN)
  1082. {
  1083. graph.updateLabelElements(cells, function(elt)
  1084. {
  1085. elt.removeAttribute('align');
  1086. elt.style.textAlign = null;
  1087. });
  1088. }
  1089. // Updates autosize after font changes
  1090. if (keys[i] == mxConstants.STYLE_FONTFAMILY ||
  1091. keys[i] == 'fontSource')
  1092. {
  1093. autoSizeCells = true;
  1094. }
  1095. }
  1096. if (autoSizeCells)
  1097. {
  1098. for (var j = 0; j < cells.length; j++)
  1099. {
  1100. if (graph.model.getChildCount(cells[j]) == 0)
  1101. {
  1102. graph.autoSizeCell(cells[j], false);
  1103. }
  1104. }
  1105. }
  1106. if (post != null)
  1107. {
  1108. post();
  1109. }
  1110. this.editorUi.fireEvent(new mxEventObject('styleChanged',
  1111. 'keys', keys, 'values', values, 'cells', cells));
  1112. }
  1113. finally
  1114. {
  1115. graph.getModel().endUpdate();
  1116. }
  1117. });
  1118. };
  1119. /**
  1120. * Adds a style change item with a prompt to the given menu.
  1121. */
  1122. Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled, fn, sprite, beforeFn)
  1123. {
  1124. return menu.addItem(label, null, mxUtils.bind(this, function()
  1125. {
  1126. var graph = this.editorUi.editor.graph;
  1127. var value = defaultValue;
  1128. var state = graph.getView().getState(graph.getSelectionCell());
  1129. if (state != null)
  1130. {
  1131. value = state.style[key] || value;
  1132. }
  1133. var doStopEditing = (beforeFn != null) ? beforeFn() : true;
  1134. var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'),
  1135. mxUtils.bind(this, function(newValue)
  1136. {
  1137. if (newValue != null && newValue.length > 0)
  1138. {
  1139. if (doStopEditing)
  1140. {
  1141. graph.getModel().beginUpdate();
  1142. try
  1143. {
  1144. graph.stopEditing(false);
  1145. graph.setCellStyles(key, newValue);
  1146. }
  1147. finally
  1148. {
  1149. graph.getModel().endUpdate();
  1150. }
  1151. }
  1152. if (fn != null)
  1153. {
  1154. fn(newValue);
  1155. }
  1156. }
  1157. }), mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : ''),
  1158. null, null, null, null, function()
  1159. {
  1160. if (fn != null && beforeFn != null)
  1161. {
  1162. fn(null);
  1163. }
  1164. });
  1165. this.editorUi.showDialog(dlg.container, 300, 80, true, true);
  1166. dlg.init();
  1167. }), parent, sprite, enabled);
  1168. };
  1169. /**
  1170. * Adds a handler for showing a menu in the given element.
  1171. */
  1172. Menus.prototype.pickColor = function(key, cmd, defaultValue, defaultColor, defaultColorValue)
  1173. {
  1174. var ui = this.editorUi;
  1175. ui.tryAndHandle(mxUtils.bind(this, function()
  1176. {
  1177. var graph = ui.editor.graph;
  1178. var h = 226 + ((Math.ceil(ColorDialog.prototype.presetColors.length / 12) +
  1179. Math.ceil(ColorDialog.prototype.defaultColors.length / 12)) * 17);
  1180. if (cmd != null && graph.cellEditor.isContentEditing())
  1181. {
  1182. // Saves and restores text selection for in-place editor
  1183. var selState = graph.cellEditor.saveSelection();
  1184. var dlg = new ColorDialog(this.editorUi, defaultValue || graph.shapeForegroundColor,
  1185. mxUtils.bind(this, function(color)
  1186. {
  1187. graph.cellEditor.restoreSelection(selState);
  1188. document.execCommand(cmd, false, (color != mxConstants.NONE) ? color : 'transparent');
  1189. var cmdMapping = {
  1190. 'forecolor': mxConstants.STYLE_FONTCOLOR,
  1191. 'backcolor': mxConstants.STYLE_LABEL_BACKGROUNDCOLOR
  1192. };
  1193. var style = cmdMapping[cmd];
  1194. if (style != null)
  1195. {
  1196. ui.fireEvent(new mxEventObject('styleChanged',
  1197. 'keys', [style], 'values', [color],
  1198. 'cells', [graph.cellEditor.getEditingCell()]));
  1199. }
  1200. }), function()
  1201. {
  1202. graph.cellEditor.restoreSelection(selState);
  1203. });
  1204. this.editorUi.showDialog(dlg.container, 230, h, true, true);
  1205. dlg.init();
  1206. }
  1207. else
  1208. {
  1209. var state = graph.getView().getState(graph.getSelectionCell());
  1210. var color = mxConstants.NONE;
  1211. if (state != null)
  1212. {
  1213. color = state.style[key] || color;
  1214. }
  1215. if (defaultColor != null)
  1216. {
  1217. color = (/(^#?[a-zA-Z0-9]*$)/.test(color)) ? color : defaultColor;
  1218. this.editorUi.pickColor(color, ColorDialog.createApplyFunction(
  1219. this.editorUi, key), defaultColor, defaultColorValue);
  1220. }
  1221. else
  1222. {
  1223. if (this.colorDialog == null)
  1224. {
  1225. this.colorDialog = new ColorDialog(this.editorUi);
  1226. }
  1227. this.colorDialog.currentColorKey = key;
  1228. if (color == mxConstants.NONE)
  1229. {
  1230. color = graph.shapeBackgroundColor.substring(1);
  1231. this.colorDialog.picker.fromString(color);
  1232. this.colorDialog.colorInput.value = mxConstants.NONE;
  1233. }
  1234. else
  1235. {
  1236. this.colorDialog.picker.fromString(mxUtils.rgba2hex(color));
  1237. }
  1238. this.editorUi.showDialog(this.colorDialog.container, 230, h, true, true);
  1239. this.colorDialog.init();
  1240. }
  1241. }
  1242. }));
  1243. };
  1244. /**
  1245. * Adds a handler for showing a menu in the given element.
  1246. */
  1247. Menus.prototype.toggleStyle = function(key, defaultValue)
  1248. {
  1249. var graph = this.editorUi.editor.graph;
  1250. var value = graph.toggleCellStyles(key, defaultValue);
  1251. this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], 'values', [value],
  1252. 'cells', graph.getSelectionCells()));
  1253. };
  1254. /**
  1255. * Creates the keyboard event handler for the current graph and history.
  1256. */
  1257. Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label)
  1258. {
  1259. var action = this.editorUi.actions.get(key);
  1260. if (action != null && (menu.showDisabled || action.isEnabled()) && action.visible)
  1261. {
  1262. var item = menu.addItem(label || action.label, null, mxUtils.bind(this, function(evt)
  1263. {
  1264. try
  1265. {
  1266. action.funct(trigger, evt);
  1267. }
  1268. catch (e)
  1269. {
  1270. this.editorUi.handleError(e);
  1271. }
  1272. }), parent, sprite, action.isEnabled());
  1273. // Adds checkmark image
  1274. if (action.toggleAction && action.isSelected())
  1275. {
  1276. menu.addCheckmark(item, Editor.checkmarkImage);
  1277. }
  1278. this.addShortcut(item, action, menu.hideShortcuts);
  1279. return item;
  1280. }
  1281. return null;
  1282. };
  1283. /**
  1284. * Adds a checkmark to the given menuitem.
  1285. */
  1286. Menus.prototype.addShortcut = function(item, action, asTooltip)
  1287. {
  1288. if (action.shortcut != null)
  1289. {
  1290. if (asTooltip)
  1291. {
  1292. item.setAttribute('title', action.shortcut);
  1293. }
  1294. else
  1295. {
  1296. var td = item.firstChild.nextSibling.nextSibling;
  1297. var span = document.createElement('span');
  1298. span.style.color = 'gray';
  1299. mxUtils.write(span, action.shortcut);
  1300. td.appendChild(span);
  1301. }
  1302. }
  1303. };
  1304. /**
  1305. * Creates the keyboard event handler for the current graph and history.
  1306. */
  1307. Menus.prototype.addMenuItems = function(menu, keys, parent, trigger, sprites)
  1308. {
  1309. for (var i = 0; i < keys.length; i++)
  1310. {
  1311. if (keys[i] == '-')
  1312. {
  1313. menu.addSeparator(parent);
  1314. }
  1315. else
  1316. {
  1317. this.addMenuItem(menu, keys[i], parent, trigger, (sprites != null) ? sprites[i] : null);
  1318. }
  1319. }
  1320. };
  1321. /**
  1322. * Creates the keyboard event handler for the current graph and history.
  1323. */
  1324. Menus.prototype.createPopupMenu = function(menu, cell, evt)
  1325. {
  1326. menu.smartSeparators = true;
  1327. menu.hideShortcuts = true;
  1328. this.addPopupMenuItems(menu, cell, evt);
  1329. };
  1330. /**
  1331. * Creates the keyboard event handler for the current graph and history.
  1332. */
  1333. Menus.prototype.addPopupMenuItems = function(menu, cell, evt)
  1334. {
  1335. if (this.isShowHistoryItems())
  1336. {
  1337. this.addPopupMenuHistoryItems(menu, cell, evt);
  1338. }
  1339. this.addPopupMenuEditItems(menu, cell, evt);
  1340. if (this.isShowStyleItems())
  1341. {
  1342. this.addPopupMenuStyleItems(menu, cell, evt);
  1343. }
  1344. if (this.isShowArrangeItems())
  1345. {
  1346. this.addPopupMenuArrangeItems(menu, cell, evt);
  1347. }
  1348. this.addPopupMenuCellItems(menu, cell, evt);
  1349. this.addPopupMenuSelectionItems(menu, cell, evt);
  1350. };
  1351. /**
  1352. * Creates the keyboard event handler for the current graph and history.
  1353. */
  1354. Menus.prototype.isShowHistoryItems = function()
  1355. {
  1356. return true;
  1357. };
  1358. /**
  1359. * Creates the keyboard event handler for the current graph and history.
  1360. */
  1361. Menus.prototype.addPopupMenuHistoryItems = function(menu, cell, evt)
  1362. {
  1363. if (this.editorUi.editor.graph.isSelectionEmpty())
  1364. {
  1365. this.addMenuItems(menu, ['undo', 'redo'], null, evt);
  1366. }
  1367. };
  1368. /**
  1369. * Creates the keyboard event handler for the current graph and history.
  1370. */
  1371. Menus.prototype.addPopupDeleteItem = function(menu, cell, evt)
  1372. {
  1373. var item = this.addMenuItem(menu, 'delete');
  1374. if (item != null && item.firstChild != null &&
  1375. item.firstChild.nextSibling != null)
  1376. {
  1377. item.firstChild.nextSibling.style.color = 'red';
  1378. var graph = this.editorUi.editor.graph;
  1379. if (graph.getSelectionCount() > 1)
  1380. {
  1381. item.firstChild.nextSibling.innerHTML =
  1382. mxUtils.htmlEntities(mxResources.get('delete') +
  1383. ' (' + graph.getSelectionCount() + ')');
  1384. }
  1385. }
  1386. };
  1387. /**
  1388. * Creates the keyboard event handler for the current graph and history.
  1389. */
  1390. Menus.prototype.addPopupMenuEditItems = function(menu, cell, evt)
  1391. {
  1392. if (this.editorUi.editor.graph.isSelectionEmpty())
  1393. {
  1394. this.addMenuItems(menu, ['pasteHere'], null, evt);
  1395. }
  1396. else
  1397. {
  1398. this.addMenuItems(menu, ['cut', 'copy', 'duplicate',
  1399. '-', 'delete', 'lockUnlock'], null, evt);
  1400. }
  1401. };
  1402. /**
  1403. * Creates the keyboard event handler for the current graph and history.
  1404. */
  1405. Menus.prototype.isShowStyleItems = function()
  1406. {
  1407. return true;
  1408. };
  1409. /**
  1410. * Creates the keyboard event handler for the current graph and history.
  1411. */
  1412. Menus.prototype.addPopupMenuStyleItems = function(menu, cell, evt)
  1413. {
  1414. if (this.editorUi.editor.graph.getSelectionCount() == 1)
  1415. {
  1416. this.addMenuItems(menu, ['-', 'setAsDefaultStyle'], null, evt);
  1417. }
  1418. else if (this.editorUi.editor.graph.isSelectionEmpty())
  1419. {
  1420. this.addMenuItems(menu, ['-', 'clearDefaultStyle'], null, evt);
  1421. }
  1422. };
  1423. /**
  1424. * Creates the keyboard event handler for the current graph and history.
  1425. */
  1426. Menus.prototype.isShowArrangeItems = function()
  1427. {
  1428. return true;
  1429. };
  1430. /**
  1431. * Creates the keyboard event handler for the current graph and history.
  1432. */
  1433. Menus.prototype.addPopupMenuArrangeItems = function(menu, cell, evt)
  1434. {
  1435. var graph = this.editorUi.editor.graph;
  1436. // Shows group actions
  1437. if (graph.getSelectionCount() > 1 || (graph.getSelectionCount() > 0 &&
  1438. !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) &&
  1439. graph.getModel().getChildCount(cell) > 0 && graph.isCellEditable(cell)))
  1440. {
  1441. this.addMenuItems(menu, ['group', 'ungroup'], null, evt);
  1442. }
  1443. var count = graph.getEditableCells(graph.getSelectionCells()).length;
  1444. if (count > 1)
  1445. {
  1446. menu.addSeparator();
  1447. this.addSubmenu('align', menu);
  1448. this.addSubmenu('distribute', menu);
  1449. }
  1450. if (count >= 1)
  1451. {
  1452. this.addMenuItems(menu, ['-', 'toFront', 'toBack'], null, evt);
  1453. if (this.isShowCellEditItems() && graph.getSelectionCount() == 1)
  1454. {
  1455. this.addMenuItems(menu, ['bringForward', 'sendBackward'], null, evt);
  1456. }
  1457. }
  1458. };
  1459. /**
  1460. * Creates the keyboard event handler for the current graph and history.
  1461. */
  1462. Menus.prototype.addPopupMenuCellItems = function(menu, cell, evt)
  1463. {
  1464. var graph = this.editorUi.editor.graph;
  1465. var state = graph.view.getState(cell);
  1466. if (state != null)
  1467. {
  1468. // Adds reset waypoints option if waypoints exist
  1469. var geo = graph.getModel().getGeometry(cell);
  1470. var hasWaypoints = geo != null && geo.points != null && geo.points.length > 0;
  1471. if (this.isShowStyleItems() && graph.getSelectionCount() == 1 &&
  1472. graph.getModel().isEdge(cell))
  1473. {
  1474. menu.addSeparator();
  1475. this.addSubmenu('line', menu);
  1476. }
  1477. if (graph.getModel().isEdge(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) != 'entityRelationEdgeStyle' &&
  1478. mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) != 'arrow')
  1479. {
  1480. var handler = graph.selectionCellsHandler.getHandler(cell);
  1481. var isWaypoint = false;
  1482. if (hasWaypoints && handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2)
  1483. {
  1484. var index = handler.getHandleForEvent(graph.updateMouseEvent(new mxMouseEvent(evt)));
  1485. // Ignores ghosted and virtual waypoints
  1486. if (index > 0 && index < handler.bends.length - 1 &&
  1487. (handler.bends[index] == null ||
  1488. handler.bends[index].node == null ||
  1489. handler.bends[index].node.style.opacity == ''))
  1490. {
  1491. // Configures removeWaypoint action before execution
  1492. // Using trigger parameter is cleaner but have to find waypoint here anyway.
  1493. var rmWaypointAction = this.editorUi.actions.get('removeWaypoint');
  1494. rmWaypointAction.handler = handler;
  1495. rmWaypointAction.index = index;
  1496. isWaypoint = true;
  1497. }
  1498. }
  1499. if (this.isShowCellEditItems())
  1500. {
  1501. this.addMenuItem(menu, 'turn', null, evt, null, mxResources.get('reverse'));
  1502. }
  1503. this.addMenuItems(menu, [(isWaypoint) ? 'removeWaypoint' : 'addWaypoint'], null, evt);
  1504. }
  1505. if (graph.getSelectionCount() == 1 && this.isShowCellEditItems() &&
  1506. (hasWaypoints || (graph.getModel().isVertex(cell) &&
  1507. graph.getModel().getEdgeCount(cell) > 0)))
  1508. {
  1509. this.addMenuItems(menu, ['clearWaypoints'], null, evt);
  1510. }
  1511. if (this.isShowCellEditItems() &&
  1512. graph.getSelectionCount() == 1 &&
  1513. graph.isCellEditable(cell))
  1514. {
  1515. this.addPopupMenuCellEditItems(menu, cell, evt);
  1516. }
  1517. }
  1518. };
  1519. /**
  1520. * Creates the keyboard event handler for the current graph and history.
  1521. */
  1522. Menus.prototype.isShowCellEditItems = function()
  1523. {
  1524. return true;
  1525. };
  1526. /**
  1527. * Creates the keyboard event handler for the current graph and history.
  1528. */
  1529. Menus.prototype.addPopupMenuCellEditItems = function(menu, cell, evt, parent)
  1530. {
  1531. var graph = this.editorUi.editor.graph;
  1532. var state = graph.view.getState(cell);
  1533. this.addMenuItems(menu, ['-', 'editStyle', 'editData', 'editLink'], parent, evt);
  1534. // Shows edit image action if there is an image in the style
  1535. if (graph.getModel().isVertex(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_IMAGE, null) != null)
  1536. {
  1537. menu.addSeparator();
  1538. this.addMenuItem(menu, 'image', parent, evt).firstChild.nextSibling.innerHTML = mxResources.get('editImage') + '...';
  1539. this.addMenuItem(menu, 'crop', parent, evt);
  1540. }
  1541. if (graph.getModel().isVertex(cell) && graph.isCellConnectable(cell))
  1542. {
  1543. this.addMenuItem(menu, 'editConnectionPoints', parent, evt);
  1544. }
  1545. };
  1546. /**
  1547. * Creates the keyboard event handler for the current graph and history.
  1548. */
  1549. Menus.prototype.addPopupMenuSelectionItems = function(menu, cell, evt)
  1550. {
  1551. if (this.editorUi.editor.graph.isSelectionEmpty())
  1552. {
  1553. this.addMenuItems(menu, ['-', 'selectAll', 'selectVertices', 'selectEdges'], null, evt);
  1554. }
  1555. };
  1556. /**
  1557. * Creates the keyboard event handler for the current graph and history.
  1558. */
  1559. Menus.prototype.createMenubar = function(container)
  1560. {
  1561. var menubar = new Menubar(this.editorUi, container);
  1562. var menus = this.defaultMenuItems;
  1563. for (var i = 0; i < menus.length; i++)
  1564. {
  1565. (mxUtils.bind(this, function(menu)
  1566. {
  1567. var elt = menubar.addMenu(mxResources.get(menus[i]), mxUtils.bind(this, function()
  1568. {
  1569. // Allows extensions of menu.funct
  1570. menu.funct.apply(this, arguments);
  1571. }));
  1572. this.menuCreated(menu, elt);
  1573. }))(this.get(menus[i]));
  1574. }
  1575. return menubar;
  1576. };
  1577. /**
  1578. * Creates the keyboard event handler for the current graph and history.
  1579. */
  1580. Menus.prototype.menuCreated = function(menu, elt, className)
  1581. {
  1582. if (elt != null)
  1583. {
  1584. className = (className != null) ? className : 'geItem';
  1585. elt.className = className;
  1586. menu.addListener('stateChanged', function()
  1587. {
  1588. elt.enabled = menu.enabled;
  1589. if (!menu.enabled)
  1590. {
  1591. elt.className = className + ' mxDisabled';
  1592. if (document.documentMode == 8)
  1593. {
  1594. elt.style.color = '#c3c3c3';
  1595. }
  1596. }
  1597. else
  1598. {
  1599. elt.className = className;
  1600. if (document.documentMode == 8)
  1601. {
  1602. elt.style.color = '';
  1603. }
  1604. }
  1605. });
  1606. }
  1607. };
  1608. /**
  1609. * Construcs a new menubar for the given editor.
  1610. */
  1611. function Menubar(editorUi, container)
  1612. {
  1613. this.editorUi = editorUi;
  1614. this.container = container;
  1615. };
  1616. /**
  1617. * Adds the menubar elements.
  1618. */
  1619. Menubar.prototype.hideMenu = function()
  1620. {
  1621. this.editorUi.hideCurrentMenu();
  1622. };
  1623. /**
  1624. * Adds a submenu to this menubar.
  1625. */
  1626. Menubar.prototype.addMenu = function(label, funct, before, clickFn)
  1627. {
  1628. var elt = document.createElement('a');
  1629. elt.className = 'geItem';
  1630. mxUtils.write(elt, label);
  1631. this.addMenuHandler(elt, funct, clickFn);
  1632. if (before != null)
  1633. {
  1634. this.container.insertBefore(elt, before);
  1635. }
  1636. else
  1637. {
  1638. this.container.appendChild(elt);
  1639. }
  1640. return elt;
  1641. };
  1642. /**
  1643. * Adds a handler for showing a menu in the given element.
  1644. */
  1645. Menubar.prototype.addMenuHandler = function(elt, funct, clickFn)
  1646. {
  1647. if (funct != null)
  1648. {
  1649. var show = true;
  1650. var clickHandler = mxUtils.bind(this, function(evt)
  1651. {
  1652. if (clickFn != null)
  1653. {
  1654. clickFn(evt);
  1655. }
  1656. if (!mxEvent.isConsumed(evt) && show &&
  1657. (elt.enabled == null || elt.enabled))
  1658. {
  1659. this.editorUi.editor.graph.popupMenuHandler.hideMenu();
  1660. var menu = new mxPopupMenu(funct);
  1661. menu.div.className += ' geMenubarMenu';
  1662. menu.smartSeparators = true;
  1663. menu.showDisabled = true;
  1664. menu.autoExpand = true;
  1665. // Disables autoexpand and destroys menu when hidden
  1666. menu.hideMenu = mxUtils.bind(this, function()
  1667. {
  1668. mxPopupMenu.prototype.hideMenu.apply(menu, arguments);
  1669. this.editorUi.resetCurrentMenu();
  1670. menu.destroy();
  1671. });
  1672. var offset = mxUtils.getOffset(elt);
  1673. menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt);
  1674. this.editorUi.setCurrentMenu(menu, elt);
  1675. }
  1676. mxEvent.consume(evt);
  1677. });
  1678. // Shows menu automatically while in expanded state
  1679. mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt)
  1680. {
  1681. if (this.editorUi.menus.autoPopup && this.editorUi.currentMenu != null &&
  1682. this.editorUi.currentMenuElt != elt)
  1683. {
  1684. this.editorUi.hideCurrentMenu();
  1685. clickHandler(evt);
  1686. }
  1687. }));
  1688. // Hides menu if already showing and prevents focus
  1689. mxEvent.addListener(elt, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown',
  1690. mxUtils.bind(this, function(evt)
  1691. {
  1692. if (!this.editorUi.menus.autoPopup && this.editorUi.currentMenu != null &&
  1693. this.editorUi.currentMenuElt != elt && mxEvent.isMouseEvent(evt))
  1694. {
  1695. this.editorUi.hideCurrentMenu();
  1696. }
  1697. show = this.editorUi.currentMenu == null;
  1698. evt.preventDefault();
  1699. }));
  1700. mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt)
  1701. {
  1702. clickHandler(evt);
  1703. show = true;
  1704. }));
  1705. }
  1706. };
  1707. /**
  1708. * Creates the keyboard event handler for the current graph and history.
  1709. */
  1710. Menubar.prototype.destroy = function()
  1711. {
  1712. // do nothing
  1713. };
  1714. /**
  1715. * Constructs a new action for the given parameters.
  1716. */
  1717. function Menu(funct, enabled)
  1718. {
  1719. mxEventSource.call(this);
  1720. this.funct = funct;
  1721. this.enabled = (enabled != null) ? enabled : true;
  1722. };
  1723. // Menu inherits from mxEventSource
  1724. mxUtils.extend(Menu, mxEventSource);
  1725. /**
  1726. * Sets the enabled state of the action and fires a stateChanged event.
  1727. */
  1728. Menu.prototype.isEnabled = function()
  1729. {
  1730. return this.enabled;
  1731. };
  1732. /**
  1733. * Sets the enabled state of the action and fires a stateChanged event.
  1734. */
  1735. Menu.prototype.setEnabled = function(value)
  1736. {
  1737. if (this.enabled != value)
  1738. {
  1739. this.enabled = value;
  1740. this.fireEvent(new mxEventObject('stateChanged'));
  1741. }
  1742. };
  1743. /**
  1744. * Sets the enabled state of the action and fires a stateChanged event.
  1745. */
  1746. Menu.prototype.execute = function(menu, parent)
  1747. {
  1748. this.funct(menu, parent);
  1749. };
  1750. /**
  1751. * "Installs" menus in EditorUi.
  1752. */
  1753. EditorUi.prototype.createMenus = function()
  1754. {
  1755. return new Menus(this);
  1756. };