[reflection] Coop handles icalls in System.Reflection and System.RuntimeTypeHandle...
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / View / ExtensionSurface.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.View
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.ComponentModel;
9     using System.Windows;
10     using System.Windows.Controls;
11     using System.Windows.Media;
12     using System.Globalization;
13     using System.Runtime;
14     using System.Diagnostics.CodeAnalysis;
15
16     //This class is responsible for providing functionality to display additional information in context of
17     //the designer view in a popup-like manner. It is basically the canvas control, which is placed on top of 
18     //the other visual elements. It provides functionality to add and remove extension windows, as well as manipulating
19     //their position and size
20     sealed class ExtensionSurface : Panel
21     {
22
23         public static readonly DependencyProperty DesignerProperty = DependencyProperty.Register(
24             "Designer",
25             typeof(DesignerView),
26             typeof(ExtensionSurface),
27             new PropertyMetadata(OnDesignerChanged));
28
29         public static readonly DependencyProperty AutoExpandCanvasProperty = DependencyProperty.Register(
30             "AutoExpandCanvas",
31             typeof(bool),
32             typeof(ExtensionSurface),
33             new UIPropertyMetadata(false));
34
35         public static readonly DependencyProperty PlacementTargetProperty = DependencyProperty.RegisterAttached(
36             "PlacementTarget",
37             typeof(FrameworkElement),
38             typeof(ExtensionSurface),
39             new UIPropertyMetadata(null, OnPlacementTargetChanged));
40
41         public static readonly DependencyProperty AlignmentProperty = DependencyProperty.RegisterAttached(
42             "Alignment",
43             typeof(PositionAlignment),
44             typeof(ExtensionSurface),
45             new UIPropertyMetadata(PositionAlignment.LeftTop));
46
47         public static readonly DependencyProperty ModeProperty = DependencyProperty.RegisterAttached(
48             "Mode",
49             typeof(PlacementMode),
50             typeof(ExtensionSurface),
51             new UIPropertyMetadata(PlacementMode.Absolute, OnPlacementModeChanged));
52
53         public static readonly DependencyProperty PositionProperty = DependencyProperty.RegisterAttached(
54             "Position",
55             typeof(Point),
56             typeof(ExtensionSurface),
57             new UIPropertyMetadata(new Point()));
58
59
60         Func<double, double, double, bool> IsGreater;
61
62         KeyValuePair<FrameworkElement, Point> selectedChild;
63         Size rearangeStartSize = new Size();
64         Rect actualPanelRect = new Rect(0, 0, 0, 0);
65         Point canvasOffset = new Point();
66         int currentZIndex = 1000;
67
68         public ExtensionSurface()
69         {
70             //add global handled for ExtensionWindow's CloseEvent 
71             this.AddHandler(ExtensionWindow.CloseEvent, new RoutedEventHandler(OnExtensionWindowClosed));
72             this.ClipToBounds = true;
73             this.IsGreater = (v1, v2, v3) => (v1 + v2 > v3);
74         }
75
76         [Fx.Tag.KnownXamlExternal]
77         public DesignerView Designer
78         {
79             get { return (DesignerView)GetValue(DesignerProperty); }
80             set { SetValue(DesignerProperty, value); }
81         }
82
83         public bool AutoExpandCanvas
84         {
85             get { return (bool)GetValue(AutoExpandCanvasProperty); }
86             set { SetValue(AutoExpandCanvasProperty, value); }
87         }
88
89         static void OnPlacementModeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
90         {
91             ExtensionWindow window = sender as ExtensionWindow;
92             if (null != window && null != window.Surface && window.Visibility == Visibility.Visible)
93             {
94                 window.Surface.PlaceWindow(window);
95             }
96         }
97
98         static void OnPlacementTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
99         {
100         }
101
102
103         //hook for designer mouse events - they are required to handle positioning and resizing
104         static void OnDesignerChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
105         {
106             ExtensionSurface ctrl = (ExtensionSurface)sender;
107             DesignerView designer;
108             if (null != args.OldValue)
109             {
110                 designer = (DesignerView)args.OldValue;
111             }
112             if (null != args.NewValue)
113             {
114                 designer = (DesignerView)args.NewValue;
115             }
116         }
117
118         protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
119         {
120             ExtensionWindow window = visualRemoved as ExtensionWindow;
121             if (null != window)
122             {
123                 window.VisibilityChanged -= OnWindowVisibilityChanged;
124                 // window.SizeChanged -= OnWindowSizeChanged;
125                 this.rearangeStartSize.Width = 0;
126                 this.rearangeStartSize.Height = 0;
127             }
128
129             base.OnVisualChildrenChanged(visualAdded, visualRemoved);
130
131             window = visualAdded as ExtensionWindow;
132             if (null != window)
133             {
134                 window.VisibilityChanged += OnWindowVisibilityChanged;
135                 // window.SizeChanged += OnWindowSizeChanged;
136                 if (!window.IsLoaded)
137                 {
138                     window.Loaded += OnChildWindowLoaded;
139                 }
140             }
141         }
142
143         protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
144         {
145             base.OnRenderSizeChanged(sizeInfo);
146             foreach (FrameworkElement child in this.Children)
147             {
148                 ExtensionWindow window = child as ExtensionWindow;
149                 if (null != window)
150                 {
151                     if (PlacementMode.Relative == GetMode(window) && null != GetPlacementTarget(window))
152                     {
153                         this.PlaceWindow(window);
154                         continue;
155                     }
156                     if (!this.AutoExpandCanvas)
157                     {
158                         this.EnsureWindowIsVisible(window);
159                     }
160                 }
161             }
162         }
163
164         void OnChildWindowLoaded(object sender, EventArgs e)
165         {
166             ExtensionWindow window = (ExtensionWindow)sender;
167             this.OnWindowVisibilityChanged(window, null);
168             window.Loaded -= OnChildWindowLoaded;
169         }
170
171         //void OnWindowSizeChanged(object sender, SizeChangedEventArgs e)
172         //{
173         //    ExtensionWindow window = (ExtensionWindow)sender;
174         //   // EnsureWindowIsVisible(window);
175         //}
176
177         void OnWindowVisibilityChanged(object sender, RoutedEventArgs args)
178         {
179             ExtensionWindow window = (ExtensionWindow)sender;
180             if (window.IsVisible)
181             {
182                 Func<double, bool> IsInvalid = x => (double.IsInfinity(x) || double.IsNaN(x) || double.Epsilon > x);
183
184                 if (IsInvalid(window.ActualWidth) || IsInvalid(window.ActualWidth) || IsInvalid(window.DesiredSize.Width) || IsInvalid(window.DesiredSize.Height))
185                 {
186                     window.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
187                 }
188                 PlaceWindow(window);
189             }
190         }
191
192         void PlaceWindow(ExtensionWindow window)
193         {
194             if (null != window)
195             {
196                 FrameworkElement target = ExtensionSurface.GetPlacementTarget(window);
197                 PositionAlignment alignment = ExtensionSurface.GetAlignment(window);
198                 PlacementMode mode = ExtensionSurface.GetMode(window);
199                 Point position = ExtensionSurface.GetPosition(window);
200
201                 Point calculatedPosition = new Point();
202                 FrameworkElement commonRoot = null;
203                 MatrixTransform transform = null;
204
205                 switch (mode)
206                 {
207                     case PlacementMode.Relative:
208                         if (null != target)
209                         {
210                             commonRoot = target.FindCommonVisualAncestor(this) as FrameworkElement;
211                             if (null == commonRoot)
212                             {
213                                 return;
214                             }
215                             transform = (MatrixTransform)target.TransformToAncestor(commonRoot);
216                         }
217                         else
218                         {
219                             if (!DesignerProperties.GetIsInDesignMode(this))
220                             {
221                                 Fx.Assert(string.Format(CultureInfo.InvariantCulture, "PlacementTarget must be set in RelativeMode on ExtensionSurface '{0}'", this.Name));
222                             }
223                         }
224                         break;
225
226                     case PlacementMode.Absolute:
227                         calculatedPosition = position;
228                         break;
229
230                     default:
231                         Fx.Assert(string.Format(CultureInfo.CurrentCulture, "ExtensionWindowPlacement.Mode {0} specified in ExtensionWindow '{1}' is not supported for ExtensionSurface", mode, window.Name));
232                         return;
233                 }
234
235                 if (PlacementMode.Relative == mode)
236                 {
237                     if (null != target)
238                     {
239                         double x;
240                         double y;
241                         switch (alignment)
242                         {
243                             case PositionAlignment.LeftTop:
244                                 calculatedPosition = transform.Transform(calculatedPosition);
245                                 break;
246
247                             case PositionAlignment.LeftBottom:
248                                 calculatedPosition = transform.Transform(new Point(0.0, target.ActualHeight));
249                                 break;
250
251                             case PositionAlignment.RightTop:
252                                 calculatedPosition = transform.Transform(new Point(target.ActualWidth, 0.0));
253                                 break;
254
255                             case PositionAlignment.RightBottom:
256                                 calculatedPosition = transform.Transform(new Point(target.ActualWidth, target.ActualHeight));
257                                 break;
258
259                             case PositionAlignment.Center:
260                                 calculatedPosition = transform.Transform(calculatedPosition);
261                                 x = ((target.ActualWidth * transform.Matrix.M11) - window.Width) / 2.0;
262                                 y = ((target.ActualHeight * transform.Matrix.M22) - window.Height) / 2.0;
263                                 calculatedPosition.Offset(x, y);
264                                 break;
265
266                             case PositionAlignment.CenterHorizontal:
267                                 calculatedPosition = transform.Transform(calculatedPosition);
268                                 x = ((target.ActualWidth * transform.Matrix.M11) - window.Width) / 2.0;
269                                 calculatedPosition.Offset(x, 0.0);
270                                 break;
271
272                             case PositionAlignment.CenterVertical:
273                                 calculatedPosition = transform.Transform(calculatedPosition);
274                                 y = ((target.ActualHeight * transform.Matrix.M22) - window.Height) / 2.0;
275                                 calculatedPosition.Offset(0.0, y);
276                                 break;
277
278                             default:
279                                 Fx.Assert(string.Format(CultureInfo.CurrentCulture, "ExtensionWindowPlacement.Position = '{0}' is not supported", alignment));
280                                 return;
281                         }
282                     }
283                 }
284                 SetWindowPosition(window, calculatedPosition);
285             }
286         }
287
288         internal void SetWindowPosition(ExtensionWindow window, Point position)
289         {
290             Func<double, double, double, double, double> CalculateInBoundsValue =
291                 (pos, size, limit, modifier) =>
292                 {
293                     if (this.AutoExpandCanvas)
294                     {
295                         return pos - modifier;
296                     }
297                     else
298                     {
299                         pos = Math.Max(0.0, pos);
300                         return pos + size > limit ? limit - size : pos;
301                     }
302                 };
303
304             //in case of AutoExpandCanvas == false:
305             // - do not allow placing window outside surface bounds
306             //in case of AutoExpandCanvas == true:
307             // - include possible negative canvas offset
308             position.X = CalculateInBoundsValue(position.X, window.DesiredSize.Width, this.ActualWidth, this.selectedChild.Value.X);
309             position.Y = CalculateInBoundsValue(position.Y, window.DesiredSize.Height, this.ActualHeight, this.selectedChild.Value.Y);
310
311             //update its position on canvas
312             ExtensionSurface.SetPosition(window, position);
313
314             bool requiresMeasure = false;
315             if (this.AutoExpandCanvas)
316             {
317                 requiresMeasure = true;
318                 this.canvasOffset.X = 0;
319                 this.canvasOffset.Y = 0;
320
321                 foreach (UIElement item in this.Children)
322                 {
323                     FrameworkElement child = item as FrameworkElement;
324                     if (null != child)
325                     {
326                         Point p = ExtensionSurface.GetPosition(child);
327                         this.canvasOffset.X = Math.Min(this.canvasOffset.X, p.X);
328                         this.canvasOffset.Y = Math.Min(this.canvasOffset.Y, p.Y);
329                     }
330                 }
331                 this.canvasOffset.X = Math.Abs(this.canvasOffset.X);
332                 this.canvasOffset.Y = Math.Abs(this.canvasOffset.Y);
333             }
334             if (requiresMeasure)
335             {
336                 this.InvalidateMeasure();
337             }
338             else
339             {
340                 this.InvalidateArrange();
341             }
342         }
343
344         void EnsureWindowIsVisible(ExtensionWindow window)
345         {
346             SetWindowPosition(window, ExtensionSurface.GetPosition(window));
347         }
348
349         internal void SetSize(ExtensionWindow window, Size size)
350         {
351             Point pos = ExtensionSurface.GetPosition(window);
352             if (!this.AutoExpandCanvas)
353             {
354                 if (IsGreater(pos.X, size.Width, this.ActualWidth))
355                 {
356                     size.Width = this.ActualWidth - pos.X;
357                 }
358                 if (IsGreater(pos.Y, size.Height, this.ActualHeight))
359                 {
360                     size.Height = this.ActualHeight - pos.Y;
361                 }
362             }
363             System.Diagnostics.Debug.WriteLine("SetSize oldSize (" + window.Width + "," + window.Height + ") newSize (" + size.Width + "," + size.Height + ")");
364             window.Width = size.Width;
365             window.Height = size.Height;
366             if (this.AutoExpandCanvas)
367             {
368                 // this.InvalidateMeasure();
369             }
370         }
371
372         protected override Size ArrangeOverride(Size arrangeSize)
373         {
374             foreach (UIElement child in this.Children)
375             {
376                 //get (left, top) coorinates
377                 Point pos = ExtensionSurface.GetPosition(child);
378                 //include eventual negative offset (panel wouldn't display elements with negative coorinates by default)
379                 pos.Offset(this.canvasOffset.X, this.canvasOffset.Y);
380                 //request child to rearange itself in given rectangle
381                 child.Arrange(new Rect(pos, child.DesiredSize));
382             }
383             System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "ArrangeOverride Size({0},{1})", arrangeSize.Width, arrangeSize.Height));
384             return arrangeSize;
385
386         }
387
388
389         protected override Size MeasureOverride(Size constraint)
390         {
391             Size result;
392
393             if (this.AutoExpandCanvas)
394             {
395                 double panelWidth = 0.0;
396                 double panelHeight = 0.0;
397
398                 //initially assume that whole content fits in rectangle with coordinates (0,0, ActualWidth, ActualHeight)
399                 double offsetMinusX = 0.0;
400                 double offsetMinusY = 0.0;
401                 double offsetPlusX = this.rearangeStartSize.Width;
402                 double offsetPlusY = this.rearangeStartSize.Height;
403
404                 foreach (UIElement item in this.Children)
405                 {
406                     FrameworkElement child = item as FrameworkElement;
407                     if (null != child)
408                     {
409                         child.Measure(constraint);
410
411                         //get child's position
412                         Point pos = ExtensionSurface.GetPosition(child);
413
414                         //calculate the minimum value of panel's (left,top) corner
415                         offsetMinusX = Math.Min(offsetMinusX, pos.X);
416                         offsetMinusY = Math.Min(offsetMinusY, pos.Y);
417
418                         //calculate the maximum value of panel's (right, bottom) corner
419                         offsetPlusX = Math.Max(offsetPlusX, pos.X + child.DesiredSize.Width);
420                         offsetPlusY = Math.Max(offsetPlusY, pos.Y + child.DesiredSize.Height);
421                     }
422                 }
423
424                 //get required panel's width and height
425                 panelWidth = Math.Abs(offsetPlusX - offsetMinusX);
426                 panelHeight = Math.Abs(offsetPlusY - offsetMinusY);
427
428                 this.actualPanelRect.Location = new Point(offsetMinusX, offsetMinusY);
429                 this.actualPanelRect.Size = new Size(panelWidth, panelHeight);
430
431                 //return it as result
432                 result = new Size(panelWidth, panelHeight);
433             }
434             else
435             {
436                 result = base.MeasureOverride(constraint);
437             }
438             System.Diagnostics.Debug.WriteLine("MO constraint:" + constraint.Width + "," + constraint.Height + " new: " + result.Width + "," + result.Height);
439             return result;
440         }
441
442         public void SelectWindow(ExtensionWindow window)
443         {
444             if (null != window && this.Children.Contains(window))
445             {
446                 this.selectedChild = new KeyValuePair<FrameworkElement, Point>(window, this.canvasOffset);
447                 this.rearangeStartSize.Width = this.ActualWidth;
448                 this.rearangeStartSize.Height = this.ActualHeight;
449                 Panel.SetZIndex(window, ++this.currentZIndex);
450             }
451         }
452
453         void OnExtensionWindowClosed(object sender, RoutedEventArgs args)
454         {
455             ExtensionWindow window = args.Source as ExtensionWindow;
456
457             if (null != window)
458             {
459                 //remove window from children collection
460                 this.Children.Remove(window);
461             }
462         }
463
464         public static void SetPlacementTarget(DependencyObject container, FrameworkElement value)
465         {
466             container.SetValue(PlacementTargetProperty, value);
467         }
468
469         public static FrameworkElement GetPlacementTarget(DependencyObject container)
470         {
471             return (FrameworkElement)container.GetValue(PlacementTargetProperty);
472         }
473
474         public static void SetAlignment(DependencyObject container, PositionAlignment value)
475         {
476             container.SetValue(AlignmentProperty, value);
477         }
478
479         public static PositionAlignment GetAlignment(DependencyObject container)
480         {
481             return (PositionAlignment)container.GetValue(AlignmentProperty);
482         }
483
484         public static void SetMode(DependencyObject container, PlacementMode value)
485         {
486             container.SetValue(ModeProperty, value);
487         }
488
489         public static PlacementMode GetMode(DependencyObject container)
490         {
491             return (PlacementMode)container.GetValue(ModeProperty);
492         }
493
494         public static void SetPosition(DependencyObject container, Point value)
495         {
496             container.SetValue(PositionProperty, value);
497         }
498
499         public static Point GetPosition(DependencyObject container)
500         {
501             return (Point)container.GetValue(PositionProperty);
502         }
503         [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Suppress to avoid unnecessary changes.")]
504         public enum PlacementMode
505         {
506             Relative, Absolute
507         }
508         [SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Suppress to avoid unnecessary changes.")]
509         public enum PositionAlignment
510         {
511             LeftTop, LeftBottom, RightTop, RightBottom, Center, CenterHorizontal, CenterVertical
512         };
513     }
514 }