aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.Common/Modules
ModeNameSize
-rw-r--r--IPPCModuleLoader.cs1167logstatsplain
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using Tango.Core;
using Tango.Editors;

namespace Tango.Editors
{
    /// <summary>
    /// Represents a base class for all element editors.
    /// </summary>
    /// <seealso cref="Tango.Editors.HybridControl" />
    /// <seealso cref="Tango.Editors.IElementEditor" />
    public abstract class ElementEditor : HybridControl, IElementEditor
    {
        private Point _startPoint;
        private Point _centerPoint;
        private Vector _startVector;
        private double _initialAngle;
        private double _anglePI;
        private bool _preventMovingEvent;

        #region Events

        /// <summary>
        /// Occurs when the editor is moving.
        /// </summary>
        public event EventHandler<DragDeltaEventArgs> Moving;

        /// <summary>
        /// Occurs before editor bounds change.
        /// </summary>
        public event EventHandler BeforeBoundsChange;

        /// <summary>
        /// Occurs after editor bounds change.
        /// </summary>
        public event EventHandler AfterBoundsChange;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the editor unique identifier.
        /// </summary>
        [ParameterIgnore]
        public String ID
        {
            get { return (String)GetValue(IDProperty); }
            set { SetValue(IDProperty, value); }
        }
        public static readonly DependencyProperty IDProperty =
            DependencyProperty.Register("ID", typeof(String), typeof(ElementEditor), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value indicating whether the hosted element will be presented inside or outside the editor.
        /// </summary>
        [ParameterIgnore]
        public bool ElementDetached
        {
            get { return (bool)GetValue(ElementDetachedProperty); }
            set { SetValue(ElementDetachedProperty, value); }
        }
        public static readonly DependencyProperty ElementDetachedProperty =
            DependencyProperty.Register("ElementDetached", typeof(bool), typeof(ElementEditor), new PropertyMetadata(false, (d, e) => (d as ElementEditor).OnElementDetachedChanged()));

        /// <summary>
        /// Gets or sets the editor top position.
        /// </summary>
        [ParameterItem(null, null)]
        public double Top
        {
            get { return (double)GetValue(TopProperty); }
            set { SetValue(TopProperty, value); }
        }
        public static readonly DependencyProperty TopProperty =
            DependencyProperty.Register("Top", typeof(double), typeof(ElementEditor), new PropertyMetadata(0.0, (d, e) => { (d as ElementEditor).OnPositionChanged(); }, (d, e) => { return (d as ElementEditor).OnCoerceTop(e); }));

        /// <summary>
        /// Gets or sets the editor left position.
        /// </summary>
        [ParameterItem(null, null)]
        public double Left
        {
            get { return (double)GetValue(LeftProperty); }
            set { SetValue(LeftProperty, value); }
        }
        public static readonly DependencyProperty LeftProperty =
            DependencyProperty.Register("Left", typeof(double), typeof(ElementEditor), new PropertyMetadata(0.0, (d, e) => { (d as ElementEditor).OnPositionChanged(); }, (d, e) => { return (d as ElementEditor).OnCoerceLeft(e); }));

        /// <summary>
        /// Gets or sets the editor angle.
        /// </summary>
        [ParameterItem(0.0, 360.0)]
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }
        public static readonly DependencyProperty AngleProperty =
            DependencyProperty.Register("Angle", typeof(double), typeof(ElementEditor), new PropertyMetadata(0.0));

