[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / FreeFormEditing / FreeFormPanel.cs
1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4
5 namespace System.Activities.Presentation.FreeFormEditing
6 {
7     using System;
8     using System.Collections.Generic;
9     using System.Windows;
10     using System.Windows.Controls;
11     using System.Windows.Documents;
12     using System.Windows.Input;
13     using System.Windows.Media;
14     using System.Windows.Threading;
15     using System.Activities.Presentation;
16     using System.Activities.Presentation.View;
17     using System.Activities.Presentation.Internal.PropertyEditing;
18     using System.Runtime;
19     using System.Linq;
20     using System.Activities.Presentation.Model;
21
22     internal class FreeFormPanel : Panel
23     {
24         public static readonly DependencyProperty ChildSizeProperty = DependencyProperty.RegisterAttached("ChildSize", typeof(Size), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
25         public static readonly DependencyProperty LocationProperty = DependencyProperty.RegisterAttached("Location", typeof(Point), typeof(FreeFormPanel), new FrameworkPropertyMetadata(new Point(-1, -1)));
26         public static readonly DependencyProperty RequiredWidthProperty = DependencyProperty.Register("RequiredWidth", typeof(Double), typeof(FreeFormPanel), new FrameworkPropertyMetadata(double.NaN));
27         public static readonly DependencyProperty RequiredHeightProperty = DependencyProperty.Register("RequiredHeight", typeof(Double), typeof(FreeFormPanel), new FrameworkPropertyMetadata(double.NaN));
28         public static readonly DependencyProperty DestinationConnectionPointProperty = DependencyProperty.RegisterAttached("DestinationConnectionPoint", typeof(ConnectionPoint), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
29         public static readonly DependencyProperty SourceConnectionPointProperty = DependencyProperty.RegisterAttached("SourceConnectionPoint", typeof(ConnectionPoint), typeof(FreeFormPanel), new FrameworkPropertyMetadata());
30         public static readonly DependencyProperty DisabledProperty = DependencyProperty.Register("Disabled", typeof(bool), typeof(FreeFormPanel), new UIPropertyMetadata(false));
31         public static readonly DependencyProperty AutoConnectContainerProperty = DependencyProperty.Register("AutoConnectContainer", typeof(IAutoConnectContainer), typeof(FreeFormPanel), new UIPropertyMetadata(null));
32
33         public const double ConnectorEditorOpacity = 1.0;
34         public const double ConnectorEditorThickness = 1.5;
35         public const double LeftStackingMargin = 50;
36         public const double TopStackingMargin = 80;
37         public const double VerticalStackingDistance = 50;
38         public const double GridSize = 10;
39         public ConnectorEditor connectorEditor;
40         double lastYPosition;
41         bool measureConnectors = false;
42         bool measureConnectorsPosted = false;
43         AutoConnectHelper autoConnectHelper = null;
44         DesignerConfigurationService designerConfigurationService = null;
45
46         public FreeFormPanel()
47         {
48             connectorEditor = null;
49             this.autoConnectHelper = new AutoConnectHelper(this);
50             lastYPosition = FreeFormPanel.TopStackingMargin;
51
52             this.Unloaded += (sender, e) =>
53             {
54                 this.RemoveConnectorEditor();
55             };
56         }
57
58         public event LocationChangedEventHandler LocationChanged;
59         public event ConnectorMovedEventHandler ConnectorMoved;
60         public event RequiredSizeChangedEventHandler RequiredSizeChanged;
61
62         public static Size GetChildSize(DependencyObject obj)
63         {
64             return (Size)obj.GetValue(FreeFormPanel.ChildSizeProperty);
65         }
66
67         public static void SetChildSize(DependencyObject obj, Size size)
68         {
69             obj.SetValue(FreeFormPanel.ChildSizeProperty, size);
70         }
71
72         public double RequiredHeight
73         {
74             get { return (double)GetValue(FreeFormPanel.RequiredHeightProperty); }
75             private set { SetValue(FreeFormPanel.RequiredHeightProperty, value); }
76         }
77
78         public double RequiredWidth
79         {
80             get { return (double)GetValue(FreeFormPanel.RequiredWidthProperty); }
81             private set { SetValue(FreeFormPanel.RequiredWidthProperty, value); }
82         }
83
84         public bool Disabled
85         {
86             get { return (bool)GetValue(DisabledProperty); }
87             set { SetValue(DisabledProperty, value); }
88         }
89
90         public IAutoConnectContainer AutoConnectContainer
91         {
92             get { return (IAutoConnectContainer)GetValue(AutoConnectContainerProperty); }
93             set { SetValue(AutoConnectContainerProperty, value); }
94         }
95
96         public static Vector CalculateMovement(Key key, bool isRightToLeft)
97         {
98             Vector moveDir;
99             switch (key)
100             {
101                 case Key.Down:
102                     moveDir = new Vector(0, FreeFormPanel.GridSize);
103                     break;
104                 case Key.Up:
105                     moveDir = new Vector(0, -FreeFormPanel.GridSize);
106                     break;
107                 case Key.Right:
108                     moveDir = new Vector(FreeFormPanel.GridSize, 0);
109                     break;
110                 case Key.Left:
111                     moveDir = new Vector(-FreeFormPanel.GridSize, 0);
112                     break;
113                 default:
114                     Fx.Assert(false, "Invalid case");
115                     moveDir = new Vector(0, 0);
116                     break;
117             }
118
119             if (isRightToLeft)
120             {
121                 moveDir.X = -moveDir.X;
122             }
123
124             return moveDir;
125         }
126
127         public static double ZeroIfNegative(double val)
128         {
129             return val.IsNoGreaterThan(0) ? 0 : val;
130         }
131
132         internal UIElement CurrentAutoConnectTarget
133         {
134             get
135             {
136                 return this.autoConnectHelper.CurrentTarget;
137             }
138         }
139
140         internal Connector CurrentAutoSplitTarget
141         {
142             get;
143             set;
144         }
145
146         bool AutoConnectEnabled
147         {
148             get
149             {
150                 if (this.designerConfigurationService == null)
151                 {
152                     DesignerView view = VisualTreeUtils.FindVisualAncestor<DesignerView>(this);
153                     if (view != null)
154                     {
155                         this.designerConfigurationService = view.Context.Services.GetService<DesignerConfigurationService>();
156                         return this.designerConfigurationService.AutoConnectEnabled;
157                     }
158                     else
159                     {
160                         return false;
161                     }
162                 }
163                 else
164                 {
165                     return this.designerConfigurationService.AutoConnectEnabled;
166                 }
167             }
168         }
169
170         public static ConnectionPoint GetDestinationConnectionPoint(DependencyObject obj)
171         {
172             return (ConnectionPoint)obj.GetValue(FreeFormPanel.DestinationConnectionPointProperty);
173         }
174
175         public static void SetDestinationConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
176         {
177             obj.SetValue(FreeFormPanel.DestinationConnectionPointProperty, connectionPoint);
178         }
179
180         public static ConnectionPoint GetSourceConnectionPoint(DependencyObject obj)
181         {
182             return (ConnectionPoint)obj.GetValue(FreeFormPanel.SourceConnectionPointProperty);
183         }
184
185         public static void SetSourceConnectionPoint(DependencyObject obj, ConnectionPoint connectionPoint)
186         {
187             obj.SetValue(FreeFormPanel.SourceConnectionPointProperty, connectionPoint);
188         }
189
190         public static Point GetLocation(DependencyObject obj)
191         {
192             return (Point)obj.GetValue(FreeFormPanel.LocationProperty);
193         }
194
195         public static void SetLocation(DependencyObject obj, Point point)
196         {
197             obj.SetValue(FreeFormPanel.LocationProperty, point);
198         }
199
200         protected override void OnInitialized(EventArgs e)
201         {
202             base.OnInitialized(e);
203             this.SnapsToDevicePixels = true;
204             this.AllowDrop = true;
205         }
206
207         internal void RemoveAutoConnectAdorner()
208         {
209             this.autoConnectHelper.RemoveDropTargets();
210         }
211
212         internal List<DependencyObject> GetChildShapes(DependencyObject excluded)
213         {
214             List<DependencyObject> children = new List<DependencyObject>();
215             foreach (UIElement element in this.Children)
216             {
217                 if (element is Connector)
218                 {
219                     continue;
220                 }
221                 if (object.Equals(element, excluded))
222                 {
223                     continue;
224                 }
225                 else if (element is VirtualizedContainerService.VirtualizingContainer)
226                 {
227                     if (object.Equals(excluded, ((VirtualizedContainerService.VirtualizingContainer)element).Child))
228                     {
229                         continue;
230                     }
231                 }
232                 children.Add(element);
233             }
234             return children;
235         }
236
237         protected override void OnPreviewDragOver(DragEventArgs e)
238         {
239             if (this.IsOutmostPanel())
240             {
241                 if (this.AutoConnectEnabled && DragDropHelper.GetDraggedObjectCount(e) == 1)
242                 {
243                     this.autoConnectHelper.OnPreviewDragOverPanel(e);
244                 }
245             }
246             base.OnPreviewDragOver(e);
247         }
248
249         public void UpdateConnectorPoints(Connector connector, List<Point> points)
250         {
251             PointCollection pointCollection = new PointCollection();
252             foreach (Point point in points)
253             {
254                 pointCollection.Add(new Point(point.X < 0 ? 0 : point.X, point.Y < 0 ? 0 : point.Y));
255             }
256             connector.Points = pointCollection;
257             OnLocationChanged(connector, null);
258         }
259
260         static public List<Point> GetEdgeRelativeToOutmostPanel(ConnectionPoint connectionPoint)
261         {
262             return connectionPoint.Edge;
263         }
264
265         static public Point GetLocationRelativeToOutmostPanel(ConnectionPoint connectionPoint)
266         {
267             return connectionPoint.Location;
268         }
269
270         public Point GetLocationRelativeToOutmostPanel(Point location)
271         {
272             return this.TranslatePoint(location, this.GetOutmostPanel());
273         }
274
275         FreeFormPanel GetOutmostPanel()
276         {
277             DependencyObject obj = this;
278             do
279             {
280                 obj = VisualTreeHelper.GetParent(obj);
281             }
282             while (obj != null && !typeof(INestedFreeFormPanelContainer).IsAssignableFrom(obj.GetType()));
283
284             if (obj != null)
285             {
286                 INestedFreeFormPanelContainer container = (INestedFreeFormPanelContainer)obj;
287                 if (container.GetChildFreeFormPanel() == this)
288                 {
289                     return container.GetOutmostFreeFormPanel();
290                 }
291             }
292             return this;
293         }
294
295         internal bool IsOutmostPanel()
296         {
297             return this == this.GetOutmostPanel();
298         }
299
300         internal static ConnectionPoint ConnectionPointHitTest(Point hitPoint, ConnectionPointsAdorner adorner)
301         {
302             FreeFormPanel panel = VisualTreeUtils.FindVisualAncestor<FreeFormPanel>(adorner.AdornedElement);
303             return ConnectionPointHitTest(hitPoint, adorner.ConnectionPoints, panel);
304         }
305
306         internal static ConnectionPoint ConnectionPointHitTest(Point hitPoint, List<ConnectionPoint> connectionPoints, FreeFormPanel panel)
307         {
308             ConnectionPoint hitConnectionPoint = null;
309             FreeFormPanel outmost = panel.GetOutmostPanel();
310             foreach (ConnectionPoint connPoint in connectionPoints)
311             {
312                 if (connPoint != null && connPoint.IsEnabled)
313                 {
314                     if (new Rect(panel.TranslatePoint(connPoint.Location, outmost) + connPoint.HitTestOffset, connPoint.HitTestSize).Contains(hitPoint))
315                     {
316                         hitConnectionPoint = connPoint;
317                         break;
318                     }
319                 }
320             }
321             return hitConnectionPoint;
322         }
323
324         protected override Size ArrangeOverride(Size finalSize)
325         {
326             double height = 0;
327             double width = 0;
328             for (int i = 0; i < Children.Count; i++)
329             {
330                 Point pt = new Point(0, 0);
331                 Size size = Children[i].DesiredSize;
332                 if (Children[i].GetType() == typeof(Connector))
333                 {
334                     ((UIElement)Children[i]).Arrange(new Rect(pt, size));
335                 }
336                 else
337                 {
338                     pt = FreeFormPanel.GetLocation(Children[i]);
339                     ((UIElement)Children[i]).Arrange(new Rect(pt, size));
340                 }
341                 if (width < (size.Width + pt.X))
342                 {
343                     width = size.Width + pt.X;
344                 }
345                 if (height < (size.Height + pt.Y))
346                 {
347                     height = size.Height + pt.Y;
348                 }
349             }
350             width = (width < this.MinWidth) ? this.MinWidth : width;
351             width = (width < this.Width) ? (this.Width < Double.MaxValue ? this.Width : width) : width;
352
353             height = (height < this.MinHeight) ? this.MinHeight : height;
354             height = (height < this.Height) ? (this.Height < Double.MaxValue ? this.Height : height) : height;
355
356             return new Size(width, height);
357         }
358
359         protected override Size MeasureOverride(Size availableSize)
360         {
361             bool isOutmostPanel = this.IsOutmostPanel();
362             base.MeasureOverride(availableSize);
363             double height;
364             double width;
365             this.MeasureChildren(out height, out width);
366             if (this.RequiredSizeChanged != null)
367             {
368                 this.RequiredSizeChanged(this, new RequiredSizeChangedEventArgs(new Size(width, height)));
369             }
370             this.RequiredWidth = width;
371             this.RequiredHeight = height;
372
373             if (isOutmostPanel)
374             {
375                 Action MeasureConnectors = () =>
376                 {
377                     //This action will execute at Input priority. 
378                     //Enabling measuring on Connectors and forcing a MeasureOverride by calling InvalidateMeasure.
379                     this.measureConnectors = true;
380                     this.InvalidateMeasure();
381                 };
382                 if (!measureConnectorsPosted)
383                 {
384                     this.Dispatcher.BeginInvoke(DispatcherPriority.Input, MeasureConnectors);
385                     measureConnectorsPosted = true;
386                 }
387                 if (measureConnectors)
388                 {
389                     measureConnectors = false;
390                     measureConnectorsPosted = false;
391                 }
392             }
393             width = (width < this.Width) ? (this.Width < Double.MaxValue ? this.Width : width) : width;
394             height = (height < this.Height) ? (this.Height < Double.MaxValue ? this.Height : height) : height;
395             return new Size(width, height);
396         }
397
398         private void MeasureChildren(out double height, out double width)
399         {
400             height = 0;
401             width = 0;
402             Point pt = new Point(0, 0);
403             bool isOutmostPanel = this.IsOutmostPanel();
404             foreach (UIElement child in Children)
405             {
406                 Connector connectorChild = child as Connector;
407                 if (connectorChild != null && isOutmostPanel)
408                 {
409                     pt = new Point(0, 0);
410
411                     if (measureConnectors)
412                     {
413                         Point srcPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(FreeFormPanel.GetSourceConnectionPoint(connectorChild));
414                         Point destPoint = FreeFormPanel.GetLocationRelativeToOutmostPanel(FreeFormPanel.GetDestinationConnectionPoint(connectorChild));
415                         if (connectorChild.Points.Count == 0 || !this.Disabled &&
416                             ((DesignerGeometryHelper.ManhattanDistanceBetweenPoints(connectorChild.Points[0], srcPoint) > ConnectorRouter.EndPointTolerance)
417                             || (DesignerGeometryHelper.ManhattanDistanceBetweenPoints(connectorChild.Points[connectorChild.Points.Count - 1], destPoint) > ConnectorRouter.EndPointTolerance)))
418                         {
419                             connectorChild.Points = new PointCollection();
420                             RoutePolyLine(connectorChild);
421                         }
422                         connectorChild.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
423                     }
424                     else
425                     {
426                         continue;
427                     }
428                 }
429                 else //Measure non-connector elements.
430                 {
431                     child.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
432                     if (!child.DesiredSize.Equals(((Size)FreeFormPanel.GetChildSize(child))))
433                     {
434                         FreeFormPanel.SetChildSize(child, child.DesiredSize);
435                     }
436                     pt = FreeFormPanel.GetLocation(child);
437                     if (!IsLocationValid(pt))
438                     {
439                         pt = new Point(LeftStackingMargin, lastYPosition);
440                         OnLocationChanged(child, new LocationChangedEventArgs(pt));
441                         FreeFormPanel.SetLocation(child, pt);
442                         lastYPosition += child.DesiredSize.Height + VerticalStackingDistance;
443                     }
444                 }
445                 if (height < child.DesiredSize.Height + pt.Y)
446                 {
447                     height = child.DesiredSize.Height + pt.Y;
448                 }
449                 if (width < child.DesiredSize.Width + pt.X)
450                 {
451                     width = child.DesiredSize.Width + pt.X;
452                 }
453             }
454
455             width = (width < this.MinWidth) ? this.MinWidth : width;
456             height = (height < this.MinHeight) ? this.MinHeight : height;
457         }
458
459         static bool IsLocationValid(Point location)
460         {
461             return location.X >= 0 && location.Y >= 0;
462         }
463
464         void OnLocationChanged(Object sender, LocationChangedEventArgs e)
465         {
466             if (LocationChanged != null)
467             {
468                 LocationChanged(sender, e);
469             }
470         }
471
472         protected override void OnMouseLeave(MouseEventArgs e)
473         {
474             if (e != null && !this.Disabled && this.IsOutmostPanel())
475             {
476                 if (connectorEditor != null && connectorEditor.BeingEdited
477                     && Mouse.DirectlyOver != null
478                     && !(Mouse.DirectlyOver is ConnectionPointsAdorner))
479                 {
480                     SaveConnectorEditor(e.GetPosition(this));
481                 }
482             }
483             base.OnMouseLeave(e);
484         }
485
486         protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
487         {
488             if (e != null && !this.Disabled && this.IsOutmostPanel())
489             {
490                 if (this.AutoConnectEnabled)
491                 {
492                     this.RemoveAutoConnectAdorner();
493                 }
494                 if (e.LeftButton == MouseButtonState.Pressed)
495                 {
496                     if (connectorEditor != null && connectorEditor.BeingEdited)
497                     {
498                         AutoScrollHelper.AutoScroll(e, this, 1);
499                         connectorEditor.Update(e.GetPosition(this));
500                         e.Handled = true;
501                     }
502                 }
503             }
504             base.OnMouseMove(e);
505         }
506
507         protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
508         {
509             if (e != null && !this.Disabled && this.IsOutmostPanel())
510             {
511                 if (connectorEditor != null && connectorEditor.BeingEdited)
512                 {
513                     SaveConnectorEditor(e.GetPosition(this));
514                 }
515             }
516             base.OnMouseLeftButtonUp(e);
517         }
518
519         public void RemoveConnectorEditor()
520         {
521             if (connectorEditor != null)
522             {
523                 connectorEditor.Remove();
524                 connectorEditor = null;
525             }
526
527         }
528
529         protected override void OnKeyDown(KeyEventArgs e)
530         {
531             if (e != null && !this.Disabled && this.IsOutmostPanel())
532             {
533                 if (connectorEditor != null && connectorEditor.BeingEdited)
534                 {
535                     if (e.Key == Key.Escape)
536                     {
537                         //If escape key is hit while dragging a connector, end dragging.
538
539                         Connector affectedConnector = connectorEditor.Connector;
540                         RemoveConnectorEditor();
541                         this.connectorEditor = new ConnectorEditor(this, affectedConnector);
542                     }
543
544                     // Ignore all other Keyboard input when rerouting connector
545                     e.Handled = true;
546                 }
547             }
548
549             base.OnKeyDown(e);
550         }
551
552         static bool ShouldCreateNewConnectorEditor(MouseButtonEventArgs e)
553         {
554             Connector connector = e.Source as Connector;
555             // Don't create new connector editor when clicking on the start dot.
556             if (connector == null || (connector.StartDot != null && connector.StartDot.IsAncestorOf(e.MouseDevice.DirectlyOver as DependencyObject)))
557             {
558                 return false;
559             }
560             return true;
561         }
562
563         protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
564         {
565             if (e != null && !this.Disabled && this.IsOutmostPanel() && e.ClickCount == 1)
566             {
567                 //If one of the edit points is clicked, update the connector editor.
568                 if ((connectorEditor != null) && connectorEditor.EditPointsHitTest(e.GetPosition(this)))
569                 {
570                     connectorEditor.Update(e.GetPosition(this));
571                     e.Handled = true;
572                 }
573                 else if (ShouldCreateNewConnectorEditor(e))
574                 {
575                     CreateNewConnectorEditor(e);
576                 }
577             }
578             base.OnPreviewMouseLeftButtonDown(e);
579         }
580
581         void CreateNewConnectorEditor(MouseButtonEventArgs e)
582         {
583             if (connectorEditor == null || !e.Source.Equals(connectorEditor.Connector))
584             {
585                 //If user clicks anywhere other than the connector editor, destroy it.
586                 RemoveConnectorEditor();
587                 if (typeof(Connector).IsAssignableFrom(e.Source.GetType()))
588                 {
589                     this.connectorEditor = new ConnectorEditor(this, e.Source as Connector);
590                 }
591             }
592         }
593
594         //Calls the Line routing algorithm and populates the points collection of the connector.
595         void RoutePolyLine(Connector connector)
596         {
597             Point[] pts = ConnectorRouter.Route(this, FreeFormPanel.GetSourceConnectionPoint(connector), FreeFormPanel.GetDestinationConnectionPoint(connector));
598             List<Point> points = new List<Point>(pts);
599             if (pts != null)
600             {
601                 UpdateConnectorPoints(connector, points);
602             }
603         }
604
605
606         //Connector editing is complete, save the final connectorEditor state into the connector.
607         void SaveConnectorEditor(Point pt)
608         {
609             bool isConnectionEndPointMoved = !connectorEditor.Persist(pt);
610
611             if (this.ConnectorMoved != null)
612             {
613                 Connector connector = this.connectorEditor.Connector;
614                 List<Point> points = this.connectorEditor.ConnectorEditorLocation;
615                 ConnectorMoved(connector, new ConnectorMovedEventArgs(points));
616             }
617
618             if (isConnectionEndPointMoved)
619             {
620                 //Persist will return false, when the ConnectionEndPoint has been moved.
621                 RemoveConnectorEditor();
622             }
623             else
624             {
625                 this.InvalidateMeasure();
626             }
627         }
628     }
629 }
630