Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/BaseClasses/ShapeBase.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: 21.9 KB
Line 
1using System;
2using System.ComponentModel;
3using System.Drawing;
4using System.Windows.Forms;
5namespace Netron.Diagramming.Core {
6  // ----------------------------------------------------------------------
7  /// <summary>
8  /// Abstract base class for shapes.
9  /// </summary>
10  // ----------------------------------------------------------------------
11  public abstract partial class ShapeBase : DiagramEntityBase, IShape {
12    #region Fields
13
14    // ------------------------------------------------------------------
15    /// <summary>
16    /// Implementation of IVersion - the current version of
17    /// ShapeBase.
18    /// </summary>
19    // ------------------------------------------------------------------
20    protected const double shapeBaseVersion = 1.0;
21
22    // ------------------------------------------------------------------
23    /// <summary>
24    /// Specifies if connectors are drawn.
25    /// </summary>
26    // ------------------------------------------------------------------
27    protected bool mShowConnectors = true;
28
29    // ------------------------------------------------------------------
30    /// <summary>
31    /// The collection of Connectors onto which you can attach a
32    /// connection.
33    /// </summary>
34    // ------------------------------------------------------------------
35    protected CollectionBase<IConnector> mConnectors;
36
37    protected bool mIsFixed = false;
38
39    #endregion
40
41    #region Properties
42
43    // ------------------------------------------------------------------
44    /// <summary>
45    /// Gets the current version.
46    /// </summary>
47    // ------------------------------------------------------------------
48    public override double Version {
49      get {
50        return shapeBaseVersion;
51      }
52    }
53
54    // ------------------------------------------------------------------
55    /// <summary>
56    /// Gets the image for this shape when displaying in a library (i.e.
57    /// toolbox).
58    /// </summary>
59    // ------------------------------------------------------------------
60    public virtual Image LibraryImage {
61      get {
62        return null;
63      }
64    }
65
66    // ------------------------------------------------------------------
67    /// <summary>
68    /// Gets or sets if the connectors are drawn.
69    /// </summary>
70    // ------------------------------------------------------------------
71    public bool ShowConnectors {
72      get {
73        return this.mShowConnectors;
74      }
75      set {
76        bool oldValue = mShowConnectors;
77        this.mShowConnectors = value;
78        if (oldValue != value) {
79          Invalidate();
80        }
81        RaiseOnChange(this, new EntityEventArgs(this));
82      }
83    }
84
85    // ------------------------------------------------------------------
86    /// <summary>
87    /// Gets or sets the canvas to which the entity belongs
88    /// </summary>
89    /// <value></value>
90    // ------------------------------------------------------------------
91    public override IModel Model {
92      get {
93        return base.Model;
94      }
95      set {
96        base.Model = value;
97        if (Connectors == null)
98          throw new InconsistencyException("The connectors collection is 'null'.");
99        foreach (IConnector con in Connectors) {
100          con.Model = value;
101        }
102      }
103    }
104
105    // ------------------------------------------------------------------
106    /// <summary>
107    /// Gets or sets the Connectors attached to this shape.
108    /// </summary>   
109    // ------------------------------------------------------------------
110    public CollectionBase<IConnector> Connectors {
111      get { return mConnectors; }
112      set { mConnectors = value; }
113    }
114
115    // ------------------------------------------------------------------
116    /// <summary>
117    /// Gets or sets the width of the bundle
118    /// </summary>
119    // ------------------------------------------------------------------
120    [Browsable(true),
121    Description("The width of the shape"),
122    Category("Layout")]
123    public int Width {
124      get {
125        return this.mRectangle.Width;
126      }
127      set {
128        Transform(
129            this.Rectangle.X,
130            this.Rectangle.Y,
131            value,
132            this.Height);
133      }
134    }
135
136    // ------------------------------------------------------------------
137    /// <summary>
138    /// Gets or sets the height of the bundle
139    /// </summary>   
140    // ------------------------------------------------------------------
141    [Browsable(true),
142    Description("The height of the shape"),
143    Category("Layout")]
144    public int Height {
145      get {
146        return this.mRectangle.Height;
147      }
148      set {
149        Transform(
150            this.Rectangle.X,
151            this.Rectangle.Y,
152            this.Width,
153            value);
154      }
155    }
156
157    // ------------------------------------------------------------------
158    /// <summary>
159    /// the x-coordinate of the upper-left corner
160    /// </summary>
161    // ------------------------------------------------------------------
162    [Browsable(true),
163    Description("The x-coordinate of the upper-left corner"),
164    Category("Layout")]
165    public int X {
166      get {
167        return mRectangle.X;
168      }
169      set {
170        Point p = new Point(value - mRectangle.X, mRectangle.Y);
171        this.MoveBy(p);
172
173        //if(Model!=null)
174        //  Model.RaiseOnInvalidate(); //note that 'this.Invalidate()' will not be enough
175      }
176    }
177
178    // ------------------------------------------------------------------
179    /// <summary>
180    /// the y-coordinate of the upper-left corner
181    /// </summary>
182    // ------------------------------------------------------------------
183    [Browsable(true), Description("The y-coordinate of the upper-left corner"), Category("Layout")]
184    public int Y {
185      get {
186        return mRectangle.Y;
187      }
188      set {
189        Point p = new Point(mRectangle.X, value - mRectangle.Y);
190        this.MoveBy(p);
191        //Model.RaiseOnInvalidate();
192      }
193    }
194
195    // ------------------------------------------------------------------
196    /// <summary>
197    /// Gets or sets the location of the bundle;
198    /// </summary>
199    // ------------------------------------------------------------------
200    [Browsable(false)]
201    public Point Location {
202      get {
203        return new Point(this.mRectangle.X, this.mRectangle.Y);
204      }
205      set {
206        //we use the move method but it requires the delta value, not
207        // an absolute position!
208        Point p = new Point(
209            value.X - mRectangle.X,
210            value.Y - mRectangle.Y);
211
212        // If you'd use this it would indeed move the bundle but not
213        // the connectors of the bundle
214        // this.mRectangle.X = value.X; this.mRectangle.Y = value.Y; Invalidate();
215        this.MoveBy(p);
216      }
217    }
218
219    // ------------------------------------------------------------------
220    /// <summary>
221    /// Gets the bounds of the paintable entity
222    /// </summary>
223    /// <value></value>
224    // ------------------------------------------------------------------
225    public override Rectangle Rectangle {
226      get {
227        return mRectangle;
228      }
229      //set{mRectangle = value;
230
231      //this.Invalidate();              }
232    }
233
234    #endregion
235
236    #region Constructor
237
238
239    /// <summary>
240    /// Constructor with the site of the bundle
241    /// </summary>
242    /// <param name="model"></param>
243    protected ShapeBase(IModel model)
244      : base(model) {
245    }
246
247    /// <summary>
248    /// Initializes a new instance of the <see cref="T:ShapeBase"/> class.
249    /// </summary>
250    protected ShapeBase()
251      : base() {
252    }
253
254    /// <summary>
255    /// Inits this instance.
256    /// </summary>
257    protected override void Initialize() {
258      base.Initialize();
259      mConnectors = new CollectionBase<IConnector>();
260      mConnectors.OnItemAdded += new EventHandler<CollectionEventArgs<IConnector>>(mConnectors_OnItemAdded);
261      mRectangle = new Rectangle(0, 0, 100, 70);
262    }
263
264    #endregion
265
266    #region Methods
267
268    // ------------------------------------------------------------------
269    /// <summary>
270    /// Overrides the base 'OnBeforeDelete' to tell all attached
271    /// connectors they're being deleted.
272    /// </summary>
273    /// <param name="deleteCommand"></param>
274    // ------------------------------------------------------------------
275    public override void OnBeforeDelete(DeleteCommand deleteCommand) {
276      base.OnBeforeDelete(deleteCommand);
277      foreach (IConnector con in this.mConnectors) {
278        con.OnBeforeDelete(deleteCommand);
279      }
280    }
281
282    // ------------------------------------------------------------------
283    /// <summary>
284    /// Overrides the base 'OnAfterDelete' to tell all attached
285    /// connectors they have been deleted.
286    /// </summary>
287    /// <param name="deleteCommand"></param>
288    // ------------------------------------------------------------------
289    public override void OnAfterDelete(DeleteCommand deleteCommand) {
290      base.OnAfterDelete(deleteCommand);
291      foreach (IConnector con in this.mConnectors) {
292        con.OnAfterDelete(deleteCommand);
293      }
294    }
295
296    // ------------------------------------------------------------------
297    /// <summary>
298    /// Generates a new Uid for this entity.
299    /// </summary>
300    /// <param name="recursive">bool: If the Uid has to be changed 
301    /// recursively down to the sub-entities, set to true, otherwise
302    /// false.</param>
303    // ------------------------------------------------------------------
304    public override void NewUid(bool recursive) {
305
306      if (recursive) {
307        foreach (IConnector connector in Connectors) {
308          connector.NewUid(recursive);
309        }
310        base.NewUid(recursive);
311      } else
312        base.NewUid(recursive);
313    }
314
315    // ------------------------------------------------------------------
316    /// <summary>
317    /// Part of the initialization, this method connects newly added
318    /// connectors to the parenting shape.
319    /// </summary>
320    /// <param name="sender">object</param>
321    /// <param name="e">CollectionEventArgs<IConnector></param>
322    // ------------------------------------------------------------------
323    void mConnectors_OnItemAdded(
324        object sender,
325        CollectionEventArgs<IConnector> e) {
326      e.Item.Parent = this;
327    }
328
329    // ------------------------------------------------------------------
330    /// <summary>
331    /// The custom menu to be added to the base menu of this entity.
332    /// 'null' is returned here.
333    /// </summary>
334    /// <returns>ToolStripItem[]</returns>
335    // ------------------------------------------------------------------
336    public override ToolStripItem[] Menu() {
337      return null;
338    }
339
340    // ------------------------------------------------------------------
341    /// <summary>
342    /// Returns the connector hit by the mouse, if any.
343    /// </summary>
344    /// <param name="p">Point: The mouse coordinates</param>
345    /// <returns>IConnector: The connector hit by the mouse</returns>
346    // ------------------------------------------------------------------
347    public IConnector HitConnector(Point p) {
348      for (int k = 0; k < mConnectors.Count; k++) {
349        if (mConnectors[k].Hit(p)) {
350          mConnectors[k].Hovered = true;
351          mConnectors[k].Invalidate();
352          return mConnectors[k];
353        } else {
354          mConnectors[k].Hovered = false;
355          mConnectors[k].Invalidate();
356
357        }
358
359
360      }
361      return null;
362    }
363
364    // ------------------------------------------------------------------
365    /// <summary>
366    /// Overrides the abstract paint method.  Nothing performed here,
367    /// let the sub-shapes do the painting.
368    /// </summary>
369    /// <param name="g">a graphics object onto which to paint</param>
370    // ------------------------------------------------------------------
371    public override void Paint(Graphics g) {
372      return;
373    }
374
375    // ------------------------------------------------------------------
376    /// <summary>
377    /// Override the abstract Hit method.  Here a simple hit test is
378    /// performed by checking if our "Rectangle" contains the point
379    /// specified.  Override this to perform more complex hit testing.
380    /// </summary>
381    /// <param name="p">Point</param>
382    /// <returns>bool</returns>
383    // ------------------------------------------------------------------
384    public override bool Hit(Point p) {
385      Rectangle r = new Rectangle(p, new Size(5, 5));
386      return Rectangle.Contains(r);
387    }
388
389    // ------------------------------------------------------------------
390    /// <summary>
391    /// Overrides the abstract Invalidate method.  This shape's rectangle
392    /// is inflated (not permanently) by a size of (40, 40) to ensure
393    /// the whole area is refreshed.
394    /// </summary>
395    // ------------------------------------------------------------------
396    public override void Invalidate() {
397      Rectangle r = Rectangle;
398      r.Offset(-10, -10);
399      r.Inflate(40, 40);
400      if (Model != null) {
401        Model.RaiseOnInvalidateRectangle(r);
402      }
403    }
404
405    // ------------------------------------------------------------------
406    /// <summary>
407    /// Moves the entity with the given shift (offset).
408    /// </summary>
409    /// <param name="p">Represents a shift-vector, not the absolute
410    /// position!</param>
411    // ------------------------------------------------------------------
412    public override void MoveBy(Point p) {
413      Rectangle recBefore = mRectangle;
414      recBefore.Inflate(20, 20);
415
416      this.mRectangle.X += p.X;
417      this.mRectangle.Y += p.Y;
418
419      UpdatePaintingMaterial();
420
421      for (int k = 0; k < this.mConnectors.Count; k++) {
422        mConnectors[k].MoveBy(p);
423      }
424      RaiseOnChange(this, new EntityEventArgs(this));
425      //refresh things
426      this.Invalidate(recBefore);//position before the move
427      this.Invalidate();//current position
428
429    }
430
431    // ------------------------------------------------------------------
432    /// <summary>
433    /// Scales the entity with the given factor at the given origin.
434    /// <remarks>More an historical milestone than used code.</remarks>
435    /// </summary>
436    /// <param name="origin">The origin.</param>
437    /// <param name="scaleX">The scale X.</param>
438    /// <param name="scaleY">The scale Y.</param>
439    // ------------------------------------------------------------------
440    void Scale(
441        Point origin,
442        double scaleX,
443        double scaleY) {
444      #region Variables
445      //temporary variables to assign the new location of the mConnectors           
446      double a, b;
447      //the new location of the connector
448      Point p;
449      //calculated/scaled/biased corners of the new rectangle
450      double ltx = 0, lty = 0, rbx = 0, rby = 0;
451
452      Rectangle currentRectangle = Rectangle;
453      //we only need to transform the LT and RB corners since the rest of the rectangle can be deduced from that
454      /*
455      PointF[] corners = new PointF[]{new PointF(currentRectangle.X, currentRectangle.Y),                                                           
456                                                      new PointF(currentRectangle.Right, currentRectangle.Bottom),                                                           
457           
458       };
459       */
460      Rectangle newRectangle;
461      #endregion
462
463      #region Transformation matrix
464
465      ltx = Math.Round((currentRectangle.X - origin.X) * scaleX, 1) + origin.X;
466      lty = Math.Round((currentRectangle.Y - origin.Y) * scaleY, 1) + origin.Y;
467
468      rbx = Math.Round((currentRectangle.Right - origin.X) * scaleX, 1) + origin.X;
469      rby = Math.Round((currentRectangle.Bottom - origin.Y) * scaleY, 1) + origin.Y;
470
471      //corners[0] = new PointF
472      //Matrix m = new Matrix();
473      // m.Translate(-origin.X, -origin.Y,MatrixOrder.Append);
474      // m.Scale(scaleX, scaleY, MatrixOrder.Append);
475      // m.Translate(origin.X, origin.Y, MatrixOrder.Append);
476      #endregion
477
478      //transfor the LTRB points of the current rectangle
479      //m.TransformPoints(corners);
480
481      #region Bias
482      /*
483            if(currentRectangle.Y <= origin.Y + ViewBase.TrackerOffset && origin.Y - ViewBase.TrackerOffset <= currentRectangle.Y)
484            {
485                //do not scale in the Y-direction
486                lty = currentRectangle.Y;
487            }
488           
489            if(currentRectangle.X <= origin.X+ ViewBase.TrackerOffset && origin.X - ViewBase.TrackerOffset <= currentRectangle.X)
490            {
491                //do not scale in the X-direction
492                ltx = currentRectangle.X;
493            }
494           
495            if(currentRectangle.Right <= origin.X + ViewBase.TrackerOffset && origin.X - ViewBase.TrackerOffset <= currentRectangle.Right)
496            {
497                //do not scale in the X-direction
498                rbx = currentRectangle.Right;
499            }
500           
501            if(currentRectangle.Bottom <= origin.Y + ViewBase.TrackerOffset && origin.Y - ViewBase.TrackerOffset <= currentRectangle.Bottom)
502            {
503                //do not scale in the Y-direction           
504                rby = currentRectangle.Bottom;
505            }
506             */
507      #endregion
508      /*
509            ltx = Math.Round(ltx);
510            lty = Math.Round(lty);
511            rbx = Math.Round(rbx);
512            rby = Math.Round(rby);
513             * */
514      //now we can re-create the rectangle of this shape           
515      //newRectangle = RectangleF.FromLTRB(ltx, lty, rbx, rby);
516      newRectangle = Rectangle.FromLTRB(Convert.ToInt32(ltx), Convert.ToInt32(lty), Convert.ToInt32(rbx), Convert.ToInt32(rby));
517      //if ((newRectangle.Width <= 50 && scaleX < 1) || (newRectangle.Height <= 50 && scaleY < 1))
518      //    return;
519      #region Scaling of the mConnectors
520      //Note that this mechanism is way easier than the calculations in the old Netron library
521      //and it also allows dynamic mConnectors.
522      foreach (IConnector cn in this.mConnectors) {
523        //De profundis: ge wilt het gewoon nie weten hoeveel bloed, zweet en tranen ik in de onderstaande berekeningen heb gestoken...
524        //met al de afrondingen en meetkundinge schaalafwijkingen..tis een wonder dat ik eruit ben geraakt.
525
526        //Scaling preserves proportions, so we calculate the proportions before the rectangle was resized and
527        //re-assign the same proportion after the rectangle is resized.
528        //I have tried many, many different possibilities but the accumulation of double-to-int conversions is a real pain.
529        //The only working solution I found was to cut things off after the first decimal.
530        a = Math.Round(((double)cn.Point.X - (double)mRectangle.X) / (double)mRectangle.Width, 1) * newRectangle.Width + ltx;
531        b = Math.Round(((double)cn.Point.Y - (double)mRectangle.Y) / (double)mRectangle.Height, 1) * newRectangle.Height + lty;
532        p = new Point(Convert.ToInt32(a), Convert.ToInt32(b));
533        cn.Point = p;
534      }
535      #endregion
536
537      //assign the new calculated rectangle to this shape
538      this.mRectangle = newRectangle;
539
540      RaiseOnChange(this, new EntityEventArgs(this));
541
542      //invalidate the space before the resize; very important if the scaling is a contraction!
543      this.Invalidate(currentRectangle);
544      //invalidate the current situation
545      this.Invalidate();
546    }
547
548    // ------------------------------------------------------------------
549    /// <summary>
550    /// Transforms the entity to the given new rectangle.
551    /// </summary>
552    /// <param name="x">The x-coordinate of the new rectangle.</param>
553    /// <param name="y">The y-coordinate of the new rectangle.</param>
554    /// <param name="width">The width.</param>
555    /// <param name="height">The height.</param>
556    // ------------------------------------------------------------------
557    public virtual void Transform(int x, int y, int width, int height) {
558      // Make sure the new size is valid.
559      if ((width < myMinSize.Width) ||
560          (height < myMinSize.Height) ||
561          (width > myMaxSize.Width) ||
562          (height > myMaxSize.Height)) {
563        return;
564      }
565
566      double a, b;
567      Point p;
568      Rectangle before = mRectangle;
569      before.Inflate(20, 20);
570      foreach (IConnector cn in this.mConnectors) {
571        a = Math.Round(
572            ((double)cn.Point.X - (double)mRectangle.X) /
573            (double)mRectangle.Width, 1) * width + x - cn.Point.X;
574
575        b = Math.Round(
576            ((double)cn.Point.Y - (double)mRectangle.Y) /
577            (double)mRectangle.Height, 1) * height + y - cn.Point.Y;
578
579        p = new Point(Convert.ToInt32(a), Convert.ToInt32(b));
580        cn.MoveBy(p);
581      }
582
583      mRectangle = new Rectangle(x, y, width, height);
584
585      RaiseOnChange(this, new EntityEventArgs(this));
586
587      // Update the material; the gradient depends on the rectangle
588      UpdatePaintingMaterial();
589      Invalidate(before);
590
591      // If we're attached to a group, make sure to let him know
592      // we've moved/resized.
593      if (mGroup != null) {
594        mGroup.CalculateRectangle();
595        mGroup.Invalidate();
596
597        // The group will invalidate us when it's invalidated so just
598        // stop now.
599        //return;
600      }
601
602      Invalidate(mRectangle);
603    }
604
605    // ------------------------------------------------------------------
606    /// <summary>
607    /// Transforms the entity to the given new rectangle.
608    /// </summary>
609    /// <param name="rectangle">The new bounds.</param>
610    // ------------------------------------------------------------------
611    public virtual void Transform(Rectangle rectangle) {
612      Transform(
613          rectangle.X,
614          rectangle.Y,
615          rectangle.Width,
616          rectangle.Height);
617    }
618
619    #endregion
620  }
621}
Note: See TracBrowser for help on using the repository browser.