EditorUiController.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920
  1. /**
  2. *$id:Action 。JS,V 2017-12-19
  3. *$author shen.zhi
  4. */
  5. /**
  6. * 构建了一种新的图形编辑器
  7. * 编辑管理 实现方法管理
  8. */
  9. EditorUi = function(editor, container)
  10. {
  11. this.editor = editor || new Editor();
  12. this.container = container || document.body;
  13. var graph = editor.graph;
  14. //禁用滚动条
  15. this.container.style.overflow = 'hidden';
  16. var textEditing = mxUtils.bind(this, function(evt)
  17. {
  18. if (evt == null)
  19. {
  20. evt = window.event;
  21. }
  22. if (this.isSelectionAllowed(evt))
  23. {
  24. return true;
  25. }
  26. return graph.isEditing() || this.dialog != null;
  27. });
  28. //禁用文本选择而不是编辑没有对话框可见
  29. if (this.container == document.body)
  30. {
  31. document.onselectstart = textEditing;
  32. document.onmousedown = textEditing;
  33. }
  34. //使用内置的上下文菜单编辑时
  35. if (mxClient.IS_IE && document.documentMode != 9)
  36. {
  37. mxEvent.addListener(this.container, 'contextmenu', textEditing);
  38. }
  39. else
  40. {
  41. this.container.oncontextmenu = textEditing;
  42. }
  43. //图像预fetches子菜单
  44. new Image().src = mxPopupMenu.prototype.submenuImage;
  45. //预取连接图像
  46. if (mxConnectionHandler.prototype.connectImage != null)
  47. {
  48. new Image().src = mxConnectionHandler.prototype.connectImage.src;
  49. }
  50. //创建用户界面
  51. this.actions = new Actions(this);
  52. this.menus = new Menus(this);
  53. this.createDivs();
  54. this.refresh();
  55. this.createUi();
  56. //contains the given the inside main图审小组
  57. graph.init(this.diagramContainer);
  58. graph.refresh();
  59. //使容器的滚动条和设置光标样式
  60. graph.container.setAttribute('tabindex', '0');
  61. graph.container.style.overflow = (touchStyle) ? 'hidden' : 'auto';
  62. graph.container.style.cursor = 'default';
  63. graph.container.style.backgroundImage = 'url(' + IMAGE_PATH + '/grid.gif)';
  64. graph.container.focus();
  65. //保持图形容器集中在鼠标按下
  66. /*var graphFireMouseEvent = graph.fireMouseEvent;
  67. graph.fireMouseEvent = function(evtName, me, sender)
  68. {
  69. if (evtName == mxEvent.MOUSE_DOWN)
  70. {
  71. var da=$(this).html();
  72. this.container.focus();
  73. }
  74. graphFireMouseEvent.apply(this, arguments);
  75. };*/
  76. //在鼠标经过时自动扩展配置
  77. graph.panningHandler.autoExpand = true;
  78. //对上下文菜单
  79. graph.panningHandler.factoryMethod = mxUtils.bind(this, function(menu, cell, evt)
  80. {
  81. this.menus.createPopupMenu(menu, cell, evt);
  82. });
  83. //初始化轮廓
  84. editor.outline.init(this.outlineContainer);
  85. //隐藏菜单
  86. var md = (mxClient.IS_TOUCH) ? 'touchstart' : 'mousedown';
  87. mxEvent.addListener(document, md, mxUtils.bind(this, function(evt)
  88. {
  89. graph.panningHandler.hideMenu();
  90. }));
  91. //增加了手势操作(缩放)
  92. if (mxClient.IS_TOUCH)
  93. {
  94. mxEvent.addListener(graph.container, 'gesturechange',
  95. mxUtils.bind(this, function(evt)
  96. {
  97. graph.view.getDrawPane().setAttribute('transform', 'scale(' + evt.scale + ')');
  98. graph.view.getOverlayPane().style.visibility = 'hidden';
  99. })
  100. );
  101. mxEvent.addListener(graph.container, 'gestureend',
  102. mxUtils.bind(this, function(evt)
  103. {
  104. graph.view.getDrawPane().removeAttribute('transform');
  105. graph.zoomToCenter = true;
  106. graph.zoom(evt.scale);
  107. graph.view.getOverlayPane().style.visibility = 'visible';
  108. })
  109. );
  110. }
  111. // Create handler for key events
  112. var keyHandler = this.createKeyHandler(editor);
  113. // Getter for key handler
  114. this.getKeyHandler = function()
  115. {
  116. return keyHandler;
  117. };
  118. // Shows dialog if changes are lost
  119. window.onbeforeunload = function()
  120. {
  121. if (editor.modified)
  122. {
  123. //return mxResources.get('allChangesLost');
  124. }
  125. };
  126. // Updates the editor UI after the window has been resized
  127. mxEvent.addListener(window, 'resize', mxUtils.bind(this, function()
  128. {
  129. this.refresh();
  130. graph.sizeDidChange();
  131. this.editor.outline.update(false);
  132. this.editor.outline.outline.sizeDidChange();
  133. }));
  134. // Updates action and menu states
  135. this.init();
  136. this.open();
  137. };
  138. /**
  139. * Specifies the size of the split bar.
  140. */
  141. EditorUi.prototype.splitSize = (mxClient.IS_TOUCH) ? 16 : 8;
  142. /**
  143. * Specifies the height of the menubar. Default is 34.
  144. */
  145. EditorUi.prototype.menubarHeight = 34;
  146. /**
  147. * Specifies the height of the toolbar. Default is 46.
  148. */
  149. EditorUi.prototype.toolbarHeight = 46;
  150. /**
  151. * Specifies the height of the footer. Default is 28.
  152. */
  153. EditorUi.prototype.footerHeight = 5;
  154. /**
  155. * Specifies the position of the horizontal split bar. Default is 190.
  156. */
  157. EditorUi.prototype.hsplitPosition = 190;
  158. /**
  159. * Specifies the position of the vertical split bar. Default is 190.
  160. */
  161. EditorUi.prototype.vsplitPosition = 190;
  162. /**
  163. * Installs the listeners to update the action states.
  164. */
  165. EditorUi.prototype.init = function()
  166. {
  167. // Updates action states
  168. this.addUndoListener();
  169. this.addSelectionListener();
  170. // Overrides clipboard to update paste action state
  171. var paste = this.actions.get('paste');
  172. var updatePaste = function()
  173. {
  174. paste.setEnabled(!mxClipboard.isEmpty());
  175. };
  176. var mxClipboardCut = mxClipboard.cut;
  177. mxClipboard.cut = function()
  178. {
  179. mxClipboardCut.apply(this, arguments);
  180. updatePaste();
  181. };
  182. var mxClipboardCopy = mxClipboard.copy;
  183. mxClipboard.copy = function()
  184. {
  185. mxClipboardCopy.apply(this, arguments);
  186. updatePaste();
  187. };
  188. };
  189. /**
  190. * Hook for allowing selection and context menu for certain events.
  191. */
  192. EditorUi.prototype.isSelectionAllowed = function(evt)
  193. {
  194. if(evt&&(
  195. (evt.toElement&&evt.toElement.nodeName&&"SPAN"==evt.toElement.nodeName)
  196. ||(evt.type&&evt.type=="selectstart"&&evt.target&&evt.target.nodeName=="#text")
  197. )){
  198. return true;
  199. }
  200. return false;
  201. };
  202. /**
  203. * Opens the current diagram via the window.opener if one exists.
  204. */
  205. EditorUi.prototype.open = function()
  206. {
  207. // Cross-domain window access is not allowed in FF, so if we
  208. // were opened from another domain then this will fail.
  209. try
  210. {
  211. if (window.opener != null && window.opener.openFile != null)
  212. {
  213. window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml, filename)
  214. {
  215. try
  216. {
  217. var doc = mxUtils.parseXml(xml);
  218. this.editor.setGraphXml(doc.documentElement);
  219. this.editor.modified = false;
  220. this.editor.undoManager.clear();
  221. if (filename != null)
  222. {
  223. this.editor.filename = filename;
  224. }
  225. }
  226. catch (e)
  227. {
  228. mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message);
  229. }
  230. }));
  231. }
  232. }
  233. catch(e)
  234. {
  235. // ignore
  236. }
  237. };
  238. /**
  239. * 在给定的文件名保存当前图。
  240. */
  241. EditorUi.prototype.save = function()
  242. {
  243. var xml = mxUtils.getXml(this.editor.getGraphXml());
  244. //火狐浏览器
  245. //if (navigator.userAgent.indexOf('Firefox') >= 0){
  246. //}
  247. //xml="<mxGraphModel grid=\"0\" guides=\"1\" tooltips=\"1\" connect=\"1\" fold=\"1\" page=\"0\" pageScale=\"1\" pageWidth=\"826\" pageHeight=\"1169\">"+xml+"</mxGraphModel>"
  248. //将xml代码保存至服务器文件
  249. $.post($("#path").val()+"/SaveToXmlServlet",{"tp":$("#mapTp").val(),"xml":xml,"type":"set"},function(text){
  250. if(text=="0"){
  251. alert("保存失败!");
  252. }
  253. });
  254. };
  255. /**
  256. * 返回一个拷贝没有状态这个编辑器的URL。
  257. */
  258. EditorUi.prototype.getUrl = function(pathname)
  259. {
  260. var href = (pathname != null) ? pathname : window.location.pathname;
  261. var parms = (pathname.indexOf('?') > 0) ? 1 : 0;
  262. // Removes template URL parameter for new blank diagram
  263. for (var key in urlParams)
  264. {
  265. if (parms == 0)
  266. {
  267. href += '?';
  268. }
  269. else
  270. {
  271. href += '&';
  272. }
  273. href += key + '=' + urlParams[key];
  274. parms++;
  275. }
  276. return href;
  277. };
  278. /**
  279. * 更新的撤销/重做项的状态。
  280. */
  281. EditorUi.prototype.addUndoListener = function()
  282. {
  283. var undo = this.actions.get('undo');
  284. var redo = this.actions.get('redo');
  285. var undoMgr = this.editor.undoManager;
  286. var undoListener = function()
  287. {
  288. undo.setEnabled(undoMgr.canUndo());
  289. redo.setEnabled(undoMgr.canRedo());
  290. };
  291. undoMgr.addListener(mxEvent.ADD, undoListener);
  292. undoMgr.addListener(mxEvent.UNDO, undoListener);
  293. undoMgr.addListener(mxEvent.REDO, undoListener);
  294. undoMgr.addListener(mxEvent.CLEAR, undoListener);
  295. // Updates the button states once
  296. undoListener();
  297. };
  298. /**
  299. * Updates the states of the given toolbar items based on the selection.
  300. */
  301. EditorUi.prototype.addSelectionListener = function()
  302. {
  303. var selectionListener = mxUtils.bind(this, function()
  304. {
  305. var graph = this.editor.graph;
  306. var selected = !graph.isSelectionEmpty();
  307. var vertexSelected = false;
  308. var edgeSelected = false;
  309. var cells = graph.getSelectionCells();
  310. if (cells != null)
  311. {
  312. for (var i = 0; i < cells.length; i++)
  313. {
  314. var cell = cells[i];
  315. if (graph.getModel().isEdge(cell))
  316. {
  317. edgeSelected = true;
  318. }
  319. if (graph.getModel().isVertex(cell))
  320. {
  321. vertexSelected = true;
  322. }
  323. if (edgeSelected && vertexSelected)
  324. {
  325. break;
  326. }
  327. }
  328. }
  329. // 更新动作状态
  330. var actions = ['cut', 'copy', 'delete', 'duplicate', 'bold', 'italic', 'style', 'fillColor',
  331. 'gradientColor', 'underline', 'fontColor', 'strokeColor', 'backgroundColor',
  332. 'borderColor', 'toFront', 'toBack', 'dashed', 'rounded', 'shadow', 'rotate',
  333. 'autosize'];
  334. for (var i = 0; i < actions.length; i++)
  335. {
  336. this.actions.get(actions[i]).setEnabled(selected);
  337. }
  338. this.actions.get('rotation').setEnabled(vertexSelected);
  339. this.actions.get('group').setEnabled(graph.getSelectionCount() > 1);
  340. this.actions.get('ungroup').setEnabled(graph.getSelectionCount() == 1 &&
  341. graph.getModel().getChildCount(graph.getSelectionCell()) > 0);
  342. var oneVertexSelected = vertexSelected && graph.getSelectionCount() == 1;
  343. this.actions.get('removeFromGroup').setEnabled(oneVertexSelected &&
  344. graph.getModel().isVertex(graph.getModel().getParent(graph.getSelectionCell())));
  345. //更新菜单状态
  346. var menus = ['fontFamily', 'fontSize', 'alignment', 'position', 'text', 'format',
  347. 'arrange', 'linewidth', 'spacing', 'gradient'];
  348. for (var i = 0; i < menus.length; i++)
  349. {
  350. this.menus.get(menus[i]).setEnabled(selected);
  351. }
  352. menus = ['line', 'lineend', 'linestart'];
  353. for (var i = 0; i < menus.length; i++)
  354. {
  355. this.menus.get(menus[i]).setEnabled(edgeSelected);
  356. }
  357. this.actions.get('setAsDefaultEdge').setEnabled(edgeSelected);
  358. this.menus.get('align').setEnabled(graph.getSelectionCount() > 1);
  359. this.menus.get('direction').setEnabled(vertexSelected || (edgeSelected &&
  360. graph.isLoop(graph.view.getState(graph.getSelectionCell()))));
  361. this.menus.get('navigation').setEnabled(graph.foldingEnabled && ((graph.view.currentRoot != null) ||
  362. (graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell()))));
  363. this.actions.get('home').setEnabled(graph.view.currentRoot != null);
  364. this.actions.get('exitGroup').setEnabled(graph.view.currentRoot != null);
  365. var groupEnabled = graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell());
  366. this.actions.get('enterGroup').setEnabled(groupEnabled);
  367. this.actions.get('expand').setEnabled(groupEnabled);
  368. this.actions.get('collapse').setEnabled(groupEnabled);
  369. this.actions.get('editLink').setEnabled(graph.getSelectionCount() == 1);
  370. this.actions.get('openLink').setEnabled(graph.getSelectionCount() == 1 &&
  371. graph.getLinkForCell(graph.getSelectionCell()) != null);
  372. });
  373. this.editor.graph.getSelectionModel().addListener(mxEvent.CHANGE, selectionListener);
  374. selectionListener();
  375. };
  376. /**
  377. * Refreshes the viewport.
  378. */
  379. EditorUi.prototype.refresh = function()
  380. {
  381. var quirks = mxClient.IS_IE && (document.documentMode == null || document.documentMode == 5);
  382. var w = this.container.clientWidth;
  383. var h = this.container.clientHeight;
  384. if (this.container == document.body)
  385. {
  386. w = document.body.clientWidth || document.documentElement.clientWidth;
  387. h = (quirks) ? document.body.clientHeight || document.documentElement.clientHeight : document.documentElement.clientHeight;
  388. }
  389. var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize - 20));
  390. var effVsplitPosition = Math.max(0, Math.min(this.vsplitPosition, h - this.menubarHeight - this.toolbarHeight - this.footerHeight - this.splitSize - 1));
  391. this.menubarContainer.style.height = this.menubarHeight + 'px';
  392. this.toolbarContainer.style.top = this.menubarHeight + 'px';
  393. this.toolbarContainer.style.height = this.toolbarHeight + 'px';
  394. this.sidebarContainer.style.top = (this.menubarHeight + this.toolbarHeight) + 'px';
  395. this.sidebarContainer.style.width = effHsplitPosition + 'px';
  396. this.sidebarContainer.style.display = "none";
  397. this.outlineContainer.style.width = effHsplitPosition + 'px';
  398. this.outlineContainer.style.height = '80px';
  399. this.outlineContainer.style.right = '15px';
  400. this.outlineContainer.style.bottom = this.footerHeight + 'px';
  401. //this.diagramContainer.style.left = (effHsplitPosition + this.splitSize) + 'px';
  402. this.diagramContainer.style.top = this.sidebarContainer.style.top;
  403. this.footerContainer.style.height = this.footerHeight + 'px';
  404. this.hsplit.style.top = this.sidebarContainer.style.top;
  405. this.hsplit.style.bottom = this.outlineContainer.style.bottom;
  406. this.hsplit.style.right = effHsplitPosition + 'px';
  407. this.vsplit.style.width = this.sidebarContainer.style.width;
  408. this.vsplit.style.bottom = (effVsplitPosition + this.footerHeight) + 'px';
  409. if (quirks)
  410. {
  411. this.menubarContainer.style.width = w + 'px';
  412. this.toolbarContainer.style.width = this.menubarContainer.style.width;
  413. var sidebarHeight = (h - effVsplitPosition - this.splitSize - this.footerHeight - this.menubarHeight - this.toolbarHeight);
  414. this.sidebarContainer.style.height = sidebarHeight + 'px';
  415. this.diagramContainer.style.width = (w - effHsplitPosition - this.splitSize) + 'px';
  416. var diagramHeight = (h - this.footerHeight - this.menubarHeight - this.toolbarHeight);
  417. this.diagramContainer.style.height = diagramHeight + 'px';
  418. this.footerContainer.style.width = this.menubarContainer.style.width;
  419. this.hsplit.style.height = diagramHeight + 'px';
  420. }
  421. else
  422. {
  423. this.sidebarContainer.style.bottom = (effVsplitPosition + this.splitSize + this.footerHeight) + 'px';
  424. this.diagramContainer.style.bottom = this.outlineContainer.style.bottom;
  425. }
  426. this.hsplit.style.display = "none";
  427. this.vsplit.style.display = "none";
  428. //this.menubarContainer.style.display = "none";
  429. };
  430. /**
  431. * Creates the required containers.
  432. */
  433. EditorUi.prototype.createDivs = function()
  434. {
  435. this.menubarContainer = this.createDiv('geMenubarContainer');
  436. this.toolbarContainer = this.createDiv('geToolbarContainer');
  437. this.sidebarContainer = this.createDiv('geSidebarContainer');
  438. this.outlineContainer = this.createDiv('geOutlineContainer');
  439. this.diagramContainer = this.createDiv('geDiagramContainer');
  440. this.footerContainer = this.createDiv('geFooterContainer');
  441. this.hsplit = this.createDiv('geHsplit');
  442. this.vsplit = this.createDiv('geVsplit');
  443. // Sets static style for containers
  444. this.menubarContainer.style.top = '0px';
  445. this.menubarContainer.style.left = '0px';
  446. this.menubarContainer.style.right = '0px';
  447. this.toolbarContainer.style.left = '0px';
  448. this.toolbarContainer.style.right = '0px';
  449. this.sidebarContainer.style.right = '0px';
  450. this.outlineContainer.style.right = '0px';
  451. this.diagramContainer.style.left = '0px';
  452. this.footerContainer.style.left = '0px';
  453. this.footerContainer.style.right = '0px';
  454. this.footerContainer.style.bottom = '0px';
  455. this.vsplit.style.right = '0px';
  456. this.diagramContainer.style.right = '198px';
  457. this.vsplit.style.height = this.splitSize + 'px';
  458. //this.hsplit.style.right = '190px';
  459. this.hsplit.style.width = this.splitSize + 'px';
  460. };
  461. /**
  462. * Creates the required containers.
  463. */
  464. EditorUi.prototype.createUi = function()
  465. {
  466. // Creates menubar
  467. this.menubar = this.menus.createMenubar(this.createDiv('geMenubar'));
  468. //this.menubarContainer.appendChild(this.menubar.container);
  469. // Creates toolbar
  470. this.toolbar = this.createToolbar(this.createDiv('geToolbar'));
  471. this.toolbarContainer.appendChild(this.toolbar.container);
  472. // Creates the sidebar
  473. this.sidebar = this.createSidebar(this.sidebarContainer);
  474. // Creates the footer
  475. this.footerContainer.appendChild(this.createFooter());
  476. // Adds status bar in menubar
  477. this.statusContainer = this.createStatusContainer();
  478. // Connects the status bar to the editor status
  479. this.editor.addListener('statusChanged', mxUtils.bind(this, function()
  480. {
  481. this.setStatusText(this.editor.getStatus());
  482. }));
  483. this.setStatusText(this.editor.getStatus());
  484. this.menubar.container.appendChild(this.statusContainer);
  485. this.sidebarContainer.innerHTML="";//sidebardata;
  486. // Inserts into DOM
  487. this.container.appendChild(this.menubarContainer);
  488. this.container.appendChild(this.toolbarContainer);
  489. this.container.appendChild(this.sidebarContainer);
  490. this.container.appendChild(this.outlineContainer);
  491. this.container.appendChild(this.diagramContainer);
  492. this.container.appendChild(this.footerContainer);
  493. this.container.appendChild(this.hsplit);
  494. this.container.appendChild(this.vsplit);
  495. // HSplit
  496. this.addSplitHandler(this.hsplit, true, 0, mxUtils.bind(this, function(value)
  497. {
  498. this.hsplitPosition = value;
  499. this.refresh();
  500. this.editor.graph.sizeDidChange();
  501. this.editor.outline.update(false);
  502. this.editor.outline.outline.sizeDidChange();
  503. }));
  504. // VSplit
  505. this.addSplitHandler(this.vsplit, false, this.footerHeight, mxUtils.bind(this, function(value)
  506. {
  507. this.vsplitPosition = value;
  508. this.refresh();
  509. this.editor.outline.update(false);
  510. this.editor.outline.outline.sizeDidChange();
  511. }));
  512. };
  513. /**
  514. * Creates a new toolbar for the given container.
  515. */
  516. EditorUi.prototype.createStatusContainer = function()
  517. {
  518. var container = document.createElement('a');
  519. container.className = 'geItem geStatus';
  520. return container;
  521. };
  522. /**
  523. * Creates a new toolbar for the given container.
  524. */
  525. EditorUi.prototype.setStatusText = function(value)
  526. {
  527. this.statusContainer.innerHTML = value;
  528. };
  529. /**
  530. * Creates a new toolbar for the given container.
  531. */
  532. EditorUi.prototype.createToolbar = function(container)
  533. {
  534. return new Toolbar(this, container);
  535. };
  536. /**
  537. * Creates a new sidebar for the given container.
  538. */
  539. EditorUi.prototype.createSidebar = function(container)
  540. {
  541. return new Sidebar(this, container);
  542. };
  543. /**
  544. * Creates and returns a new footer.
  545. */
  546. EditorUi.prototype.createFooter = function()
  547. {
  548. return this.createDiv('geFooter');
  549. };
  550. /**
  551. * Creates the actual toolbar for the toolbar container.
  552. */
  553. EditorUi.prototype.createDiv = function(classname)
  554. {
  555. var elt = document.createElement('div');
  556. elt.className = classname;
  557. return elt;
  558. };
  559. /**
  560. * Updates the states of the given undo/redo items.
  561. */
  562. EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange)
  563. {
  564. var start = null;
  565. var initial = null;
  566. function getValue()
  567. {
  568. return parseInt(((horizontal) ? elt.style.left : elt.style.bottom));
  569. }
  570. var md = (mxClient.IS_TOUCH) ? 'touchstart' : 'mousedown';
  571. var mm = (mxClient.IS_TOUCH) ? 'touchmove' : 'mousemove';
  572. var mu = (mxClient.IS_TOUCH) ? 'touchend' : 'mouseup';
  573. mxEvent.addListener(elt, md, function(evt)
  574. {
  575. start = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  576. initial = getValue();
  577. mxEvent.consume(evt);
  578. });
  579. function moveHandler(evt)
  580. {
  581. if (start != null)
  582. {
  583. var pt = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt));
  584. onChange(Math.max(0, initial + ((horizontal) ? (pt.x - start.x) : (start.y - pt.y)) - dx));
  585. mxEvent.consume(evt);
  586. }
  587. }
  588. mxEvent.addListener(document, mm, moveHandler);
  589. mxEvent.addListener(document, mu, function(evt)
  590. {
  591. moveHandler(evt);
  592. start = null;
  593. initial = null;
  594. });
  595. };
  596. /**
  597. * Displays a print dialog.
  598. */
  599. EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose)
  600. {
  601. this.hideDialog();
  602. this.dialog = new Dialog(this, elt, w, (mxClient.IS_VML) ? h - 12 : h, modal, closable, onClose);
  603. };
  604. /**
  605. * Displays a print dialog.
  606. */
  607. EditorUi.prototype.hideDialog = function()
  608. {
  609. if (this.dialog != null)
  610. {
  611. this.dialog.close();
  612. this.dialog = null;
  613. this.editor.graph.container.focus();
  614. }
  615. };
  616. /**
  617. * Adds the label menu items to the given menu and parent.
  618. */
  619. EditorUi.prototype.openFile = function()
  620. {
  621. // Closes dialog after open
  622. window.openFile = new OpenFile(mxUtils.bind(this, function()
  623. {
  624. this.hideDialog();
  625. }));
  626. // Removes openFile if dialog is closed
  627. this.showDialog(new OpenDialog(this).container, 300, 180, true, true, function()
  628. {
  629. window.openFile = null;
  630. });
  631. };
  632. /**
  633. * Adds the label menu items to the given menu and parent.
  634. */
  635. EditorUi.prototype.saveFile = function(forceDialog)
  636. {
  637. if (!forceDialog && this.editor.filename != null)
  638. {
  639. this.save(this.editor.getOrCreateFilename());
  640. }
  641. else
  642. {
  643. this.showDialog(new SaveDialog(this).container, 300, 100, true, true);
  644. }
  645. };
  646. /**
  647. * Executes the given layout.
  648. */
  649. EditorUi.prototype.executeLayout = function(layout, animate, ignoreChildCount)
  650. {
  651. var graph = this.editor.graph;
  652. var cell = graph.getSelectionCell();
  653. graph.getModel().beginUpdate();
  654. try
  655. {
  656. layout.execute(graph.getDefaultParent(), cell);
  657. }
  658. catch (e)
  659. {
  660. throw e;
  661. }
  662. finally
  663. {
  664. // Animates the changes in the graph model except
  665. // for Camino, where animation is too slow
  666. if (animate && navigator.userAgent.indexOf('Camino') < 0)
  667. {
  668. // New API for animating graph layout results asynchronously
  669. var morph = new mxMorphing(graph);
  670. morph.addListener(mxEvent.DONE, mxUtils.bind(this, function()
  671. {
  672. graph.getModel().endUpdate();
  673. }));
  674. morph.startAnimation();
  675. }
  676. else
  677. {
  678. graph.getModel().endUpdate();
  679. }
  680. }
  681. };
  682. /**
  683. * Creates the keyboard event handler for the current graph and history.
  684. */
  685. EditorUi.prototype.createKeyHandler = function(editor)
  686. {
  687. var graph = this.editor.graph;
  688. var keyHandler = new mxKeyHandler(graph);
  689. // Routes command-key to control-key on Mac
  690. keyHandler.isControlDown = function(evt)
  691. {
  692. return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey);
  693. };
  694. // Helper function to move cells with the cursor keys
  695. function nudge(keyCode)
  696. {
  697. if (!graph.isSelectionEmpty())
  698. {
  699. var dx = 0;
  700. var dy = 0;
  701. if (keyCode == 37)
  702. {
  703. dx = -1;
  704. }
  705. else if (keyCode == 38)
  706. {
  707. dy = -1;
  708. }
  709. else if (keyCode == 39)
  710. {
  711. dx = 1;
  712. }
  713. else if (keyCode == 40)
  714. {
  715. dy = 1;
  716. }
  717. graph.moveCells(graph.getSelectionCells(), dx, dy);
  718. graph.scrollCellVisible(graph.getSelectionCell());
  719. }
  720. };
  721. // Binds keystrokes to actions
  722. var bindAction = mxUtils.bind(this, function(code, control, key, shift)
  723. {
  724. var action = this.actions.get(key);
  725. if (action != null)
  726. {
  727. var f = function()
  728. {
  729. if (action.enabled)
  730. {
  731. action.funct();
  732. }
  733. };
  734. if (control)
  735. {
  736. if (shift)
  737. {
  738. keyHandler.bindControlShiftKey(code, f);
  739. }
  740. else
  741. {
  742. keyHandler.bindControlKey(code, f);
  743. }
  744. }
  745. else
  746. {
  747. if (shift)
  748. {
  749. keyHandler.bindShiftKey(code, f);
  750. }
  751. else
  752. {
  753. keyHandler.bindKey(code, f);
  754. }
  755. }
  756. }
  757. });
  758. var ui = this;
  759. var keyHandleEscape = keyHandler.escape;
  760. keyHandler.escape = function(evt)
  761. {
  762. ui.hideDialog();
  763. keyHandleEscape.apply(this, arguments);
  764. };
  765. // Ignores enter keystroke. Remove this line if you want the
  766. // enter keystroke to stop editing.
  767. keyHandler.enter = function() {};
  768. keyHandler.bindKey(8, function() { graph.foldCells(true); }); // Backspace
  769. keyHandler.bindKey(13, function() { graph.foldCells(false); }); // Enter
  770. keyHandler.bindKey(33, function() { graph.exitGroup(); }); // Page Up
  771. keyHandler.bindKey(34, function() { graph.enterGroup(); }); // Page Down
  772. keyHandler.bindKey(36, function() { graph.home(); }); // Home
  773. keyHandler.bindKey(35, function() { graph.refresh(); }); // End
  774. keyHandler.bindKey(37, function() { nudge(37); }); // Left arrow
  775. keyHandler.bindKey(38, function() { nudge(38); }); // Up arrow
  776. keyHandler.bindKey(39, function() { nudge(39); }); // Right arrow
  777. keyHandler.bindKey(40, function() { nudge(40); }); // Down arrow
  778. keyHandler.bindKey(113, function() { graph.startEditingAtCell(); });
  779. bindAction(46, false, 'delete'); // Delete
  780. bindAction(82, true, 'rotate'); // Ctrl+R
  781. // bindAction(83, true, 'save'); // Ctrl+S
  782. // bindAction(83, true, 'saveAs', true); // Ctrl+Shift+S
  783. bindAction(107, false, 'zoomIn'); // Add
  784. bindAction(109, false, 'zoomOut'); // Subtract
  785. bindAction(65, true, 'selectAll'); // Ctrl+A
  786. bindAction(86, true, 'selectVertices', true); // Ctrl+Shift+V
  787. bindAction(69, true, 'selectEdges', true); // Ctrl+Shift+E
  788. bindAction(69, true, 'export'); // Ctrl+Shift+E
  789. bindAction(66, true, 'toBack'); // Ctrl+B
  790. bindAction(70, true, 'toFront'); // Ctrl+F
  791. bindAction(68, true, 'duplicate'); // Ctrl+D
  792. bindAction(90, true, 'undo'); // Ctrl+Z
  793. bindAction(89, true, 'redo'); // Ctrl+Y
  794. bindAction(88, true, 'cut'); // Ctrl+X
  795. bindAction(67, true, 'copy'); // Ctrl+C
  796. bindAction(81, true, 'connect'); // Ctrl+Q
  797. bindAction(86, true, 'paste'); // Ctrl+V
  798. bindAction(71, true, 'group'); // Ctrl+G
  799. bindAction(71, true, 'grid', true); // Ctrl+Shift+G
  800. bindAction(85, true, 'ungroup'); // Ctrl+U
  801. bindAction(112, false, 'about'); // F1
  802. return keyHandler;
  803. };