Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Tools/TransformTool.cs @ 2868

Last change on this file since 2868 was 2868, checked in by mkommend, 14 years ago

finished mapping from OperatorGraph to GraphVisualizationInfo (ticket #867)

File size: 26.1 KB
Line 
1using System;
2using System.Diagnostics;
3using System.Collections;
4using System.Drawing;
5using System.Windows.Forms;
6
7namespace Netron.Diagramming.Core
8{
9    // ----------------------------------------------------------------------
10    /// <summary>
11    /// This tool implements the action of scaling and/or rotating shapes on
12    /// the canvas.
13    /// </summary>
14    // ----------------------------------------------------------------------
15    public class TransformTool :
16        AbstractTool,
17        IMouseListener
18    {
19
20        #region Fields
21
22        // ------------------------------------------------------------------
23        /// <summary>
24        /// The location of the mouse when the motion starts.
25        /// </summary>
26        // ------------------------------------------------------------------
27        private Point initialPoint;
28
29        // ------------------------------------------------------------------
30        /// <summary>
31        /// The intermediate location of the mouse during the motion.
32        /// </summary>
33        // ------------------------------------------------------------------
34        private Point lastPoint;
35
36        // ------------------------------------------------------------------
37        /// <summary>
38        /// Wwhether or not there is a transformation going on.
39        /// </summary>
40        // ------------------------------------------------------------------
41        private bool changing;
42
43        // ------------------------------------------------------------------
44        /// <summary>
45        /// The entities being transformed.
46        /// </summary>
47        // ------------------------------------------------------------------
48        private Hashtable transformers;
49
50        // ------------------------------------------------------------------
51        /// <summary>
52        /// The origin vector where the scaling has its start.
53        /// </summary>
54        // ------------------------------------------------------------------
55        double ox, oy;
56
57        // ------------------------------------------------------------------
58        /// <summary>
59        /// The scale with which the entities will be scaled.
60        /// </summary>
61        // ------------------------------------------------------------------
62        double scale , scalex, scaley;
63
64        // ------------------------------------------------------------------
65        /// <summary>
66        /// The eight possible types of transformations.
67        /// </summary>
68        // ------------------------------------------------------------------
69        private TransformTypes transform;
70
71        // ------------------------------------------------------------------
72        /// <summary>
73        /// Dummy which is calculated in function of the current resizing
74        /// location.
75        /// </summary>
76        // ------------------------------------------------------------------
77        Point origin = Point.Empty;
78
79        // ------------------------------------------------------------------
80        /// <summary>
81        /// Specifies if the cursor has been changed during a mouse down or
82        /// mouse move event.
83        /// </summary>
84        // ------------------------------------------------------------------
85        bool cursorChanged = false;
86
87        // ------------------------------------------------------------------
88        /// <summary>
89        /// The cursor prior to us changing it.
90        /// </summary>
91        // ------------------------------------------------------------------
92        Cursor previousCursor = null;
93
94        #endregion
95
96        #region Constructor
97        /// <summary>
98        /// Initializes a new instance of the <see cref="T:TransformTool"/> class.
99        /// </summary>
100        /// <param name="name">The name of the tool.</param>
101        public TransformTool(string name)
102            : base(name)
103        {
104        }
105        #endregion
106
107        #region Methods
108
109        // ------------------------------------------------------------------
110        /// <summary>
111        /// Called when the tool is activated.
112        /// </summary>
113        // ------------------------------------------------------------------
114        protected override void OnActivateTool()
115        {
116            //nothing to do
117        }       
118
119        // ------------------------------------------------------------------
120        /// <summary>
121        /// Sets the cursor depending on whether it's over a Tracker grip or
122        /// not.
123        /// </summary>
124        /// <param name="e">MouseEventArgs</param>
125        // ------------------------------------------------------------------
126        bool UpdateCursor(MouseEventArgs e)
127        {
128            // Don't do anything if there isn't a Tracker!
129            if (Controller.View.Tracker == null)
130            {
131                if (IsActive && Enabled)
132                {
133                    //RestoreCursor();
134                }
135                else
136                {
137                    // If we changed the cursor from the mouse being moved,
138                    // and we aren't active, then the base class won't have
139                    // any history of the previous cursor, so we use our own.
140                    // We change the cursor on a mouse move even if this tool
141                    // isn't active because we want to give visual feedback
142                    // on what clicking on a grip will do.
143                    if (previousCursor != null)
144                    {
145                        Cursor = previousCursor;
146                    }
147                }
148                cursorChanged = false;
149                previousCursor = null;
150                return false;
151            }
152
153            // If we're not active AND another tool is active, then don't do
154            // anything.  We don't want to change the cursor during a drag-drop
155            // event even if the mouse moves over a grip!
156            //if ((!IsActive) && (Controller.ActiveTool != null))
157            //{
158            //    if (previousCursor != null)
159            //    {
160            //        Cursor = previousCursor;
161            //        cursorChanged = false;
162            //    }
163            //    return false;
164            //}
165
166            bool gripHit = false;
167            //let the tracker tell us if anything was hit
168            Point gripPoint = this.Controller.View.Tracker.Hit(e.Location);
169            Cursor c = null;
170
171
172            #region determine and set the corresponding cursor
173            switch (gripPoint.X)
174            {
175                case -1:
176                    switch (gripPoint.Y)
177                    {
178                        case -1:
179                            c = Cursors.SizeNWSE;
180                            transform = TransformTypes.NW;
181                            //the origin is the right-bottom of the rectangle
182                            ox = this.Controller.View.Tracker.Rectangle.Right - ViewBase.TrackerOffset;
183                            oy = this.Controller.View.Tracker.Rectangle.Bottom - ViewBase.TrackerOffset;
184                            gripHit = true;
185                            break;
186                        case 0:
187                            c = Cursors.SizeWE;
188                            transform = TransformTypes.W;
189                            //the origin is the right-top of the rectangle
190                            ox = this.Controller.View.Tracker.Rectangle.Right - ViewBase.TrackerOffset;// +this.Controller.View.Tracker.Rectangle.Width / 2;
191                            oy = this.Controller.View.Tracker.Rectangle.Top + ViewBase.TrackerOffset;
192                            gripHit = true;
193                            break;
194                        case 1:
195                            c = Cursors.SizeNESW;
196                            transform = TransformTypes.SW;
197                            //the origin is the right-top of the rectangle
198                            ox = this.Controller.View.Tracker.Rectangle.Right - ViewBase.TrackerOffset;
199                            oy = this.Controller.View.Tracker.Rectangle.Top + ViewBase.TrackerOffset;
200                            gripHit = true;
201                            break;
202                    }
203                    break;
204                case 0:
205                    switch (gripPoint.Y)
206                    {
207                        case -1:
208                            c = Cursors.SizeNS;
209                            transform = TransformTypes.N;
210                            //the origin is the center-bottom of the rectangle
211                            ox = this.Controller.View.Tracker.Rectangle.Left + ViewBase.TrackerOffset;// +this.Controller.View.Tracker.Rectangle.Width / 2;
212                            oy = this.Controller.View.Tracker.Rectangle.Bottom - ViewBase.TrackerOffset;
213                            gripHit = true;
214                            break;
215                        case 1:
216                            c = Cursors.SizeNS;
217                            transform = TransformTypes.S;
218                            //the origin is the left-top of the rectangle
219                            ox = this.Controller.View.Tracker.Rectangle.Left + ViewBase.TrackerOffset;// +this.Controller.View.Tracker.Rectangle.Width / 2;
220                            oy = this.Controller.View.Tracker.Rectangle.Top + ViewBase.TrackerOffset;
221                            gripHit = true;
222                            break;
223                    }
224                    break;
225                case 1:
226                    switch (gripPoint.Y)
227                    {
228                        case -1:
229                            c = Cursors.SizeNESW;
230                            transform = TransformTypes.NE;
231                            //the origin is the left-bottom of the rectangle
232                            ox = this.Controller.View.Tracker.Rectangle.Left + ViewBase.TrackerOffset;
233                            oy = this.Controller.View.Tracker.Rectangle.Bottom - ViewBase.TrackerOffset;
234                            gripHit = true;
235                            break;
236                        case 0:
237                            c = Cursors.SizeWE;
238                            transform = TransformTypes.E;
239                            //the origin is the left-top of the rectangle
240                            ox = this.Controller.View.Tracker.Rectangle.Left + ViewBase.TrackerOffset;// +this.Controller.View.Tracker.Rectangle.Width / 2;
241                            oy = this.Controller.View.Tracker.Rectangle.Top + ViewBase.TrackerOffset;
242                            gripHit = true;
243                            break;
244                        case 1:
245                            c = Cursors.SizeNWSE;
246                            transform = TransformTypes.SE;
247                            //the origin is the left-top of the tracker rectangle plus the little tracker offset
248                            ox = this.Controller.View.Tracker.Rectangle.X + ViewBase.TrackerOffset;
249                            oy = this.Controller.View.Tracker.Rectangle.Y + ViewBase.TrackerOffset;
250
251                            gripHit = true;
252                            break;
253                    }
254                    break;
255            }
256
257            #endregion
258
259            if (gripHit)
260            {
261                if (Cursor != c)
262                {
263                    previousCursor = Cursor;
264                    Cursor = c;
265                    cursorChanged = true;
266                }
267            }
268            else if (cursorChanged)
269            {
270                Cursor = Cursors.Default;
271                previousCursor = null;
272                cursorChanged = false;
273            }
274            return gripHit;
275        }
276
277        void UpdateState(MouseEventArgs e)
278        {           
279            changing = false;
280            changing = UpdateCursor(e);           
281        }
282
283        // ------------------------------------------------------------------
284        /// <summary>
285        /// Handles the mouse down event.
286        /// </summary>
287        /// <param name="e">The
288        /// <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance
289        /// containing the event data.</param>
290        public bool MouseDown(MouseEventArgs e)
291        {
292            if (e == null)
293            {
294                throw new ArgumentNullException(
295                    "The argument object is 'null'");
296            }
297
298            if (e.Button == MouseButtons.Left && Enabled && !IsSuspended &&
299                (Controller.View.Tracker != null) )
300            {
301
302              if (this.Controller.Model.Selection.SelectedItems.Count > 0)
303                {                   
304                    UpdateState(e);
305
306                    if (changing)
307                    {
308                        #region Changing/transforming
309                        //the point where the scaling or dragging of the tracker started
310                        initialPoint = e.Location;
311                        //recursive location of the dragging location
312                        lastPoint = initialPoint;
313
314                        // The base class captures the current cursor when the
315                        // tool is initialized and sets the View's cursor to
316                        // that cursor when this tool is deactivated.  So, first
317                        // restore the cursor to the "selection cursor", then
318                        // change it back again.
319                        Cursor c = Cursor;
320                        Controller.View.CurrentCursor = Cursors.Default;
321                        this.ActivateTool();
322                        Cursor = c;
323                        //set the cursor corresponding to the grip
324                        //Controller.View.CurrentCursor = c;
325                        //create a new collection for the transforming entities
326                        transformers = new Hashtable();
327                        //the points of the connectors
328                        Point[] points = null;
329                        //the entity bone
330                        EntityBone bone;
331                        //take the flat selection and keep the current state as a reference for the transformation
332                        //until the mouseup pins down the final scaling
333                        foreach (IDiagramEntity entity in this.Controller.Model.Selection.FlattenedSelectionItems)
334                        {
335                            if (!entity.Resizable) continue;//only take what's resizable
336
337                            bone = new EntityBone();
338
339                            if (entity is IShape)
340                            {
341                                IShape shape = entity as IShape;
342                                if (shape.Connectors.Count > 0)
343                                {
344                                    points = new Point[shape.Connectors.Count];
345                                    for (int m = 0; m < shape.Connectors.Count; m++)
346                                    {
347                                        points[m] = shape.Connectors[m].Point;
348                                    }
349                                }
350                                else
351                                    points = null;
352                                bone.Rectangle = entity.Rectangle;
353                                bone.ConnectorPoints = points;
354                            }
355                            else if (entity is IConnection)
356                            {
357                                IConnection con = entity as IConnection;
358                                points = new Point[2] { Point.Empty, Point.Empty };
359                                //Only non-attached connection connectors have to be scaled
360                                //Attached connectors will move with their parent.
361                                if (con.From.AttachedTo == null)
362                                    points[0] = con.From.Point;
363                                if (con.To.AttachedTo == null)
364                                    points[1] = con.To.Point;
365                                //We define a connection as a bone with empty rectangle
366                                //One could use some additional members to label it but it's only overhead at this point.
367                                bone.Rectangle = Rectangle.Empty;
368                                bone.ConnectorPoints = points;
369                            }
370                            transformers.Add(entity, bone);
371                        }
372                        #endregion
373
374                        return true;
375                    }
376                }
377
378            }
379            return false;
380        }
381
382        // ------------------------------------------------------------------
383        /// <summary>
384        /// Resizes the selected entities.
385        /// <remarks>The whole logic and geometry behind the resizing is
386        /// quite involved, there is a detailed Visio diagram showing the
387        /// elements of the calculations and how the various switch cases
388        /// are linked together.
389        /// </remarks>
390        /// </summary>
391        /// <param name="e">The
392        /// <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance
393        /// containing the event data.</param>
394        // ------------------------------------------------------------------
395        public void MouseMove(MouseEventArgs e)
396        {
397            #region Checking the incoming data
398            if (e == null)
399                throw new ArgumentNullException("The argument object is 'null'");
400            #endregion
401
402            if ( (!IsActive) && (Controller.ActiveTool == null) )
403            {
404                UpdateCursor(e);
405            }
406
407            #region Definition to make the code more readable
408
409            //some annoying but necessary type conversions here to double precision
410            double lastLength = 0; //the diagonal length of the current rectangle we are resizing
411            double eX = e.X, eY = e.Y; //the vector corresponding to the mouse location
412            double lx = lastPoint.X, ly = lastPoint.Y; // the lastpoint vector
413            double iX = initialPoint.X, iY = initialPoint.Y; // the vactor when the motion started
414
415            double mx, my; //the motion vector
416            double rx = 0, ry = 0; //the resulting vector
417           
418            double sign = 1;
419            #endregion
420
421            if (IsActive)
422            {
423
424                #region The current motion vector is computed
425                mx = eX - lx;
426                my = eY - ly;
427                #endregion
428
429                #region Switching between the different compass directions of the grips
430                //the transform is the compass direction of the grip used to resize the entities
431                switch (transform)
432                {
433                    #region NW
434                    case TransformTypes.NW:
435
436
437
438                        //the region above the X-Y=0 diagonal uses the horizontal to project the motion vector onto the diagonal
439                        if (mx - my < 0)
440                        {
441                            scale = (ox - (double)e.X) / (ox - (double)initialPoint.X);
442                        }
443                        else
444                        {
445                            scale = (oy - (double)e.Y) / (oy - (double)initialPoint.Y);
446                        }
447                        //now we can pass the info the the scaling method of the selected entities
448                        scalex = scale;
449                        scaley = scale;
450                        break;
451                    #endregion
452
453                    #region N
454                    case TransformTypes.N:
455                        //remember that the coordinate system has its Y-axis pointing downwards                       
456                        scale = (oy - (double)e.Y) / (oy - (double)initialPoint.Y);
457
458                        //now we can pass the info the the scaling method of the selected entities
459                        scalex = 1F;
460                        scaley = scale;
461                        break;
462                    #endregion
463
464                    #region NE
465                    case TransformTypes.NE:
466                        //the region above the X-Y=0 diagonal uses the horizontal to project the motion vector onto the diagonal
467                        if (mx + my > 0)
468                        {
469                            scale = ((double)e.X - ox) / ((double)initialPoint.X - ox);
470
471                        }
472                        else
473                        {
474                            scale = ((double)e.Y - oy) / ((double)initialPoint.Y - oy);
475
476                        }
477                        //now we can pass the info the the scaling method of the selected entities
478                        scalex = scale;
479                        scaley = scale;
480                        break;
481                    #endregion
482
483                    #region E
484                    case TransformTypes.E:
485                        //remember that the coordinate system has its Y-axis pointing downwards                       
486                        scale = ((double)e.X - ox) / ((double)initialPoint.X - ox);
487
488                        //now we can pass the info the the scaling method of the selected entities
489                        scalex = scale;
490                        scaley = 1F;
491                        break;
492                    #endregion
493
494                    #region SE
495                    case TransformTypes.SE:
496                        //I'd call this the Visio effect...
497                        if (mx - my > 0)
498                        {
499                            scale = ((double)e.X - ox) / ((double)initialPoint.X - ox);
500                        }
501                        else
502                            scale = ((double)e.Y - oy) / ((double)initialPoint.Y - oy);
503
504                        scalex = scale;
505                        scaley = scale;
506                        break;
507                    #endregion
508
509                    #region S
510                    case TransformTypes.S:
511                        //remember that the coordinate system has its Y-axis pointing downwards                       
512                        scale = ((double)e.Y - oy) / ((double)initialPoint.Y - oy);
513
514                        //now we can pass the info the the scaling method of the selected entities
515                        scalex = 1F;
516                        scaley = scale;
517                        break;
518                    #endregion
519
520                    #region SW
521                    case TransformTypes.SW:
522
523                        //the region above the X-Y=0 diagonal uses the horizontal to project the motion vector onto the diagonal
524                        if (mx + my < 0)
525                        {
526                            scale = ((double)e.X - ox) / ((double)initialPoint.X - ox);
527                        }
528                        else
529                        {
530                            scale = ((double)e.Y - oy) / ((double)initialPoint.Y - oy);
531
532                        }
533                        //now we can pass the info the the scaling method of the selected entities
534                        scalex = scale;
535                        scaley = scale;
536                        break;
537                    #endregion
538
539                    #region W
540                    case TransformTypes.W:
541                        //remember that the coordinate system has its Y-axis pointing downwards                       
542                        scale = (ox - (double)e.X) / (ox - (double)initialPoint.X);
543
544                        //now we can pass the info the the scaling method of the selected entities
545                        scalex = scale;
546                        scaley = 1F;
547                        break;
548                    #endregion
549
550                }
551                #endregion
552
553                #region Scale the selected entities
554
555                //block scaling below some minimum
556                if (lastLength <= 70 && sign == -1)
557                {
558                    return;
559                }
560
561                // No need to use the rounding Convert method since the ox and
562                // oy doubles are really integers, but the calculations above
563                // requires double data types.
564                origin = new Point(Convert.ToInt32(ox), Convert.ToInt32(oy));
565
566                //update the location of the last point
567                lastPoint.Offset(Convert.ToInt32(rx), Convert.ToInt32(ry));
568
569                TransformCommand.Transform(origin, scalex, scaley, transformers);
570
571                #endregion
572
573                // Since we use the flattened selection the group shapes
574                // are unaware of the resize, so we have to recalculate
575                // the group rectangles.
576                foreach (IDiagramEntity entity in this.Controller.Model.Selection.SelectedItems)
577                {
578                    //the calculation will cascade to subgroups if necessary
579                    if (entity is IGroup)
580                    {
581                        (entity as IGroup).CalculateRectangle();
582                    }
583                }
584                //update the state of the tracker, i.e show it again and it'll be recalculated
585                this.Controller.View.ShowTracker();
586            }
587        }
588
589
590        public void MouseUp(MouseEventArgs e)
591        {
592            if (IsActive)
593            {
594                DeactivateTool();
595                //Controller.View.CurrentCursor = Cursors.Default;
596                cursorChanged = false;
597                previousCursor = null;
598                TransformCommand cmd = new TransformCommand(
599                    this.Controller,
600                    origin,
601                    scalex,
602                    scaley,
603                    transformers);
604                this.Controller.UndoManager.AddUndoCommand(cmd);
605            }
606        }
607        #endregion
608
609        #region Structure
610
611        #endregion
612    }
613
614
615}
Note: See TracBrowser for help on using the repository browser.