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