1 /**This is the main JavaScript module. 2 *We move it here so it will not clutter the index.php with a lot of JavaScript 3 * 4 *ALL VARIABLES DEFINED HERE WILL BE VISIBLE IN ALL OTHER MODULES AND INSIDE INDEX.PHP 5 *SO TAKE CARE! 6 **/ 7 8 /** 9 *Thinking to use name spaces more 10 *TODO: avoid colision 11 *@see http://michaux.ca/articles/javascript-namespacing 12 *@see http://www.dustindiaz.com/namespace-your-javascript/ 13 **/ 14 var testNS = {}; 15 testNS.state = 1; 16 17 /**Activate or deactivate the undo feature**/ 18 var doUndo = true; 19 20 /**Usually an instance of a Command (see /lib/commands/*.js)*/ 21 var currentMoveUndo = null; 22 23 var CONNECTOR_MANAGER = new ConnectorManager(); 24 var GRIDWIDTH = 20; 25 var fillColor=null; 26 var strokeColor='#000000'; 27 var currentText=null; 28 var FIGURE_ESCAPE_DISTANCE = 20; /**the distance by which the connectors will escape Figure's bounds*/ 29 30 /*It will store a reference to the function that will create a figure( ex: figureForKids:buildFigure3()) will be stored into this 31 *variable so upon click on canvas this function will create the object*/ 32 var createFigureFunction = null; 33 34 /**A variable that will tell us if we are in IE*/ 35 var IE = false; 36 37 /**A variable that tells us if CTRL is pressed*/ 38 var CNTRL_PRESSED = false; 39 40 /**A variable that tells us if SHIFT is pressed*/ 41 var SHIFT_PRESSED = false; 42 43 /**Current connector. It is null if no connector selected*/ 44 var connector = null; 45 46 /**Connector type*/ 47 var connectorType = ''; 48 49 document.onselectstart=stopselection; 50 51 52 53 /**Supposelly stop any selection from happening*/ 54 function stopselection(ev){ 55 if(!ev){ 56 ev = window.event; 57 } 58 //If we are selecting text within anything with the className text, we allow it 59 //This gives us the option of using textareas, inputs and any other item we 60 //want to allow text selection in. 61 if((IE && ev.srcElement.className == "text") /*IE code*/ 62 || (!IE && ev.target.className == "text") /*Non IE code*/){ 63 return true; 64 } 65 return false; 66 67 } 68 var stack = new Stack(); 69 70 71 /**keeps track if the MLB is pressed*/ 72 var mousePressed = false; 73 74 /**the default application state*/ 75 var STATE_NONE = 0; 76 77 /**we have figure to be created**/ 78 var STATE_FIGURE_CREATE = 1; 79 80 /**we selected a figure (for further editing for example)*/ 81 var STATE_FIGURE_SELECTED = 2; 82 83 /**we are selecting the start of a connector*/ 84 var STATE_CONNECTOR_PICK_FIRST = 4; 85 86 /**we are selecting the end of a connector*/ 87 var STATE_CONNECTOR_PICK_SECOND = 8; 88 89 /**we selected a connector (for further editing for example)*/ 90 var STATE_CONNECTOR_SELECTED = 16; 91 92 /**move a connection point of a connector*/ 93 var STATE_CONNECTOR_MOVE_POINT = 32; 94 95 /**we are dragging the mouse over a group of figures.*/ 96 var STATE_SELECTING_MULTIPLE = 64; 97 98 /**we have a group selected (either temporary or permanent)*/ 99 var STATE_GROUP_SELECTED = 128; 100 101 /**Keeps current state*/ 102 var state = STATE_NONE; 103 104 /**The (current) selection area*/ 105 var selectionArea = new Polygon(); 106 selectionArea.points.push(new Point(0,0)); 107 selectionArea.points.push(new Point(0,0)); 108 selectionArea.points.push(new Point(0,0)); 109 selectionArea.points.push(new Point(0,0)); 110 selectionArea.style.strokeStyle = 'grey'; 111 selectionArea.style.lineWidth = '1'; 112 113 /**Toggle grid visible or not*/ 114 var gridVisible = false; 115 116 /**Makes figure snap to grid*/ 117 var snapTo = false; 118 119 /**Keeps last coodinates while dragging*/ 120 var lastClick = []; 121 122 /**Default line width*/ 123 var defaultLineWidth = 2; 124 125 /**Current selected figure id ( -1 if none selected)*/ 126 var selectedFigureId = -1; 127 128 /**Current selected group (-1 if none selected)*/ 129 var selectedGroupId = -1; 130 131 /**Current selecte connector (-1 if none selected)*/ 132 var selectedConnectorId = -1; 133 134 /**Currently selected ConnectionPoint (if -1 none is selected)*/ 135 var selectedConnectionPointId = -1; 136 137 /**Set on true while we drag*/ 138 var dragging = false; 139 140 /**Holds a wrapper around canvas object*/ 141 var canvasProps = null; // 142 143 144 145 /**Return current canvas. 146 * Current canvas will ALWAYS have the 'a' as DOM id 147 * @author Alex Gheorghiu <alex@scriptoid.com> 148 **/ 149 function getCanvas(){ 150 var canvas = document.getElementById("a"); 151 return canvas; 152 } 153 154 155 /**Return the 2D context of current canvas 156 * @author Alex Gheorghiu <alex@scriptoid.com> 157 **/ 158 function getContext(){ 159 var canvas = getCanvas(); 160 if(canvas.getContext){ 161 return canvas.getContext("2d"); 162 } 163 else{ 164 alert('You need Safari or Firefox 1.5+ to see this demo.'); 165 } 166 } 167 168 /**Keeps current figure set id*/ 169 var currentSetId = 'basic'; 170 171 /** 172 * Reveals the figure set named by 'name' and hide previously displayed set 173 * @param {String} id - the (div) id value of the set 174 * @author Alex 175 **/ 176 function setFigureSet(id){ 177 //alert(name); 178 var div = document.getElementById(id); 179 if(div != null){ 180 if(currentSetId != null){ 181 var currentFigureSet = document.getElementById(currentSetId); 182 currentFigureSet.style.display = 'none'; 183 } 184 div.style.display = 'block'; 185 currentSetId = id; 186 } 187 } 188 189 190 /**Update an object (Figure or Connector) 191 *@param {Number} figureId - the id of the updating object 192 *@param {String} property - (or an {Array} of {String}s). The 'id' under which the property is stored 193 *TODO: is there any case where we are using property as an array ? 194 *@param {String} newValue - the new value of the property 195 *@author Zack, Alex 196 **/ 197 function updateFigure(figureId, property, newValue){ 198 Log.info("updateFigure() figureId: " + figureId + " property: " + property + ' new value: ' + newValue); 199 200 /*Try to guess the object type*/ 201 var objType = null; 202 var obj = stack.figureGetById(figureId); //try to find it inside {Figure}s 203 204 if(obj){ //try to find it inside {Connector}s 205 objType = History.OBJECT_FIGURE; 206 } 207 else{ //no in Figures 208 obj = CONNECTOR_MANAGER.connectorGetById(figureId); //search in Connectors 209 Log.info("updateFigure(): it's a connector 1"); 210 211 if(obj){ //see if it's a Canvas 212 objType = History.OBJECT_CONNECTOR; 213 } 214 else{ //no in connectors 215 if(figureId == "canvasProps"){ 216 obj = canvasProps; 217 Log.info("updateFigure(): it's the canvas"); 218 } 219 } 220 } 221 222 223 224 var objSave = obj; //keep a reference to initial shape 225 226 /*Example of property 'primitives.1.str' */ 227 var props = property.split("."); 228 //Log.info("Splitted props: " + props); 229 230 231 /*Going down the object's hierarchy down to the property's parent 232 *Example: 233 * for props = ['primitives','1','str'] 234 * figure 235 * |_primitives 236 * |_1 (it's a Text) 237 * |_str 238 */ 239 //Log.info("Object before descend: " + obj.oType); 240 var figure = obj; //TODO: Why this variable when we already have objSave? 241 for(var i = 0; i<props.length-1; i++){ 242 obj = obj[props[i]]; 243 } 244 245 //the property name 246 var propName = props[props.length -1]; 247 Log.info("updateFigure(): last property: " + propName); 248 Log.info("updateFigure(): last object in hierarchy: " + obj.oType); 249 250 251 252 /*if last prop is null */ 253 if(obj[propName] === undefined){ 254 /*If something is complicated enough to need a function, 255 * likelyhood is it will need access to its parent figure. 256 *So we will let the parent to do the update as it likes if it has 257 * a method of form set<property_name> in place 258 */ 259 var propSet = "set" + props[props.length -1]; 260 if(propSet in obj){ //@see https://developer.mozilla.org/en/JavaScript/Reference/Operators/Special_Operators/in_Operator 261 if(doUndo && obj["get"+propName]() != newValue){ 262 var undo = new PropertyCommand(figureId, objType, property, obj["get"+propName](), newValue) 263 History.addUndo(undo); 264 } 265 obj[propSet](figure,newValue); 266 } 267 } 268 else{ 269 if(obj[propName] != newValue){ //try to change it ONLY if new value is different than the last one 270 if(doUndo && obj[propName] != newValue){ 271 var undo = new PropertyCommand(figureId, objType, property, obj[propName], newValue) 272 History.addUndo(undo); 273 } 274 obj[propName] = newValue; 275 } 276 } 277 278 //connector's text special case 279 if(objSave instanceof Connector && propName == 'str'){ 280 Log.info("updateFigure(): it's a connector 2"); 281 objSave.updateMiddleText(); 282 } 283 284 draw(); 285 } 286 287 288 /**Setup the editor panel for a special shape. 289 *@param shape - can be either Connector or Figure. If null is provided the 290 *editor panel will be disabled 291 **/ 292 function setUpEditPanel(shape){ 293 //var propertiesPanel = canvas.edit; //access the edit div 294 var propertiesPanel = document.getElementById("edit"); 295 propertiesPanel.innerHTML = ""; 296 if(shape == null){ 297 //do nothing 298 } 299 else{ 300 switch(shape.oType){ 301 case 'Group': 302 //do nothing. We do not want to offer this to groups 303 break; 304 case 'CanvasProps': 305 Builder.constructCanvasPropertiesPanel(propertiesPanel, shape); 306 break; 307 default: 308 Builder.contructPropertiesPanel(propertiesPanel, shape); 309 } 310 } 311 } 312 313 314 /**Setup the creation function (that -later, upon calling - will create the actual {Figure} 315 * Note: It will also set the current state to STATE_FIGURE_CREATE 316 * @param {Function} fFunction - the function used to create the figure 317 **/ 318 function createFigure(fFunction){ 319 //alert("createFigure() - You ask me to create a figure? How dare you?"); 320 createFigureFunction = fFunction; 321 322 selectedFigureId = -1; 323 selectedConnectorId = -1; 324 selectedConnectionPointId = -1; 325 state = STATE_FIGURE_CREATE; 326 draw(); 327 328 } 329 330 /**Activate snapToGrip option*/ 331 function snapToGrid(){ 332 if(gridVisible == false && snapTo == false){ 333 showGrid(); 334 } 335 snapTo =! snapTo; 336 document.getElementById("snapCheckbox").checked=snapTo; 337 } 338 339 340 /**Makes grid visible or invisible, depedinding of previous value*/ 341 function showGrid(){ 342 var canvas = getCanvas(); 343 gridVisible = !gridVisible; 344 345 if(gridVisible){ 346 canvas.style.backgroundImage="url(assets/images/gridTile1.png)"; 347 } 348 else { 349 canvas.style.backgroundImage=""; 350 document.getElementById("snapCheckbox").checked = false; 351 } 352 document.getElementById("gridCheckbox").checked = gridVisible; 353 } 354 355 356 /**Click is disabled because we need to handle mouse down and mouse up....etc etc etc*/ 357 function onClick(ev){ 358 var coords = getCanvasXY(ev); 359 var x = coords[0]; 360 var y = coords[1]; 361 362 //here is the problem....how do we know we clicked on canvas 363 /*var fig=stack.figures[stack.figureGetMouseOver(x,y,null)]; 364 if(CNTRL_PRESSED && fig!=null){ 365 TEMPORARY_GROUP.addPrimitive(); 366 stack.figureRemove(fig); 367 stack.figureAdd(TEMPORARY_GROUP); 368 } 369 else if(stack.figureGetMouseOver(x,y,null)!=null){ 370 TEMPORARY_GROUP.primitives=[]; 371 TEMPORARY_GROUP.addPrimitive(fig); 372 stack.figureRemove(fig); 373 }*/ 374 //draw(); 375 } 376 377 378 /**Receives the ASCII character code but not the keyboard code 379 *@param {Event} ev - the event generated when kay is pressed 380 *@see <a href="http://www.quirksmode.org/js/keys.html">http://www.quirksmode.org/js/keys.html</a> 381 **/ 382 function onKeyPress(ev){ 383 if(!ev){ //get event for IE 384 ev = window.event; 385 } 386 387 //ignore texts 388 if((IE && ev.srcElement.className == "text") //IE code. In IE the ev has srcElement member 389 || (!IE && ev.target.className == "text")/*non IE code. In normal JS the ev has the target member*/){ 390 return; 391 } 392 393 switch(ev.charCode){ 394 case KEY.NUMPAD4: //Numpad 4 395 if(CNTRL_PRESSED && stack.figureGetSelected()){ //clone a figure 396 /*TODO: right now the newly created figure belongs to the Group with Id = -1 (supposelly no group) 397 *So this might not be a good ideea 398 **/ 399 400 /*Creates a clone of currently selected figure and 401 * move it a little right (10 pixels) and down (10 pixels) 402 * */ 403 var createdFigure = stack.figureGetSelected().clone(); 404 stack.figureAdd(createdFigure); 405 stack.figureSelect(stack.figures.length-1); 406 createdFigure.transform(Matrix.translationMatrix(10,10)); 407 getCanvas().style.cursor="default"; 408 } 409 break; 410 }//end switch 411 412 413 draw(); 414 return false; 415 } 416 417 418 /** 419 *Receives the key code of keyboard but not the ASCII 420 *Treats the key pressed event 421 *@param {Event} ev - the event generated when key is down 422 *@see <a href="http://www.quirksmode.org/js/keys.html">http://www.quirksmode.org/js/keys.html</a> 423 **/ 424 function onKeyDown(ev){ 425 426 Log.info("main.js->onKeyDown()->function call. Event = " + ev.type + " IE = " + IE ); 427 428 //1 - OLD IE browsers 429 if(typeof ev == 'undefined' || ev == null){ 430 ev = window.event; 431 } 432 433 //2 - avoid text elements (if you are on a text area and you press the arrow you do not want the figures to move on canvas) 434 if((IE && ev.srcElement.className == "text") /*IE code*/ 435 || (!IE && ev.target.className == "text") /*Non IE code*/){ 436 return true; 437 } 438 439 440 //3 - "enhance" event TODO: I'm not sure this is really necessary 441 ev.KEY = ev.keyCode; 442 443 444 Log.info("e.keyCode = " + ev.keyCode + " ev.KEY = " + ev.KEY); 445 446 447 switch(ev.KEY){ 448 case KEY.ESCAPE: //Esc 449 //alert('So do you want to escape me'); 450 //cancel any figure creation 451 createFigureFunction = null; 452 453 if(selectedFigureId != -1 || selectedConnectionPointId != -1 || selectedConnectorId!=-1){ 454 redraw = true; 455 } 456 457 //deselect any figure 458 selectedFigureId = -1; 459 selectedConnectionPointId = -1; 460 selectedConnectorId = -1; 461 462 463 state = STATE_NONE; 464 break; 465 466 case KEY.DELETE: //Delete 467 //delete any Figure or Group 468 // alert('Delete pressed' + this); 469 switch(state){ 470 471 case STATE_FIGURE_SELECTED: //delete a figure ONLY when the figure is selected 472 if(selectedFigureId > -1){ 473 //remove figure 474 475 if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action 476 var undo = new DeleteCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.figureGetById(selectedFigureId),ev) 477 478 History.addUndo(undo); 479 } 480 481 stack.figureRemoveById(selectedFigureId); 482 483 484 //remove glues 485 var xCPs = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedFigureId); 486 for(var k=0; k<xCPs.length; k++){ 487 CONNECTOR_MANAGER.glueRemoveAllByFirstId(xCPs[k].id); 488 489 } 490 491 //remove connection points 492 CONNECTOR_MANAGER.connectionPointRemoveAllByParent(selectedFigureId); 493 selectedFigureId = -1; 494 setUpEditPanel(canvasProps); 495 state = STATE_NONE; 496 redraw = true; 497 498 // alert('Delete done'); 499 } 500 break; 501 502 case STATE_CONNECTOR_SELECTED: 503 if(selectedConnectorId != -1){ 504 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId); 505 if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action 506 var undo = new DeleteCommand(selectedConnectorId, History.OBJECT_CONNECTOR, null, con, null); 507 History.addUndo(undo); 508 } 509 CONNECTOR_MANAGER.connectorRemoveByIdCascade(selectedConnectorId); 510 selectedConnectorId = -1; 511 setUpEditPanel(canvasProps); 512 state = STATE_NONE; 513 redraw = true; 514 } 515 break; 516 case STATE_GROUP_SELECTED: 517 var figures = stack.figureGetByGroupId(selectedGroupId); 518 if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action 519 var undo = new DeleteCommand(selectedGroupId, History.OBJECT_GROUP, null, figures, stack.groupGetById(selectedGroupId).permanent) 520 521 History.addUndo(undo); 522 } 523 var figures = stack.figureGetByGroupId(selectedGroupId); 524 stack.groupDestroy(selectedGroupId); 525 for(var i = 0; i < figures.length; i++){ 526 stack.figureRemoveById(figures[i].id); 527 } 528 selectedGroupId = -1; 529 state = STATE_NONE; 530 531 break; 532 } 533 break; 534 535 case KEY.SHIFT: //Shift 536 SHIFT_PRESSED = true; 537 break; 538 539 case KEY.CTRL: //Ctrl 540 case KEY.COMMAND: 541 case KEY.COMMAND_FIREFOX: 542 CNTRL_PRESSED = true; 543 break; 544 545 case KEY.LEFT://Arrow Left 546 action("left"); 547 return false; 548 break; 549 550 case KEY.UP://Arrow Up 551 action("up"); 552 return false; 553 break; 554 555 case KEY.RIGHT://Arrow Right 556 action("right"); 557 return false; 558 break; 559 560 case KEY.DOWN://Arrow Down 561 action("down"); 562 return false; 563 break; 564 565 case KEY.Z: 566 if(CNTRL_PRESSED){ 567 action('undo'); 568 } 569 break; 570 571 case KEY.Y: 572 if(CNTRL_PRESSED){ 573 action('redo'); 574 } 575 break; 576 577 case KEY.G: 578 if(CNTRL_PRESSED){ 579 action('group'); 580 } 581 break; 582 583 case KEY.U: 584 if(CNTRL_PRESSED){ 585 action('ungroup'); 586 } 587 break; 588 589 case KEY.S: 590 if(CNTRL_PRESSED){ 591 //Log.info("CTRL-S pressed "); 592 save(); 593 } 594 break; 595 } 596 draw(); 597 return false; 598 } 599 600 601 /** 602 *Treats the key up event 603 *@param {Event} ev - the event generated when key is up 604 **/ 605 function onKeyUp(ev){ 606 if(!ev){ //IE special code 607 ev = window.event; 608 } 609 switch(ev.keyCode){ 610 case KEY.SHIFT: //Shift 611 SHIFT_PRESSED = false; 612 break; 613 614 case KEY.ALT: //Alt 615 CNTRL_PRESSED = false; 616 break; 617 618 case KEY.CTRL: //Ctrl 619 CNTRL_PRESSED = false; 620 break; 621 } 622 return false; 623 } 624 625 626 /** 627 *Treats the mouse down event 628 *@param {Event} ev - the event generated when key is up 629 **/ 630 function onMouseDown(ev){ 631 var coords = getCanvasXY(ev); 632 var HTMLCanvas = getCanvas(); 633 var x = coords[0]; 634 var y = coords[1]; 635 lastClick = [x,y]; 636 //alert('lastClick: ' + lastClick + ' state: ' + state); 637 638 mousePressed = true; 639 //alert("onMouseDown() + state " + state + " none state is " + STATE_NONE); 640 641 switch(state){ 642 case STATE_NONE: 643 //alert("onMouseDown() - STATE_NONE"); 644 snapMonitor = [0,0]; 645 646 //FIGURE & GROUP 647 var fId = stack.figureGetByXY(x, y); 648 if(fId != -1){ //select figure 649 if(stack.figureGetById(fId).groupId != -1){ //if the figure belongs to a group then select that group 650 selectedGroupId = stack.figureGetById(fId).groupId; 651 var grp = stack.groupGetById(selectedGroupId); 652 state = STATE_GROUP_SELECTED; 653 if(doUndo){ 654 currentMoveUndo = new MatrixCommand(selectedGroupId, History.OBJECT_GROUP, History.MATRIX, Matrix.translationMatrix(grp.getBounds()[0],grp.getBounds()[1]), null); 655 } 656 } 657 else{ //ok, we will select only the figure 658 selectedFigureId = fId; 659 var f = stack.figureGetById(fId); 660 setUpEditPanel(f); 661 state = STATE_FIGURE_SELECTED; 662 if(doUndo){ 663 currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null); 664 } 665 } 666 } 667 else{ 668 //Log.info("onMouseDown() + STATE_NONE - deselect all figures"); 669 setUpEditPanel(canvasProps); 670 HandleManager.clear(); 671 selectedFigureId = -1; 672 673 state = STATE_SELECTING_MULTIPLE; 674 selectionArea.points[0] = new Point(x,y); 675 selectionArea.points[1] = new Point(x,y); 676 selectionArea.points[2] = new Point(x,y); 677 selectionArea.points[3] = new Point(x,y);//the selectionArea has no size until we start dragging the mouse 678 679 } 680 //END FIGURE 681 682 //CONNECTOR 683 var cId = CONNECTOR_MANAGER.connectorGetByXY(x, y); 684 if(cId != -1){ 685 selectedConnectorId = cId; 686 state = STATE_CONNECTOR_SELECTED; 687 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId); 688 setUpEditPanel(con); 689 Log.info('onMouseDown() + STATE_NONE - change to STATE_CONNECTOR_SELECTED'); 690 repaint = true; 691 } else { 692 selectedConnectorId = -1; 693 Log.info('onMouseDown() + STATE_NONE - deselect any connector'); 694 repaint = true; 695 } 696 //CONNECTOR END 697 break; 698 699 700 case STATE_FIGURE_CREATE: 701 snapMonitor = [0,0]; 702 703 //treat new figure 704 //do we need to create a figure on the canvas? 705 if(createFigureFunction){ 706 Log.info("onMouseDown() + STATE_FIGURE_CREATE--> new state STATE_FIGURE_SELECTED"); 707 var createdFigure = createFigureFunction(x,y); 708 createdFigure.style.lineWidth = defaultLineWidth; 709 HTMLCanvas.style.cursor = 'default'; 710 711 stack.figureAdd(createdFigure); 712 713 selectedFigureId = createdFigure.id; 714 if(!ev.noAddUndo && doUndo){//only add an action, if we are not currently undo/redoing an action 715 var undo = new CreateCommand(createdFigure.id, History.OBJECT_FIGURE, null, createdFigure.id, [createFigureFunction,ev]); 716 History.addUndo(undo); 717 } 718 selectedConnectorId = -1; 719 state = STATE_FIGURE_SELECTED; 720 createFigureFunction = null; 721 722 //move figure in position 723 //createdFigure.transform(Matrix.translationMatrix(x-createdFigure.rotationCoords[0].x, y-createdFigure.rotationCoords[0].y)) 724 725 setUpEditPanel(createdFigure); 726 mousePressed = false; 727 repaint = true; 728 } 729 break; 730 731 case STATE_FIGURE_SELECTED: 732 snapMonitor = [0,0]; 733 var fId = stack.figureGetByXY(x, y); 734 if(fId != -1 && stack.figureGetById(fId).groupId != -1){ 735 state = STATE_GROUP_SELECTED; 736 selectedGroupId = stack.figureGetById(fId).groupId; 737 selectedFigureId = -1; 738 var g = stack.groupGetById(selectedFigureId); 739 redraw = true; 740 if(doUndo){ 741 currentMoveUndo = new MatrixCommand(selectedFigureId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(g.getBounds()[0],g.getBounds()[1]), null); 742 } 743 HandleManager.figureSet(g); 744 state = STATE_GROUP_SELECTED; 745 break; 746 } 747 if(fId != -1 && fId != selectedFigureId){ //select figure, if we havent already got it selected 748 selectedFigureId = fId; 749 HandleManager.clear(); 750 var f = stack.figureGetById(fId); 751 setUpEditPanel(f); 752 redraw = true; 753 if(doUndo){ 754 currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null); 755 } 756 } 757 else if(fId == selectedFigureId && fId != -1){ 758 var f = stack.figureGetById(fId); 759 if(doUndo){ 760 currentMoveUndo = new MatrixCommand(fId, History.OBJECT_FIGURE, History.MATRIX, Matrix.translationMatrix(f.getBounds()[0],f.getBounds()[1]), null); 761 } 762 } 763 else if(HandleManager.handleGet(x, y) != null){ //select handle 764 Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected"); 765 HandleManager.handleSelectXY(x,y); 766 var oldRot = [HandleManager.figure.rotationCoords[0].clone(),HandleManager.figure.rotationCoords[1].clone()]; 767 var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1],0.00001); 768 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y); 769 if(angle!= 0){ 770 HandleManager.figure.transform(trans); 771 HandleManager.figure.transform(Matrix.rotationMatrix(-angle)); 772 trans[0][2] = -trans[0][2]; 773 trans[1][2] = -trans[1][2]; 774 HandleManager.figure.transform(trans); 775 trans[0][2] = -trans[0][2]; 776 trans[1][2] = -trans[1][2]; 777 } 778 if(doUndo){ 779 currentMoveUndo = new MatrixCommand(HandleManager.figure.id,History.OBJECT_FIGURE,History.MATRIX, oldRot, HandleManager.figure.getBounds()); 780 } 781 if(angle!= 0){ 782 HandleManager.figure.transform(trans); 783 HandleManager.figure.transform(Matrix.rotationMatrix(angle)); 784 trans[0][2] = -trans[0][2]; 785 trans[1][2] = -trans[1][2]; 786 HandleManager.figure.transform(trans); 787 } 788 } 789 else if(fId != selectedFigureId){ 790 Log.info("onMouseDown() + STATE_FIGURE_SELECTED --> deselect any figure"); 791 selectedFigureId = -1; 792 setUpEditPanel(canvasProps); 793 794 state = STATE_NONE; 795 redraw = true; 796 currentMoveUndo = null; 797 state = STATE_SELECTING_MULTIPLE; 798 selectionArea.points[0] = new Point(x,y); 799 selectionArea.points[1] = new Point(x,y); 800 selectionArea.points[2] = new Point(x,y); 801 selectionArea.points[3] = new Point(x,y);//the selectionArea has no size until we start dragging the mouse 802 803 } 804 break; 805 806 case STATE_GROUP_SELECTED: 807 //GROUPS 808 //if selected group is temporary and we pressed outside of it's border we will destroy it 809 var selectedGroup = stack.groupGetById(selectedGroupId); 810 if(!selectedGroup.contains(x,y) && HandleManager.handleGet(x,y) == null){ 811 if(selectedGroup.permanent == false && doUndo){ 812 History.addUndo(new GroupCommand(selectedGroupId,History.OBJECT_GROUP, false, stack.figureGetIdsByGroupId(selectedGroupId),false)); 813 stack.groupDestroy(selectedGroupId); 814 } 815 selectedGroupId = -1; 816 state = STATE_NONE; 817 break; 818 } 819 820 821 822 var fId = stack.figureGetByXY(x, y); 823 var gId = selectedGroupId; 824 if(fId != -1 && stack.figureGetById(fId).groupId != -1){ 825 //we have selected a figure, could be part of our group or a different group, but is in a group 826 gId = stack.figureGetById(fId).groupId; 827 } 828 else if(HandleManager.handleGet(x,y) != null){ 829 //we are dragging a handle, so set gid to -1, to skip a future if 830 gId = -1; 831 } 832 else if(fId != -1){ 833 //we have an figureId, but no groupId, we are in the wrong place! 834 state = STATE_FIGURE_SELECTED; 835 break; 836 } 837 838 839 if(gId != -1){ 840 // we have a group, set the selectedGroupId, and create an undo object 841 selectedGroupId = gId; 842 //HandleManager.clear(); 843 var g = stack.groupGetById(gId); 844 redraw = true; 845 if(doUndo){ 846 currentMoveUndo = new MatrixCommand(gId, History.OBJECT_GROUP, History.MATRIX, Matrix.translationMatrix(g.getBounds()[0],g.getBounds()[1]), null); 847 } 848 state = STATE_GROUP_SELECTED; 849 } 850 else if(HandleManager.handleGet(x, y) != null){ 851 //select a handle 852 Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected"); 853 HandleManager.handleSelectXY(x,y); 854 855 var oldRot = [HandleManager.figure.rotationCoords[0].clone(),HandleManager.figure.rotationCoords[1].clone()]; 856 var angle = Util.getAngle(HandleManager.figure.rotationCoords[0], HandleManager.figure.rotationCoords[1],0.00001); 857 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y); 858 if(angle!= 0){ 859 HandleManager.figure.transform(trans); 860 HandleManager.figure.transform(Matrix.rotationMatrix(-angle)); 861 trans[0][2] = -trans[0][2]; 862 trans[1][2] = -trans[1][2]; 863 HandleManager.figure.transform(trans); 864 trans[0][2] = -trans[0][2]; 865 trans[1][2] = -trans[1][2]; 866 } 867 if(doUndo){ 868 currentMoveUndo = new MatrixCommand(HandleManager.figure.id,History.OBJECT_GROUP,History.MATRIX, oldRot, HandleManager.figure.getBounds()); 869 } 870 if(angle!= 0){ 871 HandleManager.figure.transform(trans); 872 HandleManager.figure.transform(Matrix.rotationMatrix(angle)); 873 trans[0][2] = -trans[0][2]; 874 trans[1][2] = -trans[1][2]; 875 HandleManager.figure.transform(trans); 876 } 877 } 878 break; 879 880 881 case STATE_CONNECTOR_PICK_FIRST: 882 //moved so it can be called from undo action 883 connectorPickFirst(x,y,ev); 884 break; 885 886 case STATE_CONNECTOR_PICK_SECOND: 887 state = STATE_NONE; 888 break; 889 890 891 case STATE_CONNECTOR_SELECTED: 892 var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId); 893 var start = cps[0]; 894 var end = cps[1]; 895 if(start.point.near(x, y, 3)){ 896 var g = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(start.id); 897 898 Log.info("Picked the start point"); 899 selectedConnectionPointId = start.id; 900 if(g.length != 0 && doUndo == true){//CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints[0] 901 currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT , [g[0].id1,g[0].id2], Matrix.translationMatrix(start.point.x,start.point.y),null); 902 } 903 else if(doUndo == true) { 904 currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT , null, Matrix.translationMatrix(start.point.x,start.point.y),null); 905 } 906 state = STATE_CONNECTOR_MOVE_POINT; 907 HTMLCanvas.style.cursor = 'move'; 908 } 909 else if(end.point.near(x, y, 3)){ 910 var g = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(end.id); 911 912 Log.info("Picked the end point"); 913 selectedConnectionPointId = end.id; 914 if(g.length != 0 && doUndo == true){//CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints[CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints.length - 1] 915 currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT, [g[0].id1,g[0].id2], Matrix.translationMatrix(end.point.x,end.point.y),null); 916 } 917 else if(doUndo == true){ 918 currentMoveUndo = new MatrixCommand(selectedConnectionPointId, History.OBJECT_CONNECTION_POINT, null, Matrix.translationMatrix(end.point.x,end.point.y),null); 919 } 920 state = STATE_CONNECTOR_MOVE_POINT; 921 HTMLCanvas.style.cursor = 'move'; 922 } 923 else{ //no connection point selection 924 925 var newCId = selectedConnectorId; 926 if(HandleManager.handleGet(x,y) == null){//we only get a new connector, if we are not currently 927 //over the current connectors handles 928 newCId = CONNECTOR_MANAGER.connectorGetByXY(x, y); //did we picked another connector? 929 } 930 if(newCId == -1){ 931 Log.info('No other connector selected. Deselect all connectors'); 932 selectedConnectorId = -1; 933 state = STATE_NONE; 934 setUpEditPanel(canvasProps); 935 repaint = true; 936 937 //START: Quick Select FIGURE 938 // var fId = stack.figureGetByXY(x, y); 939 // if(fId != -1){ //select figure 940 // Log.info("onMouseDown() + STATE_CONNECTOR_SELECTED - quick select a figure, new state (STATE_FIGURE_SELECTED)"); 941 // selectedFigureId = fId; 942 // var f = stack.figureGetById(fId); 943 // setUpEditPanel(f); 944 // mousePressed = false; 945 // state = STATE_FIGURE_SELECTED; 946 // } 947 //END: Quick Select FIGURE 948 } 949 else if(newCId == selectedConnectorId){ //did we picked the same connector? 950 //do nothing - it's the same connector 951 Log.info("onMouseDown(): Nothing, it's the same connector"); 952 } else{ 953 Log.info('onMouseDown(): Select another connector'); 954 selectedConnectorId = newCId; 955 setUpEditPanel(CONNECTOR_MANAGER.connectorGetById(selectedConnectorId)); 956 state = STATE_CONNECTOR_SELECTED; 957 repaint = true; 958 } 959 if(HandleManager.handleGet(x, y) != null){ //select handle 960 Log.info("onMouseDown() + STATE_FIGURE_SELECTED - handle selected"); 961 HandleManager.handleSelectXY(x,y); 962 963 } 964 965 //canvas.style.cursor = 'default'; 966 //state = STATE_NONE; 967 } 968 969 break; 970 971 972 default: 973 //alert("onMouseDown() - switch default - state is " + state); 974 } 975 976 draw(); 977 978 return false; 979 } 980 981 982 /** 983 *Treats the mouse up event 984 *@param {Event} ev - the event generated when key is up 985 **/ 986 function onMouseUp(ev){ 987 var coords = getCanvasXY(ev); 988 x = coords[0]; 989 y = coords[1]; 990 991 lastClick = []; 992 switch(state){ 993 994 case STATE_NONE: 995 if(HandleManager.handleGetSelected()){ 996 HandleManager.clear(); 997 } 998 break; 999 1000 case STATE_FIGURE_SELECTED: 1001 if(currentMoveUndo != null && HandleManager.handleGetSelected() == null){//we are moving 1002 var f = stack.figureGetById(selectedFigureId); 1003 if(f.getBounds()[0] != currentMoveUndo.previousValue[0][2] 1004 || f.getBounds()[1] != currentMoveUndo.previousValue[1][2]) 1005 { 1006 currentMoveUndo.currentValue = [Matrix.translationMatrix(f.getBounds()[0]-currentMoveUndo.previousValue[0][2],f.getBounds()[1]-currentMoveUndo.previousValue[1][2])]; 1007 currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2]-f.getBounds()[0],currentMoveUndo.previousValue[1][2]-f.getBounds()[1])]; 1008 1009 History.addUndo(currentMoveUndo); 1010 currentMoveUndo = null; 1011 } 1012 } 1013 //lots of things that can happen here, so quite complicated 1014 if(HandleManager.handleGetSelected() != null){ //deselect current handle 1015 var figure = HandleManager.figure; 1016 var bounds = figure.getBounds(); 1017 1018 //get the angle of the original shape, prior to any action 1019 var oldRotCoords = currentMoveUndo.previousValue; 1020 var newRotCoords = figure.rotationCoords; 1021 1022 var oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1],0.001);//exact angle will return .001 different sometimes, this rounds. 1023 var newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1],0.001); 1024 1025 1026 if(oldAngle != newAngle){//if action is rotation, opposite action is easy, translate, rotate, translate back. 1027 currentMoveUndo.previousValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(oldAngle-newAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)]; 1028 currentMoveUndo.currentValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(newAngle-oldAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)]; 1029 } 1030 else{ 1031 //if not, difficult 1032 1033 //get the exact angle for rotating, if we arent rotating, we must be scaling 1034 oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1]); 1035 newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1]); 1036 var scaleX = 1; 1037 var scaleY = 1; 1038 var translationForwardMatrix = Matrix.translationMatrix(0, 0); 1039 var translationBackMatrix = Matrix.translationMatrix(0, 0); 1040 1041 var oldBounds = currentMoveUndo.currentValue;//we save bounds (pre rotated to be at an angle of 0) here upon starting to drag a handle 1042 1043 1044 //rotate the current figure back to the 0 axis, to get correct representation of width and height 1045 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y); 1046 1047 HandleManager.figure.transform(trans); 1048 trans[0][2] = -trans[0][2]; 1049 trans[1][2] = -trans[1][2]; 1050 HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle)); 1051 HandleManager.figure.transform(trans); 1052 trans[0][2] = -trans[0][2]; 1053 trans[1][2] = -trans[1][2]; 1054 1055 var newBounds = HandleManager.figure.getBounds(); 1056 1057 //rotate the figure back into its normal position 1058 HandleManager.figure.transform(trans); 1059 trans[0][2] = -trans[0][2]; 1060 trans[1][2] = -trans[1][2]; 1061 HandleManager.figure.transform(Matrix.rotationMatrix(newAngle)); 1062 HandleManager.figure.transform(trans); 1063 1064 //we need a record of the non translated, rotated bounds, in order to scale correctly later 1065 HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle)); 1066 var untranslatedBounds = HandleManager.figure.getBounds(); 1067 HandleManager.figure.transform(Matrix.rotationMatrix(newAngle)); 1068 1069 //get the old and new widths and heights; 1070 var oldWidth = (oldBounds[2]-oldBounds[0])/2; 1071 var newWidth = (newBounds[2]-newBounds[0])/2; 1072 var oldHeight = (oldBounds[3]-oldBounds[1])/2; 1073 var newHeight = (newBounds[3]-newBounds[1])/2; 1074 1075 //set the scale proprtions 1076 scaleX = oldWidth/newWidth; 1077 scaleY = oldHeight/newHeight; 1078 var handle = HandleManager.handleGetSelected(); 1079 1080 //if we are scaling the left/top we need to move the right/bottom to the 0 position 1081 if(handle.type == 'w' || handle.type == 'nw' || handle.type == 'sw'){ 1082 translationForwardMatrix[0][2] = -untranslatedBounds[2]; 1083 translationBackMatrix[0][2] = untranslatedBounds[2]; 1084 } 1085 else { 1086 translationForwardMatrix[0][2] = -untranslatedBounds[0]; 1087 translationBackMatrix[0][2] = untranslatedBounds[0]; 1088 } 1089 if(handle.type == 'n' || handle.type == 'nw' || handle.type == 'ne'){ 1090 translationForwardMatrix[1][2] = -untranslatedBounds[3]; 1091 translationBackMatrix[1][2] = untranslatedBounds[3]; 1092 } 1093 else { 1094 translationForwardMatrix[1][2] = -untranslatedBounds[1]; 1095 translationBackMatrix[1][2] = untranslatedBounds[1]; 1096 } 1097 if(doUndo){ 1098 //save the set of transformations 1099 currentMoveUndo.previousValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(scaleX,scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)]; 1100 currentMoveUndo.currentValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(1/scaleX,1/scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)]; 1101 } 1102 } 1103 if(doUndo && currentMoveUndo != null){ 1104 History.addUndo(currentMoveUndo); 1105 } 1106 HandleManager.clear(); 1107 redraw = true; 1108 } 1109 break; 1110 1111 case STATE_GROUP_SELECTED: 1112 //GROUPS 1113 if(currentMoveUndo != null && HandleManager.handleGetSelected() == null){ 1114 var f = stack.groupGetById(selectedGroupId); 1115 if(f.getBounds()[0] != currentMoveUndo.previousValue[0][2] 1116 || f.getBounds()[1] != currentMoveUndo.previousValue[1][2]) 1117 { 1118 currentMoveUndo.currentValue = [Matrix.translationMatrix(f.getBounds()[0] - currentMoveUndo.previousValue[0][2], f.getBounds()[1] - currentMoveUndo.previousValue[1][2])]; 1119 currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2] - f.getBounds()[0], currentMoveUndo.previousValue[1][2] - f.getBounds()[1])]; 1120 } 1121 } 1122 //lots of things that can happen here, so quite complicated 1123 if(HandleManager.handleGetSelected() != null){ //deselect current handle 1124 var figure = HandleManager.figure; 1125 var bounds = figure.getBounds(); 1126 1127 //get the angle of the original shape, prior to any action 1128 var oldRotCoords = currentMoveUndo.previousValue; 1129 var newRotCoords = figure.rotationCoords; 1130 1131 var oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1],0.001); //exact angle will return .001 different sometimes, this rounds. 1132 var newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1],0.001); 1133 1134 1135 if(oldAngle != newAngle && currentMoveUndo != null && doUndo){//if action is rotation, opposite action is easy, translate, rotate, translate back. 1136 currentMoveUndo.previousValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(oldAngle-newAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)]; 1137 currentMoveUndo.currentValue = [Matrix.translationMatrix(-oldRotCoords[0].x,-oldRotCoords[0].y),Matrix.rotationMatrix(newAngle-oldAngle),Matrix.translationMatrix(oldRotCoords[0].x,oldRotCoords[0].y)]; 1138 } 1139 else{//if not, difficult 1140 1141 //get the exact angle for rotating, if we arent rotating, we must be scaling 1142 oldAngle = Util.getAngle(oldRotCoords[0], oldRotCoords[1]); 1143 newAngle = Util.getAngle(newRotCoords[0], newRotCoords[1]); 1144 var scaleX = 1; 1145 var scaleY = 1; 1146 var translationForwardMatrix = Matrix.translationMatrix(0, 0); 1147 var translationBackMatrix = Matrix.translationMatrix(0, 0); 1148 1149 var oldBounds = currentMoveUndo.currentValue;//we save bounds (pre rotated to be at an angle of 0) here upon starting to drag a handle 1150 1151 1152 //rotate the current figure back to the 0 axis, to get correct representation of width and height 1153 var trans = Matrix.translationMatrix(-HandleManager.figure.rotationCoords[0].x,-HandleManager.figure.rotationCoords[0].y); 1154 1155 HandleManager.figure.transform(trans); 1156 trans[0][2] = -trans[0][2]; 1157 trans[1][2] = -trans[1][2]; 1158 HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle)); 1159 HandleManager.figure.transform(trans); 1160 trans[0][2] = -trans[0][2]; 1161 trans[1][2] = -trans[1][2]; 1162 1163 var newBounds = HandleManager.figure.getBounds(); 1164 1165 //rotate the figure back into its normal position 1166 HandleManager.figure.transform(trans); 1167 trans[0][2] = -trans[0][2]; 1168 trans[1][2] = -trans[1][2]; 1169 HandleManager.figure.transform(Matrix.rotationMatrix(newAngle)); 1170 HandleManager.figure.transform(trans); 1171 1172 //we need a record of the non translated, rotated bounds, in order to scale correctly later 1173 HandleManager.figure.transform(Matrix.rotationMatrix(-newAngle)); 1174 var untranslatedBounds = HandleManager.figure.getBounds(); 1175 HandleManager.figure.transform(Matrix.rotationMatrix(newAngle)); 1176 1177 //get the old and new widths and heights; 1178 var oldWidth = (oldBounds[2]-oldBounds[0])/2; 1179 var newWidth = (newBounds[2]-newBounds[0])/2; 1180 var oldHeight = (oldBounds[3]-oldBounds[1])/2; 1181 var newHeight = (newBounds[3]-newBounds[1])/2; 1182 1183 //set the scale proprtions 1184 scaleX = oldWidth/newWidth; 1185 scaleY = oldHeight/newHeight; 1186 var handle = HandleManager.handleGetSelected(); 1187 1188 //if we are scaling the left/top we need to move the right/bottom to the 0 position 1189 if(handle.type == 'w' || handle.type == 'nw' || handle.type == 'sw'){ 1190 translationForwardMatrix[0][2] = -untranslatedBounds[2]; 1191 translationBackMatrix[0][2] = untranslatedBounds[2]; 1192 } 1193 else { 1194 translationForwardMatrix[0][2] = -untranslatedBounds[0]; 1195 translationBackMatrix[0][2] = untranslatedBounds[0]; 1196 } 1197 if(handle.type == 'n' || handle.type == 'nw' || handle.type == 'ne'){ 1198 translationForwardMatrix[1][2] = -untranslatedBounds[3]; 1199 translationBackMatrix[1][2] = untranslatedBounds[3]; 1200 } 1201 else { 1202 translationForwardMatrix[1][2] = -untranslatedBounds[1]; 1203 translationBackMatrix[1][2] = untranslatedBounds[1]; 1204 } 1205 1206 //save the set of transformations 1207 if(doUndo && currentMoveUndo != null){ 1208 currentMoveUndo.previousValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(scaleX,scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)]; 1209 currentMoveUndo.currentValue = [Matrix.rotationMatrix(-newAngle),translationForwardMatrix,Matrix.scaleMatrix(1/scaleX,1/scaleY),translationBackMatrix,Matrix.rotationMatrix(newAngle)]; 1210 } 1211 } 1212 } 1213 HandleManager.handleSelectedIndex = -1; 1214 if(doUndo && currentMoveUndo != null){ 1215 History.addUndo(currentMoveUndo); 1216 } 1217 currentMoveUndo = null; 1218 break; 1219 1220 case STATE_SELECTING_MULTIPLE: 1221 state = STATE_NONE; 1222 var figuresToAdd = []; 1223 for(var i = 0; i < stack.figures.length; i++){ 1224 if(stack.figures[i].groupId == -1){ //we only want ungrouped items 1225 var points = stack.figures[i].getPoints(); 1226 if(points.length == 0){ //if no point at least to add bounds TODO: lame 'catch all' condition 1227 points.push( new Point(stack.figures[i].getBounds()[0], stack.figures[i].getBounds()[1]) ); //top left 1228 points.push( new Point(stack.figures[i].getBounds()[2], stack.figures[i].getBounds()[3]) ); //bottom right 1229 points.push( new Point(stack.figures[i].getBounds()[0], stack.figures[i].getBounds()[3]) ); //bottom left 1230 points.push( new Point(stack.figures[i].getBounds()[2], stack.figures[i].getBounds()[1]) ); //top right 1231 } 1232 1233 for(var a = 0; a < points.length; a++){ 1234 if( Util.isPointInside(points[a], selectionArea.getPoints()) ){ 1235 figuresToAdd.push(stack.figures[i].id); 1236 break; 1237 } 1238 } 1239 } //end if 1240 } //end for 1241 1242 if(figuresToAdd.length > 1){ //if we selected one figure then we can create a group 1243 selectedGroupId = stack.groupCreate(figuresToAdd); 1244 state = STATE_GROUP_SELECTED; 1245 } 1246 else if (figuresToAdd.length == 1){ // if we only select one figure, then it is not a group 1247 selectedFigureId = figuresToAdd[0]; 1248 state = STATE_FIGURE_SELECTED; 1249 } 1250 break; 1251 1252 1253 case STATE_CONNECTOR_PICK_SECOND: 1254 //get current connector 1255 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId); 1256 1257 //get it's CPs 1258 var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(con.id); 1259 1260 //snap to a figure? 1261 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP 1262 if(fCpId != -1){ //we are over a figure's cp 1263 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); 1264 Log.info("Second ConnectionPoint is: " + fCp); 1265 1266 conCps[1].point.x = fCp.point.x; 1267 conCps[1].point.y = fCp.point.y; 1268 1269 con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x; 1270 con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y; 1271 1272 fCp.color = ConnectionPoint.NORMAL_COLOR; //change back the color 1273 conCps[1].color = ConnectionPoint.NORMAL_COLOR; //change back the color 1274 1275 var g = CONNECTOR_MANAGER.glueCreate(fCp.id, conCps[1].id); 1276 1277 if(con.type == Connector.TYPE_JAGGED){ 1278 //TODO: add code 1279 CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(conCps[1].id); 1280 } 1281 if(!ev.noAddUndo && doUndo == true){//if this is a new action, not a "redone" action add a new Undo 1282 History.addUndo(new ConnectCommand([g.id1,g.id2], History.OBJECT_GLUE, null, g.id1, null)); 1283 } 1284 else if(doUndo == true){//otherwise, an undo already exists, and we must incrememnt the ponter 1285 History.CURRENT_POINTER ++; 1286 } 1287 1288 } 1289 else{ //not over a figure's CP 1290 conCps[1].point.x = x; 1291 conCps[1].point.y = y; 1292 1293 con.turningPoints[con.turningPoints.length - 1].x = x; 1294 con.turningPoints[con.turningPoints.length - 1].y = y; 1295 1296 if(con.type == Connector.TYPE_JAGGED){ 1297 CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(conCps[1].id); 1298 } 1299 } 1300 if(!ev.noAddUndo){ 1301 currentMoveUndo.currentValue = [currentMoveUndo.currentValue, ev]; 1302 } 1303 1304 state = STATE_CONNECTOR_SELECTED; 1305 setUpEditPanel(con); 1306 redraw = true; 1307 break; 1308 1309 1310 1311 case STATE_CONNECTOR_MOVE_POINT: 1312 //selected ConnectionPoint 1313 var selCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId); 1314 1315 //selected ConnectionPoints (that belong to selected Connector) 1316 var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selCp.parentId); 1317 1318 var conPoint = selCp; 1319 var matrix = Matrix.translationMatrix(conPoint.point.x - currentMoveUndo.previousValue[0][2], 1320 conPoint.point.y - currentMoveUndo.previousValue[1][2]); 1321 if(doUndo){ 1322 currentMoveUndo.currentValue = [matrix]; 1323 currentMoveUndo.previousValue = [Matrix.translationMatrix(currentMoveUndo.previousValue[0][2] - conPoint.point.x, 1324 currentMoveUndo.previousValue[1][2] - conPoint.point.y)]; 1325 History.addUndo(currentMoveUndo); 1326 } 1327 1328 //change back color 1329 conCps[0].color = ConnectionPoint.NORMAL_COLOR; 1330 conCps[1].color = ConnectionPoint.NORMAL_COLOR; 1331 1332 1333 //Test to see if the CP was UNGLUED to another figure 1334 var glues = CONNECTOR_MANAGER.glueGetBySecondConnectionPointId(selectedConnectionPointId); 1335 if(glues.length == 1){ //ok it is connected and it should be ony one Glue 1336 //pick first one 1337 var fCpId = glues[0].id1; //Figure's ConnectionPoint id 1338 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); //Figure's ConnectionPoint 1339 if(!fCp.point.near(x,y, 3)){ //if we dragged the CP beyound the range of figure's cp 1340 CONNECTOR_MANAGER.glueRemoveByIds(fCpId , selectedConnectionPointId); 1341 Log.info('Glue removed'); 1342 } 1343 } 1344 1345 //Test to see if the CP was GLUED to another figure 1346 //see if over a figure's CP 1347 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP 1348 if(fCpId != -1){ //we are over a figure's cp 1349 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); 1350 Log.info("onMouseUp() + STATE_CONNECTOR_MOVE_POINT : fCP is " + fCp + " and fCpId is " + fCpId); 1351 //var fig = stack.figureSelectById(fCp.parentId); 1352 1353 var con = CONNECTOR_MANAGER.connectorGetById(selCp.parentId); 1354 1355 /*Test to see if we droped into the same figure (just moved inside figure CP's range) 1356 *This means that no unglue event occured so we only need to snap back*/ 1357 var existingGlues = CONNECTOR_MANAGER.glueGetAllByIds(fCpId, selectedConnectionPointId); 1358 if(existingGlues.length > 0){ //same figure 1359 Log.info("Snap back to old figure (not a new glue)"); 1360 //ok we are still linked to old Figure...so snap back to it 1361 if(conCps[0].id == selectedConnectionPointId){//start cp 1362 conCps[0].point.x = fCp.point.x; 1363 conCps[0].point.y = fCp.point.y; 1364 1365 con.turningPoints[0].x = fCp.point.x; 1366 con.turningPoints[0].y = fCp.point.y; 1367 } else{ //end cp 1368 conCps[1].point.x = fCp.point.x; 1369 conCps[1].point.y = fCp.point.y; 1370 1371 con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x; 1372 con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y; 1373 } 1374 } 1375 else{ //new figure 1376 Log.info("Snap back to new figure (plus add a new glue)"); 1377 //ok we are still linked to old Figure...so snap back to it 1378 if(conCps[0].id == selectedConnectionPointId){//start cp 1379 conCps[0].point.x = fCp.point.x; 1380 conCps[0].point.y = fCp.point.y; 1381 1382 con.turningPoints[0].x = fCp.point.x; 1383 con.turningPoints[0].y = fCp.point.y; 1384 } else{ //end cp 1385 conCps[1].point.x = fCp.point.x; 1386 conCps[1].point.y = fCp.point.y; 1387 1388 con.turningPoints[con.turningPoints.length - 1].x = fCp.point.x; 1389 con.turningPoints[con.turningPoints.length - 1].y = fCp.point.y; 1390 } 1391 1392 //add a glue 1393 var g = CONNECTOR_MANAGER.glueCreate(fCpId, selectedConnectionPointId) 1394 if(!ev.noAddUndo && doUndo == true){//if this is a new action, not a "redone" action add a new Undo 1395 currentMoveUndo = new ConnectCommand([g.id1,g.id2], History.OBJECT_GLUE, null, g.id1, null); 1396 History.addUndo(currentMoveUndo); 1397 } 1398 else if(doUndo == true){//otherwise, an undo already exists, and we must incrememnt the ponter 1399 History.CURRENT_POINTER ++; 1400 } 1401 } 1402 } 1403 else{ 1404 //update if we moved away 1405 //CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(selectedConnectionPointId); 1406 } 1407 CONNECTOR_MANAGER.connectorAdjustByConnectionPoint(selectedConnectionPointId); 1408 1409 1410 state = STATE_CONNECTOR_SELECTED; //back to selected connector 1411 selectedConnectionPointId = -1; //but deselect the connection point 1412 redraw = true; 1413 break; 1414 1415 1416 case STATE_CONNECTOR_SELECTED: 1417 if(currentMoveUndo){ 1418 var turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints; 1419 var newTurns = [turns.length]; 1420 for(var i = 0; i < turns.length; i ++){ 1421 newTurns[i] = turns[i].clone(); 1422 } 1423 currentMoveUndo.currentValue = newTurns; 1424 History.addUndo(currentMoveUndo); 1425 state = STATE_NONE; 1426 selectedConnectorId = -1; 1427 } 1428 break; 1429 } 1430 currentMoveUndo = null; 1431 mousePressed = false; 1432 draw(); 1433 } 1434 1435 /**Remembers last move. Initially it's null but once set it's a [x,y] array*/ 1436 var lastMove = null; 1437 1438 /**It will accumulate the changes on either X or Y coordinates for snap effect. 1439 *As we need to escape the "gravity/attraction" of the grid system we need to "accumulate" more changes 1440 *and if those changes become greater than a certain threshold we will initiate a snap action 1441 *Zack : "Because of the snap to grid function we need to move more than a certain amount of pixels 1442 *so we will not be snapped back to the old location" 1443 *Initially it's [0,0] but once more and more changes got added a snap effect will be triggered 1444 *and some of it's elements will be reset to 0. 1445 *So snapMonitor = [sumOfChagesOnX, sumOfChangesOnY] 1446 **/ 1447 var snapMonitor = [0,0]; 1448 1449 1450 /**Treats the mouse move event 1451 *@param {Event} ev - the event generated when key is up 1452 **/ 1453 function onMouseMove(ev){ 1454 // //resize canvas. 1455 // if(lastMousePosition != null){ 1456 // resize(ev); 1457 // } 1458 var redraw = false; 1459 var coords = getCanvasXY(ev); 1460 var x = coords[0]; 1461 var y = coords[1]; 1462 var canvas = getCanvas(); 1463 1464 /*change cursor 1465 *More here: http://www.javascriptkit.com/dhtmltutors/csscursors.shtml 1466 */ 1467 Log.debug("onMouseMoveCanvas: test if over a figure: " + x + "," + y); 1468 1469 1470 switch(state){ 1471 1472 case STATE_NONE: 1473 if(stack.figureIsOver(x,y)){ //over a figure or dragging it 1474 canvas.style.cursor = 'move'; 1475 Log.debug('onMouseMove() - STATE_NONE - mouse cursor = move'); 1476 } 1477 1478 else{ //default cursor 1479 canvas.style.cursor = 'default'; 1480 Log.debug('onMouseMove() - STATE_NONE - mouse cursor = default'); 1481 } 1482 //END CURSOR STYLE 1483 1484 //draw figure 1485 1486 1487 break; 1488 1489 1490 case STATE_SELECTING_MULTIPLE: 1491 selectionArea.points[1].x = x; //top right 1492 1493 selectionArea.points[2].x = x; //bottom right 1494 selectionArea.points[2].y = y; 1495 1496 selectionArea.points[3].y = y;//bottom left 1497 redraw = true; 1498 break; 1499 1500 1501 case STATE_FIGURE_CREATE: 1502 if(createFigureFunction){ //creating a new figure 1503 canvas.style.cursor = 'crosshair'; 1504 } 1505 break; 1506 1507 1508 case STATE_FIGURE_SELECTED: 1509 if(stack.figureIsOver(x,y)){ //over a figure or dragging it 1510 canvas.style.cursor = 'move'; 1511 //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = move'); 1512 } 1513 else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures 1514 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor(); 1515 //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = ' + canvas.style.cursor); 1516 } 1517 else{ //default cursor 1518 canvas.style.cursor = 'default'; 1519 //Log.info('onMouseMove() - STATE_FIGURE_SELECTED - mouse cursor = default'); 1520 } 1521 1522 /*move figure only if no handle is selected*/ 1523 if(mousePressed == true && lastMove != null && selectedFigureId != -1 1524 && HandleManager.handleGetSelected() == null ){ 1525 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - move selected figure"); 1526 var fig = stack.figureGetById(selectedFigureId); 1527 var moveMatrix = generateMoveMatrix(fig, x, y); 1528 // if(!Matrix.equals(moveMatrix, Matrix.IDENTITY)){ 1529 fig.transform(moveMatrix); 1530 redraw = true; 1531 // } 1532 1533 /* 1534 draw(); 1535 return; 1536 */ 1537 } 1538 1539 /*if we have a handle action*/ 1540 if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){ 1541 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action"); 1542 var handle = HandleManager.handleGetSelected(); 1543 //alert('Handle action'); 1544 handle.action(lastMove,x,y); 1545 redraw = true; 1546 } 1547 1548 break; 1549 1550 1551 case STATE_GROUP_SELECTED: 1552 //GROUPS 1553 if(stack.figureIsOver(x,y)){ //over a figure or dragging it 1554 canvas.style.cursor = 'move'; 1555 Log.debug('onMouseMove() - STATE_GROUP_SELECTED - mouse cursor = move'); 1556 } 1557 else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures 1558 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor(); 1559 } 1560 else{ //default cursor 1561 canvas.style.cursor = 'default'; 1562 Log.debug('onMouseMove() - STATE_GROUP_SELECTED - mouse cursor = default'); 1563 } 1564 1565 /*move group only if no handle is selected*/ 1566 if(mousePressed == true && lastMove != null && selectedGroupId != -1 1567 && HandleManager.handleGetSelected() == null ){ 1568 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - move selected figure"); 1569 var g = stack.groupGetById(selectedGroupId); 1570 var moveMatrix = generateMoveMatrix(g, x, y); 1571 g.transform(moveMatrix); 1572 redraw = true; 1573 } 1574 1575 /*if we have a handle action*/ 1576 if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){ 1577 //Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action"); 1578 var handle = HandleManager.handleGetSelected(); 1579 //alert('Handle action'); 1580 handle.action(lastMove,x,y); 1581 redraw = true; 1582 } 1583 1584 break; 1585 1586 1587 case STATE_CONNECTOR_PICK_FIRST: 1588 //change FCP (figure connection points) color 1589 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP 1590 1591 if(fCpId != -1){ //we are over a figure's CP 1592 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); 1593 fCp.color = ConnectionPoint.OVER_COLOR; 1594 // canvas.style.cursor = 'crosshair'; 1595 selectedConnectionPointId = fCpId; 1596 } 1597 else{ //change back old connection point to normal color 1598 if(selectedConnectionPointId != -1){ 1599 var oldCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId); 1600 oldCp.color = ConnectionPoint.NORMAL_COLOR; 1601 // canvas.style.cursor = 'normal'; 1602 selectedConnectionPointId = -1; 1603 } 1604 } 1605 redraw = true; 1606 break; 1607 1608 1609 case STATE_CONNECTOR_PICK_SECOND: 1610 //moved to allow undo to access it 1611 connectorPickSecond(x,y,ev); 1612 redraw = true; 1613 break; 1614 1615 1616 case STATE_CONNECTOR_SELECTED: 1617 //alert('Move but we have a connector'); 1618 //change cursor to move if over a connector's CP 1619 //var connector = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId); 1620 var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId); 1621 var start = cps[0]; 1622 var end = cps[1]; 1623 if(start.point.near(x, y, 3) || end.point.near(x, y, 3)){ 1624 canvas.style.cursor = 'move'; 1625 } 1626 else if(HandleManager.handleGet(x,y) != null){ //over a handle?. Handles should appear only for selected figures 1627 canvas.style.cursor = HandleManager.handleGet(x,y).getCursor(); 1628 } 1629 else{ 1630 canvas.style.cursor = 'default'; 1631 } 1632 1633 /*if we have a handle action*/ 1634 if(mousePressed==true && lastMove != null && HandleManager.handleGetSelected()!=null){ 1635 Log.info("onMouseMove() + STATE_FIGURE_SELECTED - trigger a handler action"); 1636 var handle = HandleManager.handleGetSelected(); 1637 //alert('Handle action'); 1638 1639 /*We need completely new copies of the turningPoints in order to restore them, 1640 *this is simpler than keeping track of the handle used, the direction in which the handle edits 1641 *and the turningPoints it edits*/ 1642 1643 //store old turning points 1644 var turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints; 1645 var oldTurns = [turns.length]; 1646 for(var i = 0; i < turns.length; i++){ 1647 oldTurns[i] = turns[i].clone(); 1648 } 1649 1650 1651 //DO the handle action 1652 handle.action(lastMove,x,y); 1653 1654 //store new turning points 1655 turns = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId).turningPoints; 1656 var newTurns = [turns.length]; 1657 for(var i = 0; i < turns.length; i++){ 1658 newTurns[i] = turns[i].clone(); 1659 } 1660 1661 1662 //see if old turning points are the same as the new turning points 1663 var difference = false; 1664 for(var k=0;k<newTurns.length; k++){ 1665 if(! newTurns[k].equals(oldTurns[k])){ 1666 difference = true; 1667 } 1668 } 1669 1670 //store the Command in History 1671 if(difference && doUndo){ 1672 currentMoveUndo = new ConnectorHandleCommand(selectedConnectorId, History.OBJECT_CONNECTOR, null, oldTurns, newTurns); 1673 History.addUndo(currentMoveUndo); 1674 } 1675 1676 redraw = true; 1677 } 1678 break; 1679 1680 1681 case STATE_CONNECTOR_MOVE_POINT: 1682 if(mousePressed){ //only if we are dragging 1683 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId); 1684 var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(selectedConnectorId); 1685 1686 //update cursor if over a figure's cp 1687 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x,y, ConnectionPoint.TYPE_FIGURE); 1688 if(fCpId != -1){ 1689 canvas.style.cursor = 'default'; 1690 if(cps[0].id == selectedConnectionPointId){ 1691 cps[0].color = ConnectionPoint.OVER_COLOR; 1692 } 1693 else{ 1694 cps[1].color = ConnectionPoint.OVER_COLOR; 1695 } 1696 } 1697 else{ 1698 canvas.style.cursor = 'move'; 1699 if(cps[0].id == selectedConnectionPointId){ 1700 cps[0].color = ConnectionPoint.NORMAL_COLOR; 1701 } 1702 else{ 1703 cps[1].color = ConnectionPoint.NORMAL_COLOR; 1704 } 1705 } 1706 1707 /*update connector - but not unglue/glue it (Unglue and glue is handle in onMouseUp) 1708 *as we want the glue-unglue to produce only when mouse is released*/ 1709 1710 1711 if(cps[0].id == selectedConnectionPointId){ //start 1712 //alert('start'); 1713 cps[0].point.x = x; 1714 cps[0].point.y = y; 1715 con.turningPoints[0].x = x; 1716 con.turningPoints[0].y = y; 1717 } else{ //end 1718 //alert('end'); 1719 cps[1].point.x = x; 1720 cps[1].point.y = y; 1721 con.turningPoints[con.turningPoints.length - 1].x = x; 1722 con.turningPoints[con.turningPoints.length - 1].y = y; 1723 } 1724 redraw = true; 1725 } 1726 break; 1727 } 1728 1729 1730 lastMove = [x, y]; 1731 1732 if(redraw){ 1733 draw(); 1734 } 1735 return false; 1736 } 1737 1738 1739 /**Pick the first connector we can get at (x,y) position 1740 *@param {Number} x - the x position 1741 *@param {Number} y - the y position 1742 *@param {Event} ev - the event triggered 1743 **/ 1744 function connectorPickFirst(x, y, ev){ 1745 //create connector 1746 var conId = CONNECTOR_MANAGER.connectorCreate(new Point(x, y),new Point(x+10,y+10) /*fake cp*/, connectorType); 1747 selectedConnectorId = conId; 1748 var con = CONNECTOR_MANAGER.connectorGetById(conId); 1749 if(ev != null && !ev.noAddUndo && doUndo){ 1750 currentMoveUndo = new CreateCommand(selectedConnectorId, History.OBJECT_CONNECTOR, connectorType, null, ev) 1751 History.addUndo(currentMoveUndo); 1752 } 1753 1754 //TRY TO GLUE IT 1755 //1.get CP of the connector 1756 var conCps = CONNECTOR_MANAGER.connectionPointGetAllByParent(conId); 1757 1758 //see if we can snap to a figure 1759 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP 1760 if(fCpId != -1){ 1761 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); 1762 1763 //update connector' cp 1764 conCps[0].point.x = fCp.point.x; 1765 conCps[0].point.y = fCp.point.y; 1766 1767 //update connector's turning point 1768 con.turningPoints[0].x = fCp.point.x; 1769 con.turningPoints[0].y = fCp.point.y; 1770 1771 var g = CONNECTOR_MANAGER.glueCreate(fCp.id, conCps[0].id); 1772 //alert('First glue ' + g); 1773 //lets create an undo with composite id 1774 1775 /*we have already added an undo object, this is not needed 1776 *if(ev != null && doUndo){//if this is a new action, not a "redone" action add a new Undo 1777 History.addUndo(new Action([g.id1,g.id2], History.OBJECT_GLUE, History.ACTION_CONNECT, null, g.id1, null)); 1778 } 1779 else if(doUndo){//otherwise, an undo already exists, and we must incrememnt the ponter 1780 //History.CURRENT_POINTER ++; 1781 }*/ 1782 } 1783 state = STATE_CONNECTOR_PICK_SECOND; 1784 } 1785 1786 /**Pick the second connector we can get at (x,y) position 1787 *@param {Number} x - the x position 1788 *@param {Number} y - the y position 1789 *@param {Event} ev - the event triggered 1790 **/ 1791 function connectorPickSecond(x, y, ev){ 1792 //current connector 1793 var con = CONNECTOR_MANAGER.connectorGetById(selectedConnectorId) //it should be the last one 1794 var cps = CONNECTOR_MANAGER.connectionPointGetAllByParent(con.id); 1795 1796 //change FCP (figure connection points) color 1797 var fCpId = CONNECTOR_MANAGER.connectionPointGetByXY(x, y, ConnectionPoint.TYPE_FIGURE); //find figure's CP 1798 1799 if(fCpId != -1){ 1800 var fCp = CONNECTOR_MANAGER.connectionPointGetById(fCpId); 1801 fCp.color = ConnectionPoint.OVER_COLOR; 1802 cps[1].color = ConnectionPoint.OVER_COLOR; 1803 selectedConnectionPointId = fCpId; 1804 } 1805 else{//change back old connection point to normal color 1806 if(selectedConnectionPointId != -1){ 1807 var oldCp = CONNECTOR_MANAGER.connectionPointGetById(selectedConnectionPointId); 1808 oldCp.color = ConnectionPoint.NORMAL_COLOR; 1809 cps[1].color = ConnectionPoint.NORMAL_COLOR; 1810 selectedConnectionPointId = -1; 1811 } 1812 } 1813 1814 //update point 1815 if(con.type == Connector.TYPE_STRAIGHT){ 1816 //update last connection point 1817 cps[1].point.x = x; 1818 cps[1].point.y = y; 1819 1820 //update last turning point 1821 con.turningPoints[1].x = x; 1822 con.turningPoints[1].y = y; 1823 } 1824 else if(con.type == Connector.TYPE_JAGGED){ 1825 1826 //WE WILL DRAW A FAKE CONNECTOR - that will be adjusted once mouse released 1827 1828 //WE WILL FULLY RECONSTRUCT THE turningPoints vector 1829 var lastIndex = con.turningPoints.length - 1; 1830 1831 //update last connection point 1832 cps[1].point.x = x; 1833 cps[1].point.y = y; 1834 1835 1836 //update last turning point 1837 con.turningPoints[lastIndex].x = x; 1838 con.turningPoints[lastIndex].y = y; 1839 1840 //now add 2 more intermediate points to make it look jagged 1841 var first = con.turningPoints[0]; 1842 var last = con.turningPoints[lastIndex]; 1843 1844 // 1845 var tp1 = new Point( (first.x + last.x)/2, first.y); 1846 var tp2 = new Point( (first.x + last.x)/2, last.y); 1847 1848 con.turningPoints = [first, tp1, tp2, last]; 1849 1850 //Log.info("Now the jagged con has " + con.turningPoints.length + " points" + con.turningPoints); 1851 } 1852 } 1853 1854 1855 /** Creates a moving matrix taking into consideration the snapTo option 1856 * The strange stuff is that Dia (http://projects.gnome.org/dia/) is using a top/left align 1857 * but OpenOffice's Draw is using something similar to Diagramo 1858 * @return {Matrix} - translation matrix 1859 * @param {Object} fig - could be a figure, or a Connector 1860 * @param {Number} x - mouse position 1861 * @param {Number} y - mouse position 1862 * @author Zack Newsham 1863 * @author Alex Gheorghiu 1864 */ 1865 function generateMoveMatrix(fig, x,y){ 1866 // Log.group("generateMoveMatrix"); 1867 var dx = x - lastMove[0]; 1868 var dy = y - lastMove[1]; 1869 // Log.info("generateMoveMatrix() - delta " + dx + ', ' + dy); 1870 1871 var moveMatrix = null; 1872 1873 if(snapTo){ //snap effect 1874 moveMatrix = [ 1875 [1, 0, 0], 1876 [0, 1, 0], 1877 [0, 0, 1] 1878 ]; 1879 1880 // Log.info("generateMoveMatrix() - old snapMonitor : " + snapMonitor); 1881 // Log.info("generateMoveMatrix() - dx : " + dx + " dy: " + dy); 1882 snapMonitor[0] += dx; 1883 snapMonitor[1] += dy; 1884 // Log.info("generateMoveMatrix() - new snapMonitor : " + snapMonitor); 1885 // Log.info("generateMoveMatrix() - figure bounds : " + fig.getBounds()); 1886 1887 //HORIZONTAL 1888 if(dx > 0){ //dragged to right 1889 var nextGridX = Math.ceil( (fig.getBounds()[2] + snapMonitor[0]) / GRIDWIDTH ) * GRIDWIDTH; 1890 if((snapMonitor[0] + fig.getBounds()[2]) % GRIDWIDTH >= GRIDWIDTH/2 ){ 1891 moveMatrix[0][2] = nextGridX - fig.getBounds()[2]; 1892 // Log.info("generateMoveMatrix() - drag right, nextGridX: " + nextGridX + " x-adjust: " + (nextGridX - fig.getBounds()[2]) ); 1893 snapMonitor[0] -= nextGridX - fig.getBounds()[2]; 1894 } 1895 } 1896 else if(dx < 0){ //dragged to left 1897 var previousGridX = Math.floor( (fig.getBounds()[0] + snapMonitor[0]) / GRIDWIDTH ) * GRIDWIDTH; 1898 1899 if( fig.getBounds()[0] + snapMonitor[0] >= previousGridX 1900 && fig.getBounds()[0] + snapMonitor[0] <= previousGridX + GRIDWIDTH/2 1901 ){ 1902 moveMatrix[0][2] = -(fig.getBounds()[0] - previousGridX); 1903 // Log.info("generateMoveMatrix() - drag left, previousGridX: " + previousGridX + " x-adjust: " + (-(fig.getBounds()[0] - previousGridX)) ); 1904 snapMonitor[0] += fig.getBounds()[0] - previousGridX; 1905 } 1906 } 1907 1908 //VERTICAL 1909 if(dy > 0){ //dragged to bottom 1910 var nextGridY = Math.ceil( (fig.getBounds()[3] + snapMonitor[1]) / GRIDWIDTH ) * GRIDWIDTH; 1911 if( (fig.getBounds()[3] + snapMonitor[1]) % GRIDWIDTH > GRIDWIDTH/2 ){ 1912 // Log.info("generateMoveMatrix() - drag bottom"); 1913 moveMatrix[1][2] = nextGridY - fig.getBounds()[3]; 1914 snapMonitor[1] -= nextGridY - fig.getBounds()[3]; 1915 } 1916 } 1917 else if (dy < 0){ //drag to top 1918 var previousGridY = Math.floor( (fig.getBounds()[1] + snapMonitor[1]) / GRIDWIDTH ) * GRIDWIDTH; 1919 if(fig.getBounds()[1] + snapMonitor[1] >= previousGridY 1920 && fig.getBounds()[1] + snapMonitor[1] <= previousGridY + GRIDWIDTH/2 1921 ){ 1922 // Log.info("generateMoveMatrix() - drag top"); 1923 moveMatrix[1][2] = -(fig.getBounds()[1] - previousGridY); 1924 snapMonitor[1] += fig.getBounds()[1] - previousGridY; 1925 } 1926 } 1927 1928 // Log.info("generateMoveMatrix() - 'trimmed' snapMonitor : " + snapMonitor); 1929 1930 } else{ //normal move 1931 moveMatrix = [ 1932 [1, 0, dx], 1933 [0, 1, dy], 1934 [0, 0, 1] 1935 ]; 1936 } 1937 1938 Log.groupEnd(); 1939 return moveMatrix; 1940 } 1941 1942 1943 /**Computes the bounds of the canvas*/ 1944 function getCanvasBounds(){ 1945 var canvasMinX = $("#a").offset().left; 1946 var canvasMaxX = canvasMinX + $("#a").width(); 1947 var canvasMinY = $("#a").offset().top; 1948 var canvasMaxY = canvasMinX + $("#a").height(); 1949 1950 return [canvasMinX, canvasMinY, canvasMaxX, canvasMaxY]; 1951 } 1952 1953 /**Computes the (x,y) coordinates of an event in page 1954 *@param {Event} ev - the event 1955 **/ 1956 function getBodyXY(ev){ 1957 return [ev.pageX,ev.pageY];//TODO: add scroll 1958 } 1959 1960 1961 /** 1962 *Extracts the X and Y from an event (for canvas) 1963 *@param {Event} ev - the event 1964 **/ 1965 function getCanvasXY(ev){ 1966 var position = []; 1967 var canvasBounds = getCanvasBounds(); 1968 var tempPageX = null; 1969 var tempPageY = null; 1970 1971 if(ev.touches){ //iPad 1972 if(ev.touches.length > 0){ 1973 tempPageX = ev.touches[0].pageX; 1974 tempPageY = ev.touches[0].pageY; 1975 } 1976 } 1977 else{ //normal Desktop 1978 tempPageX = ev.pageX; 1979 tempPageY = ev.pageY; 1980 } 1981 1982 if(tempPageX >= canvasBounds[0] && tempPageX <= canvasBounds[2] 1983 && tempPageY >= canvasBounds[1] && tempPageY <= canvasBounds[3]) 1984 { 1985 //alert('Inside canvas'); 1986 position = [tempPageX - $("#a").offset().left, tempPageY - $("#a").offset().top]; 1987 //alert("getXT : " + position); 1988 } 1989 1990 return position; 1991 } 1992 1993 1994 1995 /**Cleans up the canvas*/ 1996 function reset(){ 1997 var canvas = getCanvas(); 1998 var ctx = getContext(); 1999 ctx.beginPath(); 2000 ctx.clearRect(0,0,canvas.width,canvas.height) 2001 ctx.closePath(); 2002 ctx.stroke(); 2003 //canvas.width=canvas.width; 2004 } 2005 2006 2007 /**Draws all the stuff on the canvas*/ 2008 function draw(){ 2009 2010 var ctx = getContext(); 2011 2012 //alert('Paint 1') 2013 reset(); 2014 //alert('Paint 2') 2015 stack.paint(ctx); 2016 2017 minimap.updateMinimap(); 2018 //alert('Paint 3') 2019 } 2020 2021 2022 2023 2024 /** 2025 *Dispatch actions. Detect the action needed and trigger it. 2026 *@param {String} action - the action name 2027 **/ 2028 function action(action){ 2029 redraw = false; 2030 switch(action){ 2031 2032 case 'undo': 2033 Log.info("main.js->action()->Undo. Nr of actions in the stack: " + History.COMMANDS.length); 2034 History.undo(); 2035 redraw = true; 2036 break; 2037 2038 case 'redo': 2039 Log.info("main.js->action()->Redo. Nr of actions in the stack: " + History.COMMANDS.length); 2040 History.redo(); 2041 redraw = true; 2042 break; 2043 2044 case 'group': 2045 /*After we pressed Ctrl-G any temporary group will became permanent*/ 2046 if(selectedGroupId != -1){ 2047 var group = stack.groupGetById(selectedGroupId); 2048 group.permanent = true; 2049 if(doUndo){ 2050 History.addUndo( new GroupCommand(selectedGroupId, History.OBJECT_GROUP, true, false, stack.figureGetIdsByGroupId(selectedGroupId)) ); 2051 } 2052 } 2053 redraw = true; 2054 break; 2055 2056 case 'ungroup': 2057 if(selectedGroupId != -1){ 2058 if(doUndo){ 2059 History.addUndo(new GroupCommand(selectedGroupId, History.OBJECT_GROUP, true, stack.figureGetIdsByGroupId(selectedGroupId), false)); 2060 } 2061 2062 stack.groupDestroy(selectedGroupId); 2063 selectedGroupId = -1; 2064 state = STATE_NONE; 2065 redraw = true; 2066 } 2067 break; 2068 2069 case 'connector-jagged': 2070 selectedFigureId = -1; 2071 state = STATE_CONNECTOR_PICK_FIRST; 2072 connectorType = Connector.TYPE_JAGGED; 2073 redraw = true; 2074 break; 2075 2076 case 'connector-straight': 2077 selectedFigureId = -1; 2078 state = STATE_CONNECTOR_PICK_FIRST; 2079 connectorType = Connector.TYPE_STRAIGHT; 2080 redraw = true; 2081 break; 2082 2083 case 'rotate90': 2084 case 'rotate90A': 2085 if(selectedFigureId){ 2086 //alert("Selected figure index: " + stack.figureSelectedIndex); 2087 var figure = stack.figureGetById(selectedFigureId); 2088 var bounds = figure.getBounds(); 2089 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2 2090 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2 2091 //alert(dx + ' ' + dy); 2092 //alert("Selected figure is: " + figure); 2093 var TRANSLATE = [ 2094 [1, 0, dx * -1], 2095 [0, 1, dy * -1], 2096 [0, 0, 1] 2097 ] 2098 /* 2099 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2 2100 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2 2101 */ 2102 //TODO: figure out why we have to -1 off the dx, we have to do this regardless of rotation angle 2103 var TRANSLATEBACK = [ 2104 [1, 0, dx], 2105 [0, 1, dy], 2106 [0, 0, 1] 2107 ] 2108 figure.transform(TRANSLATE); 2109 if(action=="rotate90"){ 2110 figure.transform(R90); 2111 } 2112 else{ 2113 figure.transform(R90A); 2114 } 2115 figure.transform(TRANSLATEBACK); 2116 2117 redraw = true; 2118 } 2119 break; 2120 2121 case 'up': 2122 if(selectedFigureId != -1){ 2123 //alert("Selected figure index: " + stack.figureSelectedIndex); 2124 var figure = stack.figureGetById(selectedFigureId); 2125 TRANSLATE = [ 2126 [1, 0, 0], 2127 [0, 1, -1], 2128 [0, 0, 1] 2129 ] 2130 figure.transform(TRANSLATE); 2131 redraw = true; 2132 } 2133 break; 2134 2135 case 'down': 2136 if(selectedFigureId != -1){ 2137 //alert("Selected figure index: " + stack.figureSelectedIndex); 2138 var figure = stack.figureGetById(selectedFigureId); 2139 TRANSLATE = [ 2140 [1, 0, 0], 2141 [0, 1, 1], 2142 [0, 0, 1] 2143 ] 2144 figure.transform(TRANSLATE); 2145 redraw = true; 2146 } 2147 break; 2148 2149 case 'right': 2150 if(selectedFigureId != -1){ 2151 //alert("Selected figure index: " + stack.figureSelectedIndex); 2152 var figure = stack.figureGetById(selectedFigureId); 2153 TRANSLATE = [ 2154 [1, 0, 1], 2155 [0, 1, 0], 2156 [0, 0, 1] 2157 ] 2158 figure.transform(TRANSLATE); 2159 redraw = true; 2160 } 2161 break; 2162 2163 case 'left': 2164 if(selectedFigureId != -1){ 2165 //alert("Selected figure index: " + stack.figureSelectedIndex); 2166 var figure = stack.figureGetById(selectedFigureId); 2167 var TRANSLATE = [ 2168 [1, 0, -1], 2169 [0, 1, 0], 2170 [0, 0, 1] 2171 ] 2172 figure.transform(TRANSLATE); 2173 redraw = true; 2174 } 2175 break; 2176 2177 case 'grow': 2178 if(selectedFigureId != -1){ 2179 //alert("Selected figure index: " + stack.figureSelectedIndex); 2180 var figure = stack.figureGetById(selectedFigureId); 2181 var bounds = figure.getBounds(); 2182 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2 2183 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2 2184 //alert(dx + ' ' + dy); 2185 //alert("Selected figure is: " + figure); 2186 var TRANSLATE = [ 2187 [1, 0, dx * -1], 2188 [0, 1, dy * -1], 2189 [0, 0, 1] 2190 ] 2191 /* 2192 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2 2193 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2 2194 */ 2195 var TRANSLATEBACK = [ 2196 [1, 0, dx], 2197 [0, 1, dy], 2198 [0, 0, 1] 2199 ] 2200 2201 var GROW = [ 2202 [1 + 0.2, 0, 0], 2203 [0, 1 + 0.2, 0], 2204 [0, 0, 1] 2205 ] 2206 figure.transform(TRANSLATE); 2207 figure.transform(GROW); 2208 figure.transform(TRANSLATEBACK); 2209 redraw = true; 2210 } 2211 break; 2212 2213 case 'shrink': 2214 if(selectedFigureId != -1){ 2215 //alert("Selected figure index: " + stack.figureSelectedIndex); 2216 var figure = stack.figureGetById(selectedFigureId); 2217 var bounds = figure.getBounds(); 2218 var dx = bounds[0] + (bounds[2] - bounds[0]) / 2 2219 var dy = bounds[1] + (bounds[3] - bounds[1]) / 2 2220 //alert(dx + ' ' + dy); 2221 //alert("Selected figure is: " + figure); 2222 var TRANSLATE = [ 2223 [1, 0, dx * -1], 2224 [0, 1, dy * -1], 2225 [0, 0, 1] 2226 ] 2227 /* 2228 var dx = bounds[0] + (bounds[3] - bounds[1]) / 2 2229 var dy = bounds[1] + (bounds[2] - bounds[0]) / 2 2230 */ 2231 var TRANSLATEBACK = [ 2232 [1, 0, dx], 2233 [0, 1, dy], 2234 [0, 0, 1] 2235 ] 2236 var SHRINK = [ 2237 [1 - 0.2, 0, 0], 2238 [0, 1 - 0.2, 0], 2239 [0, 0, 1] 2240 ] 2241 figure.transform(TRANSLATE); 2242 figure.transform(SHRINK); 2243 figure.transform(TRANSLATEBACK); 2244 redraw = true; 2245 } 2246 break; 2247 2248 case 'duplicate': 2249 if(selectedFigureId != -1){ 2250 var createdFigure = stack.figureGetById(selectedFigureId).clone(); 2251 createdFigure.transform(Matrix.translationMatrix(10,10)); 2252 stack.figureAdd(createdFigure); 2253 stack.figureSelect(stack.figures.length-1); 2254 createdFigure=null; 2255 getCanvas().style.cursor="default"; 2256 redraw = true; 2257 } 2258 break; 2259 2260 case 'back': 2261 if(selectedFigureId != -1){ 2262 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], 0)); 2263 stack.setPosition(selectedFigureId, 0); 2264 redraw = true; 2265 } 2266 break; 2267 2268 case 'front': 2269 if(selectedFigureId != -1){ 2270 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.figures.length-1)); 2271 stack.setPosition(selectedFigureId, stack.figures.length-1); 2272 redraw = true; 2273 } 2274 break; 2275 2276 case 'moveback': 2277 if(selectedFigureId != -1){ 2278 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.idToIndex[selectedFigureId] - 1)); 2279 stack.swapToPosition(selectedFigureId, stack.idToIndex[selectedFigureId] - 1); 2280 redraw = true; 2281 } 2282 break; 2283 2284 case 'moveforward': 2285 if(selectedFigureId != -1){ 2286 History.addUndo(new ZOrderCommand(selectedFigureId, History.OBJECT_FIGURE, null, stack.idToIndex[selectedFigureId], stack.idToIndex[selectedFigureId] + 1)); 2287 stack.swapToPosition(selectedFigureId, stack.idToIndex[selectedFigureId] + 1); 2288 redraw = true; 2289 } 2290 break; 2291 2292 }//end switch 2293 2294 if(redraw){ 2295 draw(); 2296 } 2297 } 2298 2299 /**Stores last mouse position. Null initially.*/ 2300 var lastMousePosition = null; 2301 2302 2303 //function startResize(ev){ 2304 // lastMousePosition = getBodyXY(ev); 2305 // currentMoveUndo = new Action(null, null, History.ACTION_CANVAS_RESIZE, null, lastMousePosition, null); 2306 //} 2307 // 2308 // 2309 //function stopResize(ev){ 2310 // if(lastMousePosition != null){ 2311 // currentMoveUndo.currentValue = [lastMousePosition[0],lastMousePosition[1]]; 2312 // History.addUndo(currentMoveUndo); 2313 // currentMoveUndo = null; 2314 // lastMousePosition = null; 2315 // } 2316 //} 2317 2318 2319 //function resize(ev){ 2320 // if(lastMousePosition != null){ 2321 // var currentMousePosition 2322 // if(ev instanceof Array){ 2323 // //we are undoing this 2324 // currentMousePosition = ev; 2325 // } 2326 // else{ 2327 // currentMousePosition = getBodyXY(ev); 2328 // } 2329 // var width = canvas.getwidth() - (lastMousePosition[0] - currentMousePosition[0]); 2330 // var height = canvas.getheight() - (lastMousePosition[1] - currentMousePosition[1]); 2331 // canvas.setwidth(canvas,width); 2332 // canvas.setheight(canvas, height); 2333 // setUpEditPanel(canvas); 2334 // lastMousePosition = currentMousePosition; 2335 // /*if(canvas.width >= document.body.scrollWidth-370){ 2336 // } 2337 // else { 2338 // //document.getElementById("container").style.width = ""; 2339 // }*/ 2340 // draw(); 2341 // } 2342 //} 2343 2344 2345 /*======================APPLE=====================================*/ 2346 /**Triggered when an touch is initiated (iPad/iPhone). 2347 *Simply forward to onMouseDown 2348 *@param {Event} event - the event triggered 2349 **/ 2350 function touchStart(event){ 2351 event.preventDefault(); 2352 2353 onMouseDown(event); 2354 } 2355 2356 2357 /**Triggered while touching and moving is in progress (iPad/iPhone). 2358 *Simply forward to onMouseMove 2359 *@param {Event} event - the event triggered 2360 **/ 2361 function touchMove(event){ 2362 event.preventDefault(); 2363 2364 onMouseMove(event); 2365 } 2366 2367 2368 /**Triggered when touch ends (iPad/iPhone). 2369 *Simply forward to onMouseUp 2370 *@param {Event} event - the event triggered 2371 **/ 2372 function touchEnd(event){ 2373 onMouseUp(event); 2374 } 2375 2376 /**Triggered when touch is canceled (iPad/iPhone). 2377 *@param {Event} event - the event triggered 2378 **/ 2379 function touchCancel(event){ 2380 //nothing 2381 } 2382 /*======================END APPLE=================================*/ 2383 2384