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/BaseClasses/ShapeBase.cs @ 2768

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

added solution folders and sources for the netron library (ticket #867)

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