        /// <summary>
        /// Gets or sets the corners brush.
        /// </summary>
        [ParameterIgnore]
        public Brush CornersBrush
        {
            get { return (Brush)GetValue(CornersBrushProperty); }
            set { SetValue(CornersBrushProperty, value); }
        }
        public static readonly DependencyProperty CornersBrushProperty =
            DependencyProperty.Register("CornersBrush", typeof(Brush), typeof(ElementEditor), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets the editor inner content.
        /// </summary>
        [ParameterIgnore]
        public FrameworkElement InnerContent
        {
            get { return (FrameworkElement)GetValue(InnerContentProperty); }
            set { SetValue(InnerContentProperty, value); }
        }
        public static readonly DependencyProperty InnerContentProperty =
            DependencyProperty.Register("InnerContent", typeof(FrameworkElement), typeof(FrameworkElementEditor), new PropertyMetadata(null));

        /// <summary>
        /// Gets an bind-able observable collection of the component properties.
        /// </summary>
        [ParameterIgnore]
        public ReadOnlyObservableCollection<ParameterItem> Parameters
        {
            get { return (ReadOnlyObservableCollection<ParameterItem>)GetValue(ParametersProperty); }
            set { SetValue(ParametersProperty, value); }
        }
        public static readonly DependencyProperty ParametersProperty =
            DependencyProperty.Register("Parameters", typeof(ReadOnlyObservableCollection<ParameterItem>), typeof(ElementEditor), new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets an optional attached element for editors mirroring mode.
        /// </summary>
        [ParameterIgnore]
        public IElementEditor AttachedEditor
        {
            get { return (IElementEditor)GetValue(AttachedEditorProperty); }
            set { SetValue(AttachedEditorProperty, value); }
        }
        public static readonly DependencyProperty AttachedEditorProperty =
            DependencyProperty.Register("AttachedEditor", typeof(IElementEditor), typeof(ElementEditor), new PropertyMetadata(null,(d,e) => (d as ElementEditor).OnAttachedEditorChanged()));

        /// <summary>
        /// Gets the hosted element.
        /// </summary>
        [ParameterIgnore]
        public abstract Object HostedElement { get; }

        protected int _zIndex;
        /// <summary>
        /// Gets or sets the editor Z-index.
        /// </summary>
        public virtual int ZIndex
        {
            get
            {
                return Canvas.GetZIndex(this as UIElement);
            }
            set
            {
                _zIndex = value;
                Canvas.SetZIndex(this as UIElement, value);
                RaisePropertyChanged(nameof(ZIndex));
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ElementEditor"/> class.
        /// </summary>
        public ElementEditor()
        {
            ID = Guid.NewGuid().ToString();
            Parameters = new ReadOnlyObservableCollection<ParameterItem>(GetEditorParameters());
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Gets the collection of <see cref="T:Tango.Editors.AnimationSetup" /> for the current editor and element properties.
        /// </summary>
        /// <param name="duration">The animation duration.</param>
        /// <param name="animationSetupMode">The animation setup mode.</param>
        /// <returns></returns>
        /// <remarks>
        /// The returned collection of animation setups can be inserted into a <see cref="T:WpfVideoTools.Timeline.TimelineControl" />.
        /// </remarks>
        public virtual List<AnimationSetup> GetAnimationSetups(TimeSpan duration, AnimationSetupMode animationSetupMode = AnimationSetupMode.Linear)
        {
            List<AnimationSetup> setups = new List<AnimationSetup>();

            setups.Add(GetPropertyAnimationSetup(TopProperty, duration, animationSetupMode));
            setups.Add(GetPropertyAnimationSetup(LeftProperty, duration, animationSetupMode));
            setups.Add(GetPropertyAnimationSetup(WidthProperty, duration, animationSetupMode));
            setups.Add(GetPropertyAnimationSetup(HeightProperty, duration, animationSetupMode));
            setups.Add(GetPropertyAnimationSetup(AngleProperty, duration, animationSetupMode));

            return setups;
        }

        /// <summary>
        /// Sets the editor bounds.
        /// </summary>
        /// <param name="bounds">The bounds.</param>
        public virtual void SetBounds(Rect bounds)
        {
            Top = bounds.Top;
            Left = bounds.Left;
            Width = bounds.Width;
            Height = bounds.Height;
        }

        /// <summary>
        /// Gets the editor bounds.
        /// </summary>
        /// <returns></returns>
        public virtual Rect GetBounds()
        {
            return new Rect(Left, Top, Width, Height);
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Determines whether the control key is down.
        /// </summary>
        protected bool IsCtrlDown()
        {
            return Keyboard.IsKeyDown(Key.LeftCtrl);
        }

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Invoked when the attached editor has changed.
        /// </summary>
        protected virtual void OnAttachedEditorChanged()
        {

        }

        /// <summary>
        /// Gets the editor parameters.
        /// </summary>
        /// <returns></returns>
        protected virtual ObservableCollection<ParameterItem> GetEditorParameters()
        {
            var parameters = this.CreateParametersCollection(ParameterItemMode.Binding);
            parameters.Add(this.CreateParameterItem("Width", new ParameterItemAttribute(null, null), parameters.Max(x => x.Index + 1), ParameterItemMode.Binding));
            parameters.Add(this.CreateParameterItem("Height", new ParameterItemAttribute(null, null), parameters.Max(x => x.Index + 1), ParameterItemMode.Binding));
            var angle = parameters.Single(x => x.Name == "Angle");
            parameters.Remove(angle);
            parameters.Add(angle);
            return parameters;
        }

        /// <summary>
        /// Gets the property animation setup.
        /// </summary>
        /// <param name="dependencyProperty">The dependency property.</param>
        /// <param name="duration">The duration.</param>
        /// <param name="animationSetupMode">The animation setup mode.</param>
        /// <returns></returns>
        protected virtual AnimationSetup GetPropertyAnimationSetup(DependencyProperty dependencyProperty, TimeSpan duration, AnimationSetupMode animationSetupMode)
        {
            return GetPropertyAnimationSetup(this, dependencyProperty, duration, animationSetupMode);
        }

        /// <summary>
        /// Gets the property animation setup.
        /// </summary>
        /// <param name="dependencyObject">The dependency object.</param>
        /// <param name="dependencyProperty">The dependency property.</param>
        /// <param name="duration">The duration.</param>
        /// <param name="animationSetupMode">The animation setup mode.</param>
        /// <returns></returns>
        protected virtual AnimationSetup GetPropertyAnimationSetup(DependencyObject dependencyObject, DependencyProperty dependencyProperty, TimeSpan duration, AnimationSetupMode animationSetupMode)
        {
            if (animationSetupMode == AnimationSetupMode.Linear && (dependencyProperty.PropertyType == typeof(double) || dependencyProperty.PropertyType == typeof(Point)))
            {
                if (dependencyProperty.PropertyType == typeof(double))
                {
                    DoubleAnimation ani = new DoubleAnimation((double)dependencyObject.GetValue(dependencyProperty), duration);
                    return new AnimationSetup(ani, dependencyObject, dependencyProperty);
                }
                else
                {
                    PointAnimation ani = new PointAnimation((Point)dependencyObject.GetValue(dependencyProperty), duration);
                    return new AnimationSetup(ani, dependencyObject, dependencyProperty);
                }
            }
            else
            {
                ObjectAnimationUsingKeyFrames ani = new ObjectAnimationUsingKeyFrames();
                ani.KeyFrames.Add(new DiscreteObjectKeyFrame(dependencyObject.GetValue(dependencyProperty), TimeSpan.FromSeconds(0)));
                return new AnimationSetup(ani, dependencyObject, dependencyProperty);
            }
        }

        /// <summary>
        /// Called when the position has changed.
        /// </summary>
        protected virtual void OnPositionChanged()
        {
            Canvas.SetTop(this, Top);
            Canvas.SetLeft(this, Left);
        }

        /// <summary>
        /// Drags the editor.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void MoveDrag(object sender, DragDeltaEventArgs e)
        {
            BeginAnimation(LeftProperty, null);
            BeginAnimation(TopProperty, null);

            Point dragDelta = this.RenderTransform.Transform(new Point(e.HorizontalChange, e.VerticalChange));

            Left += dragDelta.X;
            Top += dragDelta.Y;

            if (!_preventMovingEvent)
            {
                if (Moving != null) Moving(this, e);
            }
        }

        /// <summary>
        /// Called when editor dragging has started.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragStartedEventArgs"/> instance containing the event data.</param>
        protected virtual void DragStarted(object sender, DragStartedEventArgs e)
        {
            _initialAngle = Angle;
            _centerPoint = this.TranslatePoint(new Point(Width * RenderTransformOrigin.X, Height * RenderTransformOrigin.Y), this.Parent as FrameworkElement);
            _startPoint = Mouse.GetPosition(this.Parent as FrameworkElement);
            _startVector = Point.Subtract(_startPoint, _centerPoint);

            _anglePI = Angle * Math.PI / 180.0;

            OnBeforeBoundsChange();
        }

        /// <summary>
        /// Called when the editor dragging has completed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragCompletedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnDragEnded(object sender, DragCompletedEventArgs e)
        {
            OnAfterBoundsChange();
        }

        /// <summary>
        /// Drags the editor left side. 
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragLeft(object sender, DragDeltaEventArgs e)
        {
            BeginAnimation(LeftProperty, null);
            BeginAnimation(WidthProperty, null);
            BeginAnimation(TopProperty, null);

            var deltaHorizontal = Math.Min(e.HorizontalChange, ActualWidth - MinWidth);
            if (Width - deltaHorizontal > 0)
            {
                Top = Top + deltaHorizontal * Math.Sin(_anglePI) - RenderTransformOrigin.X * deltaHorizontal * Math.Sin(_anglePI);
                Left = Left + deltaHorizontal * Math.Cos(_anglePI) + (RenderTransformOrigin.X * deltaHorizontal * (1 - Math.Cos(_anglePI)));
                Width -= deltaHorizontal;
            }
        }

        /// <summary>
        /// Drags the editor right side.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragRight(object sender, DragDeltaEventArgs e)
        {
            BeginAnimation(LeftProperty, null);
            BeginAnimation(WidthProperty, null);
            BeginAnimation(TopProperty, null);

            var deltaHorizontal = Math.Min(-e.HorizontalChange, ActualWidth - MinWidth);

            if (Width - deltaHorizontal > 0)
            {
                Top = Top - this.RenderTransformOrigin.X * deltaHorizontal * Math.Sin(_anglePI);
                Left = Left + (deltaHorizontal * this.RenderTransformOrigin.X * (1 - Math.Cos(_anglePI)));
                Width -= deltaHorizontal;
            }
        }

        /// <summary>
        /// Drags the editor top.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragTop(object sender, DragDeltaEventArgs e)
        {
            BeginAnimation(TopProperty, null);
            BeginAnimation(LeftProperty, null);
            BeginAnimation(HeightProperty, null);

            var deltaVertical = Math.Min(e.VerticalChange, ActualHeight - MinHeight);

            if (Height - deltaVertical > 0)
            {
                Top = Top + deltaVertical * Math.Cos(-_anglePI) + (RenderTransformOrigin.Y * deltaVertical * (1 - Math.Cos(-_anglePI)));
                Left = Left + deltaVertical * Math.Sin(-_anglePI) - (RenderTransformOrigin.Y * deltaVertical * Math.Sin(-_anglePI));
                Height -= deltaVertical;
            }
        }

        /// <summary>
        /// Drags the editor bottom.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragBottom(object sender, DragDeltaEventArgs e)
        {
            BeginAnimation(TopProperty, null);
            BeginAnimation(LeftProperty, null);
            BeginAnimation(HeightProperty, null);

            var deltaVertical = Math.Min(-e.VerticalChange, ActualHeight - MinHeight);

            if (Height - deltaVertical > 0)
            {
                Top = Top + (RenderTransformOrigin.Y * deltaVertical * (1 - Math.Cos(-_anglePI)));
                Left = Left - deltaVertical * RenderTransformOrigin.Y * Math.Sin(-_anglePI);
                Height -= deltaVertical;
            }
        }

        /// <summary>
        /// Drags the top left corner.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragTopLeft(object sender, DragDeltaEventArgs e)
        {
            //if (!IsCtrlDown())
            //{
                DragLeft(sender, e);
                DragTop(sender, e);
            //}
            //else
            //{
            //    DragAngle(sender, e);
            //}
        }

        /// <summary>
        /// Drags the top right corner.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragTopRight(object sender, DragDeltaEventArgs e)
        {
            //if (!IsCtrlDown())
            //{
                DragRight(sender, e);
                DragTop(sender, e);
            //}
            //else
            //{
            //    DragAngle(sender, e);
            //}
        }

        /// <summary>
        /// Drags the bottom right corner.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragBottomRight(object sender, DragDeltaEventArgs e)
        {
            //if (!IsCtrlDown())
            //{
                DragRight(sender, e);
                DragBottom(sender, e);
            //}
            //else
            //{
            //    DragAngle(sender, e);
            //}
        }

        /// <summary>
        /// Drags the bottom left corner.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragBottomLeft(object sender, DragDeltaEventArgs e)
        {
            //if (!IsCtrlDown())
            //{
                DragLeft(sender, e);
                DragBottom(sender, e);
            //}
            //else
            //{
            //    DragAngle(sender, e);
            //}
        }

        /// <summary>
        /// Drags the editor angle.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="DragDeltaEventArgs"/> instance containing the event data.</param>
        protected virtual void DragAngle(object sender, DragDeltaEventArgs e)
        {
            Point currentPoint = Mouse.GetPosition(this.Parent as FrameworkElement);
            Vector deltaVector = Point.Subtract(currentPoint, _centerPoint);
            double angle = Vector.AngleBetween(_startVector, deltaVector);
            BeginAnimation(AngleProperty, null);
            Angle = _initialAngle + angle;
        }

        /// <summary>
        /// Raises the <see cref="BeforeBoundsChange"/> event.
        /// </summary>
        protected virtual void OnBeforeBoundsChange()
        {
            if (BeforeBoundsChange != null) BeforeBoundsChange(this, new EventArgs());
        }

        /// <summary>
        /// Raises the <see cref="AfterBoundsChange"/> event.
        /// </summary>
        protected virtual void OnAfterBoundsChange()
        {
            if (AfterBoundsChange != null) AfterBoundsChange(this, new EventArgs());
        }

        /// <summary>
        /// Called before the top property changes.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual object OnCoerceTop(object value)
        {
            return value;
        }

        /// <summary>
        /// Called before the left property changes.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual object OnCoerceLeft(object value)
        {
            return value;
        }

        /// <summary>
        /// Called when the editor detached property has changed.
        /// </summary>
        protected virtual void OnElementDetachedChanged()
        {
            //Do Nothing ?
        }

        #endregion

        #region Override Methods

        /// <summary>
        /// Invoked when an unhandled <see cref="E:System.Windows.Input.Mouse.MouseDown" /> attached event reaches an element in its route that is derived from this class. Implement this method to add class handling for this event.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Windows.Input.MouseButtonEventArgs" /> that contains the event data. This event data reports details about the mouse button that was pressed and the handled state.</param>
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            if (!Keyboard.IsKeyDown(Key.LeftShift))
            {
                e.Handled = true;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Moves the editor by the specified delta arguments.
        /// </summary>
        /// <param name="e">The <see cref="DragDeltaEventArgs" /> instance containing the event data.</param>
        public void PushMove(DragDeltaEventArgs e)
        {
            _preventMovingEvent = true;
            MoveDrag(this, e);
            _preventMovingEvent = false;
        }

        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public abstract IElementEditor Clone();


        #endregion

        #region IConfigurable Members

        /// <summary>
        /// Gets a configuration object representing the configurable properties. This configuration can be serialized to a stream or file, and later be loaded and applied to the configurable object.
        /// </summary>
        /// <returns></returns>
        public virtual IConfiguration GetConfiguration()
        {
            ElementEditorConfiguration config = new ElementEditorConfiguration();
            config.ConfigurableType = this.GetType();
            config.ID = ID;
            config.ElementDetached = ElementDetached;
            config.BackgroundBrushString = Background != null ? Background.ConvertToString() : null;
            config.Left = Left;
            config.Top = Top;
            config.Width = Width;
            config.Height = Height;
            config.Angle = Angle;
            return config;
        }

        /// <summary>
        /// Applies the specified configuration with an optional animation if supported by the configurable type.
        /// </summary>
        /// <param name="configuration">The configuration.</param>
        /// <param name="animation">The animation.</param>
        public virtual void SetConfiguration(IConfiguration configuration, ConfigurationAnimation animation)
        {
            if (!(configuration is ElementEditorConfiguration))
            {
                throw new InvalidConfigurationException();
            }

            var config = configuration as ElementEditorConfiguration;

            ID = config.ID;
            ElementDetached = config.ElementDetached;

            try
            {
                if (config.BackgroundBrushString != null)
                {
                    Background = config.BackgroundBrushString.ToBrush();
                }
            }
            catch
            { }

            this.StartDoubleAnimation(LeftProperty, animation, config.Left);
            this.StartDoubleAnimation(TopProperty, animation, config.Top);
            this.StartDoubleAnimation(WidthProperty, animation, config.Width);
            this.StartDoubleAnimation(HeightProperty, animation, config.Height);
            this.StartDoubleAnimation(AngleProperty, animation, config.Angle);
        }

        #endregion
    }
}