Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.10/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Tools/TransformTool.cs

Last change on this file was 4068, checked in by swagner, 14 years ago

Sorted usings and removed unused usings in entire solution (#1094)

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