Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2817-BinPackingSpeedup/HeuristicLab.ExtLibs/HeuristicLab.Netron/3.0.2672.12446/Netron.Diagramming.Core-3.0.2672.12446/Diagram elements/Group/CollapsibleGroupShape.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: 17.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Drawing;
4using System.Windows.Forms;
5namespace Netron.Diagramming.Core {
6  // ----------------------------------------------------------------------
7  /// <summary>
8  /// This is an extension of the <see cref="GroupShape"/> which paints
9  /// itself and allows to collapse/expand its content. Note that it does
10  /// not inherit from the <see cref="GroupShape"/> though because it requires
11  /// the <see cref="ShapeMaterialBase"/> mechanism and, hence, inherits
12  /// from the <see cref="ComplexShapeBase"/> class.
13  /// </summary>
14  // ----------------------------------------------------------------------
15  public partial class CollapsibleGroupShape : ComplexShapeBase, IGroup {
16    #region Fields
17
18    // ------------------------------------------------------------------
19    /// <summary>
20    /// Implementation of IVersion - the current version of
21    /// CollapsibleGroupShape.
22    /// </summary>
23    // ------------------------------------------------------------------
24    protected const double collapsibleGroupShapeVersion = 1.0;
25
26    // ------------------------------------------------------------------
27    /// <summary>
28    /// the Entities field
29    /// </summary>
30    // ------------------------------------------------------------------
31    private CollectionBase<IDiagramEntity> mEntities;
32
33    // ------------------------------------------------------------------
34    /// <summary>
35    /// in essence, whether the group shape should be painted
36    /// </summary>
37    // ------------------------------------------------------------------
38    private bool mEmphasizeGroup = true;
39
40    // ------------------------------------------------------------------
41    /// <summary>
42    /// the collapse/expand icon
43    /// </summary>
44    // ------------------------------------------------------------------
45    private SwitchIconMaterial xicon;
46
47    // ------------------------------------------------------------------
48    /// <summary>
49    /// the rectangle before it's being collapsed
50    /// </summary>
51    // ------------------------------------------------------------------
52    private Rectangle rectangleMemory;
53
54    // ------------------------------------------------------------------
55    /// <summary>
56    /// the location memory of the externally connected connectors
57    /// </summary>
58    // ------------------------------------------------------------------
59    private Dictionary<IConnector, Point> connectorMemory;
60
61    // ------------------------------------------------------------------
62    /// <summary>
63    /// global offset when a collapsed group is moved around
64    /// </summary>
65    // ------------------------------------------------------------------
66    private Point groupConnectorOffset = Point.Empty;
67
68    // ------------------------------------------------------------------
69    /// <summary>
70    /// the Collapsed field
71    /// </summary>
72    // ------------------------------------------------------------------
73    private bool mCollapsed;
74
75    // ------------------------------------------------------------------
76    /// <summary>
77    /// the expand hyperlink
78    /// </summary>
79    // ------------------------------------------------------------------
80    private ClickableLabelMaterial expandLabel;
81
82    // ------------------------------------------------------------------
83    /// <summary>
84    /// Specifies if ungrouping is allowed.
85    /// </summary>
86    // ------------------------------------------------------------------
87    bool mAllowUnGroup = false;
88
89    #endregion
90
91    #region Properties
92
93    // ------------------------------------------------------------------
94    /// <summary>
95    /// Gets the current version.
96    /// </summary>
97    // ------------------------------------------------------------------
98    public override double Version {
99      get {
100        return collapsibleGroupShapeVersion;
101      }
102    }
103
104    // ------------------------------------------------------------------
105    /// <summary>
106    /// Gets or sets if un-grouping of the entities is allowed.
107    /// </summary>
108    // ------------------------------------------------------------------
109    public virtual bool CanUnGroup {
110      get {
111        return mAllowUnGroup;
112      }
113      set {
114        mAllowUnGroup = value;
115      }
116    }
117
118    /// <summary>
119    /// Gets or sets whether the group is in a collapsed state
120    /// </summary>
121    public bool Collapsed {
122      get { return mCollapsed; }
123      set { mCollapsed = value; }
124    }
125
126
127    /// <summary>
128    /// Gets or sets whether the group as a shape should be painted on the canvas.
129    /// </summary>
130    /// <value><c>true</c> to paint the group shape; otherwise, <c>false</c>.</value>
131    public bool EmphasizeGroup {
132      get { return mEmphasizeGroup; }
133      set { mEmphasizeGroup = value; }
134    }
135    /// <summary>
136    /// Gets the friendly name of the entity to be displayed in the UI
137    /// </summary>
138    /// <value></value>
139    public override string EntityName {
140      get {
141        return "Group shape";
142      }
143    }
144
145    /// <summary>
146    /// Gets or sets the rectangle.
147    /// </summary>
148    /// <value>The rectangle.</value>
149    public override Rectangle Rectangle {
150      get {
151        return mRectangle;
152      }
153    }
154    /// <summary>
155    /// Gets or sets the entities directly underneath. To get the whole branch of entities in case of nested groups, <see cref="Leafs"/>.
156    /// </summary>
157    public CollectionBase<IDiagramEntity> Entities {
158      get { return mEntities; }
159      set {
160
161        throw new InconsistencyException("You cannot set the entities, use the already instantiated collection to add or remove items.");
162
163
164      }
165    }
166    /// <summary>
167    /// Gets the whole branch of entities if this group has sub-groups. Contrary to the <see cref="GroupShape"/> the result WILLl contain the group shapes.
168    /// </summary>
169    /// <value>The branch.</value>
170    public CollectionBase<IDiagramEntity> Leafs {
171      get {
172        CollectionBase<IDiagramEntity> flatList = new CollectionBase<IDiagramEntity>();
173        foreach (IDiagramEntity entity in mEntities) {
174          if (entity is IGroup) {
175            Utils.TraverseCollect(entity as IGroup, ref flatList);
176          }
177          flatList.Add(entity);
178        }
179        return flatList;
180      }
181    }
182
183    #endregion
184
185    #region Constructor
186    ///<summary>
187    ///Default constructor
188    ///</summary>
189    public CollapsibleGroupShape(IModel model)
190      : base(model) {
191      this.mEntities = new CollectionBase<IDiagramEntity>();
192      this.mEntities.OnItemAdded += new EventHandler<CollectionEventArgs<IDiagramEntity>>(mEntities_OnItemAdded);
193      this.mEntities.OnClear += new EventHandler(mEntities_OnClear);
194      this.mEntities.OnItemRemoved += new EventHandler<CollectionEventArgs<IDiagramEntity>>(mEntities_OnItemRemoved);
195
196      xicon = new SwitchIconMaterial(SwitchIconType.PlusMinus);
197      this.Children.Add(xicon);
198      //do this before attaching the next event handler, otherwise the shape will be added twice to the paintables
199      xicon.Collapsed = false;
200
201      xicon.OnCollapse += new EventHandler(xicon_OnCollapse);
202      xicon.OnExpand += new EventHandler(xicon_OnExpand);
203
204      connectorMemory = new Dictionary<IConnector, Point>();
205
206      expandLabel = new ClickableLabelMaterial("Expand...");
207      this.Children.Add(expandLabel);
208      expandLabel.Visible = false;
209      expandLabel.OnClick += new EventHandler(expandLabel_OnClick);
210    }
211
212    void expandLabel_OnClick(object sender, EventArgs e) {
213      xicon.Collapsed = false;
214    }
215
216    void xicon_OnExpand(object sender, EventArgs e) {
217      Expand();
218    }
219
220    void xicon_OnCollapse(object sender, EventArgs e) {
221      Collapse();
222    }
223    #endregion
224
225    #region Methods
226
227    /// <summary>
228    /// Collapses this instance.
229    /// </summary>
230    public void Collapse() {
231      rectangleMemory = mRectangle;
232      mRectangle = new Rectangle(mRectangle.Location, new Size(110, 25));
233
234      foreach (IDiagramEntity entity in Entities) {
235        RemoveFromPaintables(entity);
236      }
237      this.Resizable = false;
238
239
240      connectorMemory.Clear();//forget the previous memory
241      groupConnectorOffset = Point.Empty;
242      //use the flattened collection (Leafs) in case there are sub-groups
243      MoveConnectors(Leafs, new Point((int)(Rectangle.Left + Rectangle.Width / 2), Rectangle.Bottom));
244
245      Rectangle rec = rectangleMemory;
246      rec.Inflate(20, 20);
247      Invalidate(rec);
248      mCollapsed = true;
249      expandLabel.Visible = true;
250    }
251    internal void RemoveFromPaintables(IDiagramEntity entity) {
252      this.Model.Paintables.Remove(entity);
253      if (entity is CollapsibleGroupShape && !(entity as CollapsibleGroupShape).Collapsed) {
254        foreach (IDiagramEntity ent in (entity as CollapsibleGroupShape).Entities) {
255          RemoveFromPaintables(ent);
256        }
257
258      }
259    }
260
261    internal void AddToPaintables(IDiagramEntity entity) {
262      this.Model.Paintables.Add(entity);
263      if (entity is CollapsibleGroupShape && !(entity as CollapsibleGroupShape).Collapsed) {
264        foreach (IDiagramEntity ent in (entity as CollapsibleGroupShape).Entities) {
265          AddToPaintables(ent);
266        }
267
268      }
269
270    }
271    /// <summary>
272    /// Expands this instance.
273    /// </summary>
274    public void Expand() {
275      mRectangle = rectangleMemory;
276      foreach (IDiagramEntity entity in Entities) {
277        AddToPaintables(entity);
278      }
279      this.Resizable = true;
280      UnMoveConnectors();
281      Invalidate(mRectangle);
282      mCollapsed = false;
283      connectorMemory.Clear();
284      expandLabel.Visible = false;
285    }
286
287
288
289    /// <summary>
290    /// Moves the connectors of the children to the central group connector location.
291    /// </summary>
292    internal void MoveConnectors(CollectionBase<IDiagramEntity> entities, Point point) {
293      IConnection cnn;
294      foreach (IDiagramEntity entity in entities) {
295        if (entity is IGroup) {
296          continue; //since we use a flattened collection (the Leafs) we don't care about the groups here
297          //the inclusion of the subgroups in the Leafs is however important to make the subgroups
298          //(in)visible when collapsed/expanded.
299        }
300        if (entity is IShape) {
301          foreach (IConnector cn in (entity as IShape).Connectors) {
302            if (cn.AttachedConnectors.Count > 0) {
303              foreach (IConnector cn2 in cn.AttachedConnectors) {
304                if (cn2.Parent is IConnection) {
305                  cnn = cn2.Parent as IConnection;
306                  //the ends have to be connected
307                  if (cnn.From.AttachedTo.Parent is IShape && cnn.To.AttachedTo.Parent is IShape) {
308                    if (entities.Contains(cnn.From.AttachedTo.Parent as IShape) && entities.Contains(cnn.To.AttachedTo.Parent as IShape))
309                      continue;//both endconnectors are internal
310                    else//one of the connectors is external
311                                        {
312                      if (entities.Contains(cnn.From.AttachedTo.Parent as IShape)) //the From is internal
313                                            {
314                        MoveConnector(cnn.From, point);
315                      } else //the To is internal                           
316                                            {
317                        MoveConnector(cnn.To, point);
318                      }
319                    }
320                  }
321                }
322              }
323            }
324          }
325        }
326      }
327    }
328
329    /// <summary>
330    /// Moves the connector to the given location.
331    /// </summary>
332    /// <param name="cn">The cn.</param>
333    /// <param name="point">The point.</param>
334    private void MoveConnector(IConnector cn, Point point) {
335      connectorMemory.Add(cn, cn.Point);//luckily the Point is a struct so the value will be actually copied and we can safely change the value next
336      cn.MoveBy(new Point(point.X - cn.Point.X, point.Y - cn.Point.Y));
337      cn.Enabled = false;
338    }
339
340    /// <summary>
341    /// Moves the connectors back from the central group connector to their original location.
342    /// </summary>
343    private void UnMoveConnectors() {
344      //some things will go wrong here if the content of the Entities has changed
345
346      //recall the memory and move them;
347
348      Dictionary<IConnector, Point>.KeyCollection keys = connectorMemory.Keys;
349      Point p;
350      foreach (IConnector cn in keys) {
351        p = connectorMemory[cn];
352        cn.MoveBy(new Point(p.X - cn.Point.X + groupConnectorOffset.X, p.Y - cn.Point.Y + groupConnectorOffset.Y));
353        cn.Enabled = true;
354      }
355    }
356
357    /// <summary>
358    /// The custom menu to be added to the base menu of this entity
359    /// </summary>
360    /// <returns>ToolStripItem[]</returns>
361    public override ToolStripItem[] Menu() {
362      return null;
363    }
364    /// <summary>
365    /// Handles the OnItemRemoved of the Entities
366    /// </summary>
367    /// <param name="sender">The sender.</param>
368    /// <param name="e">The e.</param>
369    void mEntities_OnItemRemoved(object sender, CollectionEventArgs<IDiagramEntity> e) {
370      CalculateRectangle();
371    }
372
373    /// <summary>
374    /// Handles the OnClear event of the Entities.
375    /// </summary>
376    /// <param name="sender">The source of the event.</param>
377    /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
378    void mEntities_OnClear(object sender, EventArgs e) {
379      mRectangle = Rectangle.Empty;
380    }
381
382    /// <summary>
383    /// Handles the OnItemAdded event of the Entities
384    /// </summary>
385    /// <param name="sender">The sender.</param>
386    /// <param name="e">The e.</param>
387    void mEntities_OnItemAdded(object sender, CollectionEventArgs<IDiagramEntity> e) {
388      //if(mEntities.Count == 1)
389      //    mRectangle = e.Item.Rectangle;
390      //else
391      //{
392      //    mRectangle = Rectangle.Union((Rectangle) mRectangle, e.Item.Rectangle);
393      //}
394      CalculateRectangle();
395    }
396
397    /// <summary>
398    /// Calculates the bounding rectangle of this group.
399    /// </summary>
400    public void CalculateRectangle() {
401      if (mEntities == null || mEntities.Count == 0)
402        return;
403      Rectangle rec = mEntities[0].Rectangle;
404      foreach (IDiagramEntity entity in Entities) {
405        //cascade the calculation if necessary
406        if ((entity is CollapsibleGroupShape) && !(entity as CollapsibleGroupShape).Collapsed) (entity as IGroup).CalculateRectangle();
407
408        rec = Rectangle.Union(rec, entity.Rectangle);
409      }
410      rec.Inflate(20, 20);
411      this.mRectangle = rec;
412      rec.Offset(5, 5);
413      xicon.Transform(new Rectangle(rec.Location, new Size(16, 16)));
414
415      expandLabel.Transform(new Rectangle(rec.Location.X + 20, rec.Location.Y, 50, 12));
416    }
417
418
419    /// <summary>
420    /// Paints the entity on the control
421    /// <remarks>This method should not be called since the painting occurs via the <see cref="Model.Paintables"/>.</remarks>
422    /// </summary>
423    /// <param name="g"></param>
424    public override void Paint(System.Drawing.Graphics g) {
425      Rectangle rec = Rectangle;
426      Utils.DrawRoundRect(g, ArtPalette.ConnectionShadow, rec);
427
428      base.Paint(g);
429    }
430
431    /// <summary>
432    /// Tests whether the group is hit by the mouse
433    /// </summary>
434    /// <param name="p"></param>
435    /// <returns></returns>
436    public override bool Hit(System.Drawing.Point p) {
437      if (mCollapsed)
438        return mRectangle.Contains(p);
439
440      foreach (IDiagramEntity entity in mEntities) {
441        if (entity.Hit(p))
442          return true;
443      }
444      return false;
445    }
446
447    /// <summary>
448    /// Invalidates the entity
449    /// </summary>
450    public override void Invalidate() {
451
452      if (mRectangle == null)
453        return;
454
455      Rectangle rec = mRectangle;
456      rec.Inflate(20, 20);
457      Model.RaiseOnInvalidateRectangle(rec);
458    }
459
460    /// <summary>
461    /// Moves the entity on the canvas
462    /// </summary>
463    /// <param name="p"></param>
464    public override void MoveBy(System.Drawing.Point p) {
465      base.MoveBy(p);
466
467      Rectangle recBefore = mRectangle;
468      recBefore.Inflate(20, 20);
469
470      //no need to invalidate since it'll be done by the individual move actions
471      foreach (IDiagramEntity entity in mEntities) {
472        entity.MoveBy(p);
473      }
474
475      mRectangle.X += p.X;
476      mRectangle.Y += p.Y;
477      //shift the latent copy of the real rectangle if the group is in a collapsed state
478      if (mCollapsed) {
479        rectangleMemory.X += p.X;
480        rectangleMemory.Y += p.Y;
481        //the global offset has to be memorized; when we'll expand the group this offset has to be added to the original values
482        groupConnectorOffset.Offset(p);
483      }
484
485
486      //refresh things
487      this.Invalidate(recBefore);//position before the move
488      this.Invalidate();//current position
489
490    }
491
492    public override bool MouseDown(MouseEventArgs e) {
493      // Was a material hit in the base ComplexShape?
494      if (base.MouseDown(e)) {
495        return true;
496      }
497
498      // If not, should we pass it on to the entities in this
499      // group?
500      foreach (IDiagramEntity entity in this.mEntities) {
501        if (entity.Hit(e.Location)) {
502          if (entity.MouseDown(e)) {
503            return true;
504          }
505        }
506      }
507
508      return false;
509    }
510
511
512    #endregion
513  }
514
515
516}
Note: See TracBrowser for help on using the repository browser.