context: /*
   * Greenshot - a free and open source screenshot tool
   * Copyright (C) 2007-2014 Thomas Braun, Jens Klingen, Robin Krom
   *
   * For more information see: http://getgreenshot.org/
   * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 1 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  using Greenshot.Configuration;
  using Greenshot.Drawing.Fields;
  using Greenshot.Drawing.Filters;
  using Greenshot.Helpers;
  using Greenshot.IniFile;
  using Greenshot.Memento;
  using Greenshot.Plugin;
  using Greenshot.Plugin.Drawing;
  using GreenshotPlugin;
  using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Windows.Forms;
  
  namespace Greenshot.Drawing
  {
      /// <summary>
      /// represents a rectangle, ellipse, label or whatever. Can contain filters, too.
      /// serializable for clipboard support
      /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call
      /// OnPropertyChanged whenever a public property has been changed.
      /// </summary>
      [Serializable]
      public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer
      {
          protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
  
          protected static readonly Color DefaultLineColor = Color.FromArgb(0, 150, 255);
  
          private bool isMadeUndoable;
          private const int M11 = 0;
          private const int M12 = 1;
          private const int M21 = 2;
          private const int M22 = 3;
  
          protected EditStatus _defaultEditMode = EditStatus.DRAWING;
          public EditStatus DefaultEditMode
          {
              get
              {
                  return _defaultEditMode;
              }
          }
  
          /// <summary>
          /// The public accessible Dispose
          /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
          /// </summary>
          public void Dispose()
          {
              Dispose(true);
              GC.SuppressFinalize(this);
          }
  
          protected virtual void Dispose(bool disposing)
          {
              if (!disposing)
              {
                  return;
              }
              if (_grippers != null)
              {
                  for (int i = 0; i < _grippers.Length; i++)
                  {
                      if (_grippers[i] == null)
                      {
                          continue;
                      }
                      _grippers[i].Dispose();
                      _grippers[i] = null;
                  }
                  _grippers = null;
              }
  
              FieldAggregator aggProps = _parent.FieldAggregator;
              aggProps.UnbindElement(this);
          }
  
          ~DrawableContainer()
          {
              Dispose(false);
          }
  
          [NonSerialized]
          private PropertyChangedEventHandler _propertyChanged;
          public event PropertyChangedEventHandler PropertyChanged
          {
              add { _propertyChanged += value; }
              remove { _propertyChanged -= value; }
          }
  
          public List<IFilter> Filters
          {
              get
              {
                  List<IFilter> ret = new List<IFilter>();
                  foreach (IFieldHolder c in Children)
                  {
                      if (c is IFilter)
                      {
                          ret.Add(c as IFilter);
                      }
                  }
                  return ret;
              }
          }
  
          [NonSerialized]
          internal Surface _parent;
          public ISurface Parent
          {
              get { return _parent; }
              set { SwitchParent((Surface)value); }
          }
          [NonSerialized]
          protected Gripper[] _grippers;
          private bool layoutSuspended;
  
          [NonSerialized]
          private Gripper _targetGripper;
  
          public Gripper TargetGripper
          {
              get
              {
                  return _targetGripper;
              }
          }
  
          [NonSerialized]
          private bool _selected;
          public bool Selected
          {
              get
              {
                  return _selected;
              }
              set
              {
                  _selected = value;
                  OnPropertyChanged("Selected");
              }
          }
  
          [NonSerialized]
          private EditStatus _status = EditStatus.UNDRAWN;
          public EditStatus Status
          {
              get
              {
                  return _status;
              }
              set
              {
                  _status = value;
              }
          }
  
          private int left;
          public int Left
          {
              get
              {
                  return left;
              }
              set
              {
                  if (value == left)
                  {
                      return;
                  }
                  left = value;
                  DoLayout();
              }
          }
  
          private int top;
          public int Top
          {
              get
              {
                  return top;
              }
              set
              {
                  if (value == top)
                  {
                      return;
                  }
                  top = value;
                  DoLayout();
              }
          }
  
          private int width;
          public int Width
          {
              get
              {
                  return width;
              }
              set
              {
                  if (value == width)
                  {
                      return;
                  }
                  width = value;
                  DoLayout();
              }
          }
  
          private int height;
          public int Height
          {
              get
              {
                  return height;
              }
              set
              {
                  if (value == height)
                  {
                      return;
                  }
                  height = value;
                  DoLayout();
              }
          }
  
          public Point Location
          {
              get
              {
                  return new Point(left, top);
              }
              set
              {
                  left = value.X;
                  top = value.Y;
              }
          }
  
          public Size Size
          {
              get
              {
                  return new Size(width, height);
              }
              set
              {
                  width = value.Width;
                  height = value.Height;
              }
          }
  
          [NonSerialized]
          // will store current bounds of this DrawableContainer before starting a resize
          protected Rectangle _boundsBeforeResize = Rectangle.Empty;
  
          [NonSerialized]
          // "workbench" rectangle - used for calculatoing bounds during resizing (to be applied to this DrawableContainer afterwards)
          protected RectangleF _boundsAfterResize = RectangleF.Empty;
  
          public Rectangle Bounds
          {
              get
              {
                  return GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
              }
              set
              {
                  Left = Round(value.Left);
                  Top = Round(value.Top);
                  Width = Round(value.Width);
                  Height = Round(value.Height);
              }
          }
  
          public virtual void ApplyBounds(RectangleF newBounds)
          {
              Left = Round(newBounds.Left);
              Top = Round(newBounds.Top);
              Width = Round(newBounds.Width);
              Height = Round(newBounds.Height);
          }
  
          public DrawableContainer(Surface parent)
          {
              InitializeFields();
              _parent = parent;
              InitControls();
          }
  
          public void Add(IFilter filter)
          {
              AddChild(filter);
          }
  
          public void Remove(IFilter filter)
          {
              RemoveChild(filter);
          }
  
          private static int Round(float f)
          {
              if (float.IsPositiveInfinity(f) || f > int.MaxValue / 2) return int.MaxValue / 2;
              if (float.IsNegativeInfinity(f) || f < int.MinValue / 2) return int.MinValue / 2;
              return (int)Math.Round(f);
          }
  
          private bool accountForShadowChange;
          public virtual Rectangle DrawingBounds
          {
              get
              {
                  foreach (IFilter filter in Filters)
                  {
                      if (filter.Invert)
                      {
                          return new Rectangle(Point.Empty, _parent.Image.Size);
                      }
                  }
                  // Take a base safetymargin
                  int lineThickness = 5;
                  if (HasField(FieldType.LINE_THICKNESS))
                  {
                      lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS);
                  }
                  int offset = lineThickness / 2;
  
                  int shadow = 0;
                  if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW)))
                  {
                      accountForShadowChange = false;
                      shadow += 10;
                  }
                  return new Rectangle(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow);
              }
          }
  
          public virtual void Invalidate()
          {
              if (Status != EditStatus.UNDRAWN)
              {
                  _parent.Invalidate(DrawingBounds);
              }
          }
  
          public void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
          {
              int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
              if (horizontalAlignment == HorizontalAlignment.Left)
              {
  
gt:                 Left = lineThickness / 2;