/src/DocumentWorkspace.cs
C# | 2824 lines | 2228 code | 451 blank | 145 comment | 276 complexity | 9c25685eadb589abf2a89f59d80d7308 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- /////////////////////////////////////////////////////////////////////////////////
- // Paint.NET //
- // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
- // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
- // See src/Resources/Files/License.txt for full licensing and attribution //
- // details. //
- // . //
- /////////////////////////////////////////////////////////////////////////////////
-
- using PaintDotNet.Base;
- using PaintDotNet.HistoryFunctions;
- using PaintDotNet.HistoryMementos;
- using PaintDotNet.SystemLayer;
- using PaintDotNet.Tools;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.Globalization;
- using System.IO;
- using System.Reflection;
- using System.Runtime.Serialization;
- using System.Security;
- using System.Threading;
- using System.Windows.Forms;
-
- namespace PaintDotNet
- {
- /// <summary>
- /// Builds on DocumentView by adding application-specific elements.
- /// </summary>
- internal class DocumentWorkspace
- : DocumentView,
- IHistoryWorkspace,
- IThumbnailProvider
- {
- public static readonly DateTime NeverSavedDateTime = DateTime.MinValue;
-
- private static Type[] _tools; // TODO: move to Tool class?
- private static ToolInfo[] _toolInfos;
-
- private ZoomBasis _zoomBasis;
- private AppWorkspace _appWorkspace;
- private SaveConfigToken _saveConfigToken;
- private readonly Selection _selection = new Selection();
- private Surface _scratchSurface;
- private readonly SelectionRenderer _selectionRenderer;
- private readonly Hashtable _staticToolData = Hashtable.Synchronized(new Hashtable());
- private Type _preNullTool;
- private int _nullToolCount;
- private int _zoomChangesCount;
- private readonly HistoryStack _history;
- private Layer _activeLayer;
- private System.Windows.Forms.Timer _toolPulseTimer;
- private DateTime _lastSaveTime = NeverSavedDateTime;
- private int _suspendToolCursorChanges;
-
- private readonly string _contextStatusBarWithAngleFormat = PdnResources.GetString("StatusBar.Context.SelectedArea.Text.WithAngle.Format");
- private readonly string _contextStatusBarFormat = PdnResources.GetString("StatusBar.Context.SelectedArea.Text.Format");
-
- public void SuspendToolCursorChanges()
- {
- ++_suspendToolCursorChanges;
- }
-
- public void ResumeToolCursorChanges()
- {
- --_suspendToolCursorChanges;
-
- if (_suspendToolCursorChanges <= 0 && Tool != null)
- {
- Cursor = Tool.Cursor;
- }
- }
-
- public ImageResource StatusIcon { get; private set; }
-
- public string StatusText { get; private set; }
-
- public void SetStatus(string newStatusText, ImageResource newStatusIcon)
- {
- StatusText = newStatusText;
- StatusIcon = newStatusIcon;
- OnStatusChanged();
- }
-
- public event EventHandler StatusChanged;
- protected virtual void OnStatusChanged()
- {
- if (StatusChanged != null)
- {
- StatusChanged(this, EventArgs.Empty);
- }
- }
-
- static DocumentWorkspace()
- {
- InitializeTools();
- InitializeToolInfos();
- }
-
- public DateTime LastSaveTime
- {
- get
- {
- return _lastSaveTime;
- }
- }
-
- public bool IsZoomChanging
- {
- get
- {
- return (_zoomChangesCount > 0);
- }
- }
-
-
- private void BeginZoomChanges()
- {
- ++_zoomChangesCount;
- }
-
-
-
- private void EndZoomChanges()
- {
- --_zoomChangesCount;
- }
-
-
- protected override void OnSizeChanged(EventArgs e)
- {
- PerformLayout();
- base.OnSizeChanged(e);
- }
-
- protected override void OnLayout(LayoutEventArgs e)
- {
- if (_zoomBasis == ZoomBasis.FitToWindow)
- {
- ZoomToWindow();
-
- // This bizarre ordering of setting PanelAutoScroll prevents some very weird layout/scroll-without-scrollbars stuff.
- PanelAutoScroll = true;
- PanelAutoScroll = false;
- }
-
- base.OnLayout(e);
- }
-
- protected override void OnResize(EventArgs e)
- {
- if (_zoomBasis == ZoomBasis.FitToWindow)
- {
- PerformLayout();
- }
-
- base.OnResize(e);
- }
-
- public DocumentWorkspace()
- {
- FileType = null;
- FilePath = null;
- _activeLayer = null;
- _history = new HistoryStack(this);
-
- InitializeComponent();
-
- // hook the DocumentWorkspace with its selectedPath ...
- _selectionRenderer = new SelectionRenderer(RendererList, Selection, this);
- RendererList.Add(_selectionRenderer, true);
- _selectionRenderer.EnableOutlineAnimation = true;
- _selectionRenderer.EnableSelectionTinting = false;
- _selectionRenderer.EnableSelectionOutline = true;
-
- _selection.Changed += SelectionChanged;
-
- _zoomBasis = ZoomBasis.FitToWindow;
- }
-
- protected override void OnUnitsChanged()
- {
- if (!Selection.IsEmpty)
- {
- UpdateSelectionInfoInStatusBar();
- }
-
- base.OnUnitsChanged();
- }
-
- public void UpdateStatusBarToToolHelpText(Tool tool)
- {
- if (tool == null)
- {
- SetStatus(string.Empty, null);
- }
- else
- {
- string toolName = tool.Name;
- string helpText = tool.HelpText;
-
- string contextFormat = PdnResources.GetString("StatusBar.Context.Help.Text.Format");
- string contextText = string.Format(contextFormat, toolName, helpText);
-
- SetStatus(contextText, PdnResources.GetImageResource("Icons.MenuHelpHelpTopicsIcon.png"));
- }
- }
-
- public void UpdateStatusBarToToolHelpText()
- {
- UpdateStatusBarToToolHelpText(Tool);
- }
-
- private void UpdateSelectionInfoInStatusBar()
- {
- if (Selection.IsEmpty)
- {
- UpdateStatusBarToToolHelpText();
- }
- else
- {
- string newStatusText;
-
- int area;
- Rectangle bounds;
-
- using (PdnRegion tempSelection = Selection.CreateRegionRaw())
- {
- tempSelection.Intersect(Document.Bounds);
- bounds = Utility.GetRegionBounds(tempSelection);
- area = tempSelection.GetArea();
- }
-
- string unitsAbbreviationXY;
- string xString;
- string yString;
- string unitsAbbreviationWH;
- string widthString;
- string heightString;
-
- Document.CoordinatesToStrings(Units, bounds.X, bounds.Y, out xString, out yString, out unitsAbbreviationXY);
- Document.CoordinatesToStrings(Units, bounds.Width, bounds.Height, out widthString, out heightString, out unitsAbbreviationWH);
-
- var nfi = (NumberFormatInfo)CultureInfo.CurrentCulture.NumberFormat.Clone();
-
- string areaString;
- if (Units == MeasurementUnit.Pixel)
- {
- nfi.NumberDecimalDigits = 0;
- areaString = area.ToString("N", nfi);
- }
- else
- {
- nfi.NumberDecimalDigits = 2;
- double areaD = Document.PixelAreaToPhysicalArea(area, Units);
- areaString = areaD.ToString("N", nfi);
- }
-
- string pluralUnits = PdnResources.GetString("MeasurementUnit." + Units + ".Plural");
- var moveTool = Tool as MoveToolBase;
-
- if (moveTool != null && moveTool.HostShouldShowAngle)
- {
- var nfi2 = (NumberFormatInfo)nfi.Clone();
- nfi2.NumberDecimalDigits = 2;
- float angle = moveTool.HostAngle;
-
- while (angle > 180.0f)
- {
- angle -= 360.0f;
- }
-
- while (angle < -180.0f)
- {
- angle += 360.0f;
- }
-
- newStatusText = string.Format(
- _contextStatusBarWithAngleFormat,
- xString,
- unitsAbbreviationXY,
- yString,
- unitsAbbreviationXY,
- widthString,
- unitsAbbreviationWH,
- heightString,
- unitsAbbreviationWH,
- areaString,
- pluralUnits.ToLower(),
- moveTool.HostAngle.ToString("N", nfi2));
- }
- else
- {
- newStatusText = string.Format(
- _contextStatusBarFormat,
- xString,
- unitsAbbreviationXY,
- yString,
- unitsAbbreviationXY,
- widthString,
- unitsAbbreviationWH,
- heightString,
- unitsAbbreviationWH,
- areaString,
- pluralUnits.ToLower());
- }
-
- SetStatus(newStatusText, PdnResources.GetImageResource("Icons.SelectionIcon.png"));
- }
- }
-
- private void SelectionChanged(object sender, EventArgs e)
- {
- UpdateRulerSelectionTinting();
- UpdateSelectionInfoInStatusBar();
- }
-
- private void InitializeComponent()
- {
- _toolPulseTimer = new System.Windows.Forms.Timer {Interval = 16};
- _toolPulseTimer.Tick += ToolPulseTimerTick;
- }
-
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (Tool != null)
- {
- Tool.Dispose();
- Tool = null;
- }
- }
-
- base.Dispose(disposing);
- }
-
- public void PerformActionAsync(DocumentWorkspaceAction action)
- {
- BeginInvoke(new Procedure<DocumentWorkspaceAction>(PerformAction), new object[] { action });
- }
-
- public void PerformAction(DocumentWorkspaceAction action)
- {
- bool nullTool = false;
-
- if ((action.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
- {
- PushNullTool();
- Update();
- nullTool = true;
- }
-
- try
- {
- using (new WaitCursorChanger(this))
- {
- HistoryMemento ha = action.PerformAction(this);
-
- if (ha != null)
- {
- History.PushNewMemento(ha);
- }
- }
- }
-
- finally
- {
- if (nullTool)
- {
- PopNullTool();
- }
- }
- }
-
- /// <summary>
- /// Executes a HistoryFunction in the context of this DocumentWorkspace.
- /// </summary>
- /// <param name="function">The HistoryFunction to execute.</param>
- /// <remarks>
- /// Depending on the HistoryFunction, the currently active tool may be refreshed.
- /// </remarks>
- public HistoryFunctionResult ExecuteFunction(HistoryFunction function)
- {
- HistoryFunctionResult result;
-
- bool nullTool = false;
-
- if ((function.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
- {
- PushNullTool();
- Update();
- nullTool = true;
- }
-
- try
- {
- using (new WaitCursorChanger(this))
- {
- HistoryMemento hm = null;
- string errorText;
-
- try
- {
- bool cancelled = false;
-
- if ((function.ActionFlags & ActionFlags.ReportsProgress) != ActionFlags.ReportsProgress)
- {
- hm = function.Execute(this);
- }
- else
- {
- var pd = new ProgressDialog();
- bool pdLoaded = false;
- bool closeAtLoad = false;
-
- EventHandler loadCallback =
- delegate
- {
- pdLoaded = true;
-
- if (closeAtLoad)
- {
- pd.Close();
- }
- };
-
- ProgressEventHandler progressCallback =
- delegate(object sender, ProgressEventArgs e)
- {
- if (!pdLoaded)
- {
- }
- else
- {
- double newValue = Utility.Clamp(e.Percent, 0.0, 100.0);
- pd.Value = newValue;
- }
- };
-
- EventHandler<EventArgs<HistoryMemento>> finishedCallback =
- delegate(object sender, EventArgs<HistoryMemento> e)
- {
- hm = e.Data;
-
- if (pdLoaded)
- {
- // TODO: fix ProgressDialog's very weird interface
- pd.ExternalFinish();
- pd.Close();
- }
- else
- {
- closeAtLoad = true;
- }
- };
-
- EventHandler cancelClickCallback =
- delegate
- {
- cancelled = true;
- function.RequestCancel();
- //pd.Cancellable = false;
- };
-
- pd.Text = PdnInfo.GetBareProductName();
- pd.Description = PdnResources.GetString("ExecuteFunction.ProgressDialog.Description.Text");
- pd.Load += loadCallback;
- pd.Cancellable = false; //function.Cancellable;
- pd.CancelClick += cancelClickCallback;
- function.Progress += progressCallback;
- function.BeginExecute(this, this, finishedCallback);
- pd.ShowDialog(this);
- pd.Dispose();
- }
-
- if (hm == null && !cancelled)
- {
- result = HistoryFunctionResult.SuccessNoOp;
- }
- else if (hm == null && cancelled)
- {
- result = HistoryFunctionResult.Cancelled;
- }
- else
- {
- result = HistoryFunctionResult.Success;
- }
-
- errorText = null;
- }
-
- catch (HistoryFunctionNonFatalException hfnfex)
- {
- if (hfnfex.InnerException is OutOfMemoryException)
- {
- result = HistoryFunctionResult.OutOfMemory;
- }
- else
- {
- result = HistoryFunctionResult.NonFatalError;
- }
-
- if (hfnfex.LocalizedErrorText != null)
- {
- errorText = hfnfex.LocalizedErrorText;
- }
- else
- {
- if (hfnfex.InnerException is OutOfMemoryException)
- {
- errorText = PdnResources.GetString("ExecuteFunction.GenericOutOfMemory");
- }
- else
- {
- errorText = PdnResources.GetString("ExecuteFunction.GenericError");
- }
- }
- }
-
- if (errorText != null)
- {
- Utility.ErrorBox(this, errorText);
- }
-
- if (hm != null)
- {
- History.PushNewMemento(hm);
- }
- }
- }
-
- finally
- {
- if (nullTool)
- {
- PopNullTool();
- }
- }
-
- return result;
- }
-
- public override void ZoomIn()
- {
- ZoomBasis = ZoomBasis.ScaleFactor;
- base.ZoomIn();
- }
-
- public override void ZoomIn(double factor)
- {
- ZoomBasis = ZoomBasis.ScaleFactor;
- base.ZoomIn(factor);
- }
-
- public override void ZoomOut()
- {
- ZoomBasis = ZoomBasis.ScaleFactor;
- base.ZoomOut();
- }
-
- public override void ZoomOut(double factor)
- {
- ZoomBasis = ZoomBasis.ScaleFactor;
- base.ZoomOut(factor);
- }
-
- // TODO:
- /// <summary>
- /// Same as PerformAction(Type) except it lets you rename the HistoryMemento's name.
- /// </summary>
- /// <param name="actionType"></param>
- /// <param name="newName"></param>
- /// <param name="icon"></param>
- public void PerformAction(Type actionType, string newName, ImageResource icon)
- {
- using (new WaitCursorChanger(this))
- {
- ConstructorInfo ci = actionType.GetConstructor(new[] { typeof(DocumentWorkspace) });
- object actionAsObject = ci.Invoke(new object[] { this });
- var action = actionAsObject as DocumentWorkspaceAction;
-
- if (action == null)
- {
- }
- else
- {
- bool nullTool = false;
-
- if ((action.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
- {
- PushNullTool();
- Update();
- nullTool = true;
- }
-
- try
- {
- HistoryMemento ha = action.PerformAction(this);
-
- if (ha != null)
- {
- ha.Name = newName;
- ha.Image = icon;
- History.PushNewMemento(ha);
- }
- }
-
- finally
- {
- if (nullTool)
- {
- PopNullTool();
- }
- }
- }
- }
- }
-
- public event EventHandler ZoomBasisChanging;
- protected virtual void OnZoomBasisChanging()
- {
- if (ZoomBasisChanging != null)
- {
- ZoomBasisChanging(this, EventArgs.Empty);
- }
- }
-
- public event EventHandler ZoomBasisChanged;
- protected virtual void OnZoomBasisChanged()
- {
- if (ZoomBasisChanged != null)
- {
- ZoomBasisChanged(this, EventArgs.Empty);
- }
- }
-
- public ZoomBasis ZoomBasis
- {
- get
- {
- return _zoomBasis;
- }
-
- set
- {
- if (_zoomBasis == value) return;
- OnZoomBasisChanging();
- _zoomBasis = value;
-
- switch (_zoomBasis)
- {
- case ZoomBasis.FitToWindow:
- ZoomToWindow();
-
- // Enable PanelAutoScroll only long enough to recenter the view
- PanelAutoScroll = true;
- PanelAutoScroll = false;
-
- // this would be unset by the scalefactor changes in ZoomToWindow
- _zoomBasis = ZoomBasis.FitToWindow;
- break;
-
- case ZoomBasis.ScaleFactor:
- PanelAutoScroll = true;
- break;
-
- default:
- throw new InvalidEnumArgumentException();
- }
-
- OnZoomBasisChanged();
- }
- }
-
- public void ZoomToSelection()
- {
- if (Selection.IsEmpty)
- {
- ZoomToWindow();
- }
- else
- {
- using (PdnRegion region = Selection.CreateRegion())
- {
- ZoomToRectangle(region.GetBoundsInt());
- }
- }
- }
-
- public void ZoomToRectangle(Rectangle selectionBounds)
- {
- var selectionCenter = new PointF((selectionBounds.Left + selectionBounds.Right + 1) / 2,
- (selectionBounds.Top + selectionBounds.Bottom + 1) / 2);
-
- ScaleFactor zoom = ScaleFactor.Min(ClientRectangleMin.Width, selectionBounds.Width + 2,
- ClientRectangleMin.Height, selectionBounds.Height + 2,
- ScaleFactor.MinValue);
-
- // Zoom out to fit the image
- ZoomBasis = ZoomBasis.ScaleFactor;
- ScaleFactor = zoom;
-
- var cornerPosition = new PointF(selectionCenter.X - (VisibleDocumentRectangleF.Width / 2),
- selectionCenter.Y - (VisibleDocumentRectangleF.Height / 2));
-
- DocumentScrollPositionF = cornerPosition;
- }
-
- protected override void HandleMouseWheel(Control sender, MouseEventArgs e)
- {
- if (ModifierKeys == Keys.Control)
- {
- double mouseDelta = (double)e.Delta / 120.0f;
- Rectangle visibleDocBoundsStart = VisibleDocumentBounds;
- Point mouseDocPt = MouseToDocument(sender, new Point(e.X, e.Y));
- RectangleF visibleDocDocRect1 = VisibleDocumentRectangleF;
-
- var mouseNPt = new PointF(
- (mouseDocPt.X - visibleDocDocRect1.X) / visibleDocDocRect1.Width,
- (mouseDocPt.Y - visibleDocDocRect1.Y) / visibleDocDocRect1.Height);
-
- const double factor = 1.12;
- double mouseFactor = Math.Pow(factor, Math.Abs(mouseDelta));
-
- if (e.Delta > 0)
- {
- ZoomIn(mouseFactor);
- }
- else if (e.Delta < 0)
- {
- ZoomOut(mouseFactor);
- }
-
- RectangleF visibleDocDocRect2 = VisibleDocumentRectangleF;
-
- var scrollPt2 = new PointF(
- mouseDocPt.X - visibleDocDocRect2.Width * mouseNPt.X,
- mouseDocPt.Y - visibleDocDocRect2.Height * mouseNPt.Y);
-
- DocumentScrollPositionF = scrollPt2;
-
- Rectangle visibleDocBoundsEnd = VisibleDocumentBounds;
-
- if (visibleDocBoundsEnd != visibleDocBoundsStart)
- {
- // Make sure the screen updates, otherwise it can get a little funky looking
- Update();
- }
- }
-
- base.HandleMouseWheel(sender, e);
- }
-
- public void SelectClosestVisibleLayer(Layer layer)
- {
- int oldLayerIndex = Document.Layers.IndexOf(layer);
- int newLayerIndex = oldLayerIndex;
-
- // find the closest layer that is still visible
- for (int i = 0; i < Document.Layers.Count; ++i)
- {
- int lower = oldLayerIndex - i;
- int upper = oldLayerIndex + i;
-
- if (lower >= 0 && lower < Document.Layers.Count && ((Layer)Document.Layers[lower]).Visible)
- {
- newLayerIndex = lower;
- break;
- }
-
- if (upper < 0 || upper >= Document.Layers.Count || !((Layer) Document.Layers[upper]).Visible) continue;
- newLayerIndex = upper;
- break;
- }
-
- if (newLayerIndex != oldLayerIndex)
- {
- ActiveLayer = (Layer)Document.Layers[newLayerIndex];
- }
- }
-
- public void UpdateRulerSelectionTinting()
- {
- if (!RulersEnabled) return;
- Rectangle bounds = Selection.GetBounds();
- SetHighlightRectangle(bounds);
- }
-
- private void LayerRemovingHandler(object sender, IndexEventArgs e)
- {
- var layer = (Layer)Document.Layers[e.Index];
- layer.PropertyChanging -= LayerPropertyChangingHandler;
- layer.PropertyChanged -= LayerPropertyChangedHandler;
-
- // pick a new valid layer!
- int newLayerIndex;
-
- if (e.Index == Document.Layers.Count - 1)
- {
- newLayerIndex = e.Index - 1;
- }
- else
- {
- newLayerIndex = e.Index + 1;
- }
-
- if (newLayerIndex >= 0 && newLayerIndex < Document.Layers.Count)
- {
- ActiveLayer = (Layer)Document.Layers[newLayerIndex];
- }
- else
- {
- if (Document.Layers.Count == 0)
- {
- ActiveLayer = null;
- }
- else
- {
- ActiveLayer = (Layer)Document.Layers[0];
- }
- }
- }
-
- private static void LayerRemovedHandler(object sender, IndexEventArgs e)
- {
- }
-
- private void LayerInsertedHandler(object sender, IndexEventArgs e)
- {
- var layer = (Layer)Document.Layers[e.Index];
- ActiveLayer = layer;
- layer.PropertyChanging += LayerPropertyChangingHandler;
- layer.PropertyChanged += LayerPropertyChangedHandler;
- }
-
- private void LayerPropertyChangingHandler(object sender, PropertyEventArgs e)
- {
- string nameFormat = PdnResources.GetString("LayerPropertyChanging.HistoryMementoNameFormat");
- string haName = string.Format(nameFormat, e.PropertyName);
-
- var lpha = new LayerPropertyHistoryMemento(
- haName,
- PdnResources.GetImageResource("Icons.MenuLayersLayerPropertiesIcon.png"),
- this,
- Document.Layers.IndexOf(sender));
-
- History.PushNewMemento(lpha);
- }
-
- private void LayerPropertyChangedHandler(object sender, PropertyEventArgs e)
- {
- var layer = (Layer)sender;
-
- if (!layer.Visible &&
- layer == ActiveLayer &&
- Document.Layers.Count > 1 &&
- !History.IsExecutingMemento)
- {
- SelectClosestVisibleLayer(layer);
- }
- }
-
- private void ToolPulseTimerTick(object sender, EventArgs e)
- {
- if (FindForm() == null || FindForm().WindowState == FormWindowState.Minimized)
- {
- return;
- }
-
- if (Tool != null && Tool.Active)
- {
- Tool.PerformPulse();
- }
- }
-
- protected override void OnLoad(EventArgs e)
- {
- if (_appWorkspace == null)
- {
- throw new InvalidOperationException("Must set the Workspace property");
- }
-
- base.OnLoad(e);
- }
-
- public event EventHandler ActiveLayerChanging;
- protected void OnLayerChanging()
- {
- if (ActiveLayerChanging != null)
- {
- ActiveLayerChanging(this, EventArgs.Empty);
- }
- }
-
- public event EventHandler ActiveLayerChanged;
- protected void OnLayerChanged()
- {
- Focus();
-
- if (ActiveLayerChanged != null)
- {
- ActiveLayerChanged(this, EventArgs.Empty);
- }
- }
-
- public Layer ActiveLayer
- {
- get
- {
- return _activeLayer;
- }
-
- set
- {
- OnLayerChanging();
-
- bool deactivateTool = Tool != null && Tool.DeactivateOnLayerChange;
-
- if (deactivateTool)
- {
- PushNullTool();
- EnableToolPulse = false;
- }
-
- try
- {
- // Verify that the layer is in the document (sanity checking)
- if (Document != null)
- {
- if (value != null && !Document.Layers.Contains(value))
- {
- throw new InvalidOperationException("ActiveLayer was changed to a layer that is not contained within the Document");
- }
- }
- else
- {
- // Document == null
- if (value != null)
- {
- throw new InvalidOperationException("ActiveLayer was set to non-null while Document was null");
- }
- }
-
- // Finally, set the field.
- _activeLayer = value;
- }
-
- finally
- {
- if (deactivateTool)
- {
- PopNullTool();
- EnableToolPulse = true;
- }
- }
-
- OnLayerChanged();
- }
- }
-
- public int ActiveLayerIndex
- {
- get
- {
- return Document.Layers.IndexOf(ActiveLayer);
- }
-
- set
- {
- ActiveLayer = (Layer)Document.Layers[value];
- }
- }
-
- public bool EnableToolPulse
- {
- get
- {
- return _toolPulseTimer.Enabled;
- }
-
- set
- {
- _toolPulseTimer.Enabled = value;
- }
- }
-
- public HistoryStack History
- {
- get
- {
- return _history;
- }
- }
-
- public Tool Tool { get; private set; }
-
- public Type GetToolType()
- {
- return Tool != null ? Tool.GetType() : null;
- }
-
- public void SetToolFromType(Type toolType)
- {
- if (toolType == GetToolType())
- {
- return;
- }
- if (toolType == null)
- {
- SetTool(null);
- }
- else
- {
- Tool newTool = CreateTool(toolType);
- SetTool(newTool);
- }
- }
-
- public void PushNullTool()
- {
- if (_nullToolCount == 0)
- {
- _preNullTool = GetToolType();
- SetTool(null);
- _nullToolCount = 1;
- }
- else
- {
- ++_nullToolCount;
- }
- }
-
- public void PopNullTool()
- {
- --_nullToolCount;
-
- if (_nullToolCount == 0)
- {
- SetToolFromType(_preNullTool);
- _preNullTool = null;
- }
- else if (_nullToolCount < 0)
- {
- throw new InvalidOperationException("PopNullTool() call was not matched with PushNullTool()");
- }
- }
-
- public Type PreviousActiveToolType { get; private set; }
-
- public void SetTool(Tool copyMe)
- {
- OnToolChanging();
-
- if (Tool != null)
- {
- PreviousActiveToolType = Tool.GetType();
- Tool.CursorChanged -= ToolCursorChangedHandler;
- Tool.PerformDeactivate();
- Tool.Dispose();
- Tool = null;
- }
-
- if (copyMe == null)
- {
- EnableToolPulse = false;
- }
- else
- {
- Tracing.LogFeature("SetTool(" + copyMe.GetType().FullName + ")");
- Tool = CreateTool(copyMe.GetType());
- Tool.PerformActivate();
- Tool.CursorChanged += ToolCursorChangedHandler;
-
- if (_suspendToolCursorChanges <= 0)
- {
- Cursor = Tool.Cursor;
- }
-
- EnableToolPulse = true;
- }
-
- OnToolChanged();
- }
-
- public Tool CreateTool(Type toolType)
- {
- return CreateTool(toolType, this);
- }
-
- private static Tool CreateTool(Type toolType, DocumentWorkspace dc)
- {
- ConstructorInfo ci = toolType.GetConstructor(new[] { typeof(DocumentWorkspace) });
- var tool = (Tool)ci.Invoke(new object[] { dc });
- return tool;
- }
-
- private static void InitializeTools()
- {
- // add all the tools
- _tools = new[]
- {
- typeof(RectangleSelectTool),
- typeof(MoveTool),
- typeof(LassoSelectTool),
- typeof(MoveSelectionTool),
-
- typeof(EllipseSelectTool),
- typeof(ZoomTool),
-
- typeof(MagicWandTool),
- typeof(PanTool),
-
- typeof(PaintBucketTool),
- typeof(GradientTool),
-
- typeof(PaintBrushTool),
- typeof(EraserTool),
- typeof(PencilTool),
- typeof(ColorPickerTool),
- typeof(CloneStampTool),
- typeof(RecolorTool),
- typeof(TextTool),
-
- typeof(LineTool),
- typeof(RectangleTool),
- typeof(RoundedRectangleTool),
- typeof(EllipseTool),
- typeof(FreeformShapeTool),
- };
- }
-
- private static void InitializeToolInfos()
- {
- int i = 0;
- _toolInfos = new ToolInfo[_tools.Length];
-
- foreach (Type toolType in _tools)
- {
- using (Tool tool = CreateTool(toolType, null))
- {
- _toolInfos[i] = tool.Info;
- ++i;
- }
- }
- }
-
- public static Type[] Tools
- {
- get
- {
- return (Type[])_tools.Clone();
- }
- }
-
- public static ToolInfo[] ToolInfos
- {
- get
- {
- return (ToolInfo[])_toolInfos.Clone();
- }
- }
-
- public event EventHandler ToolChanging;
- protected void OnToolChanging()
- {
- if (ToolChanging != null)
- {
- ToolChanging(this, EventArgs.Empty);
- }
- }
-
- public event EventHandler ToolChanged;
- protected void OnToolChanged()
- {
- if (ToolChanged != null)
- {
- ToolChanged(this, EventArgs.Empty);
- }
- }
-
- private void ToolCursorChangedHandler(object sender, EventArgs e)
- {
- if (_suspendToolCursorChanges <= 0)
- {
- Cursor = Tool.Cursor;
- }
- }
-
- // Note: static tool data is removed whenever the Document changes
- // TODO: shouldn't this be moved to the Tool class somehow?
- public object GetStaticToolData(Type toolType)
- {
- return _staticToolData[toolType];
- }
-
- public void SetStaticToolData(Type toolType, object data)
- {
- _staticToolData[toolType] = data;
- }
-
- public AppWorkspace AppWorkspace
- {
- get
- {
- return _appWorkspace;
- }
-
- set
- {
- _appWorkspace = value;
- }
- }
-
- public Selection Selection
- {
- get
- {
- return _selection;
- }
- }
-
- public bool EnableOutlineAnimation
- {
- get
- {
- return _selectionRenderer.EnableOutlineAnimation;
- }
-
- set
- {
- _selectionRenderer.EnableOutlineAnimation = value;
- }
- }
-
- public bool EnableSelectionOutline
- {
- get
- {
- return _selectionRenderer.EnableSelectionOutline;
- }
-
- set
- {
- _selectionRenderer.EnableSelectionOutline = value;
- }
- }
-
- public bool EnableSelectionTinting
- {
- get
- {
- return _selectionRenderer.EnableSelectionTinting;
- }
-
- set
- {
- _selectionRenderer.EnableSelectionTinting = value;
- }
- }
-
- public void ResetOutlineWhiteOpacity()
- {
- _selectionRenderer.ResetOutlineWhiteOpacity();
- }
-
- public event EventHandler FilePathChanged;
- protected virtual void OnFilePathChanged()
- {
- if (FilePathChanged != null)
- {
- FilePathChanged(this, EventArgs.Empty);
- }
- }
-
- public string FilePath { get; private set; }
-
- public string GetFriendlyName()
- {
- string friendlyName = FilePath != null ? Path.GetFileName(FilePath) : PdnResources.GetString("Untitled.FriendlyName");
-
- return friendlyName;
- }
-
- public FileType FileType { get; private set; }
-
- public SaveConfigToken SaveConfigToken
- {
- get
- {
- if (_saveConfigToken == null)
- {
- return null;
- }
- return (SaveConfigToken)_saveConfigToken.Clone();
- }
- }
-
- public event EventHandler SaveOptionsChanged;
- protected virtual void OnSaveOptionsChanged()
- {
- if (SaveOptionsChanged != null)
- {
- SaveOptionsChanged(this, EventArgs.Empty);
- }
- }
-
- /// <summary>
- /// Sets the FileType and SaveConfigToken parameters that are used if the
- /// user chooses "Save" from the File menu. These are not used by the
- /// DocumentControl class and should be used by whoever actually goes
- /// to save the Document instance.
- /// </summary>
- /// <param name="fileType"></param>
- /// <param name="saveParameters"></param>
- /// <param name="newFilePath"></param>
- /// <param name="newFileType"></param>
- /// <param name="newSaveConfigToken"></param>
- public void SetDocumentSaveOptions(string newFilePath, FileType newFileType, SaveConfigToken newSaveConfigToken)
- {
- FilePath = newFilePath;
- OnFilePathChanged();
-
- FileType = newFileType;
-
- if (newSaveConfigToken == null)
- {
- _saveConfigToken = null;
- }
- else
- {
- _saveConfigToken = (SaveConfigToken)newSaveConfigToken.Clone();
- }
-
- OnSaveOptionsChanged();
- }
-
- public void GetDocumentSaveOptions(out string filePathResult, out FileType fileTypeResult, out SaveConfigToken saveConfigTokenResult)
- {
- filePathResult = FilePath;
- fileTypeResult = FileType;
-
- if (_saveConfigToken == null)
- {
- saveConfigTokenResult = null;
- }
- else
- {
- saveConfigTokenResult = (SaveConfigToken)_saveConfigToken.Clone();
- }
- }
-
- private bool _isScratchSurfaceBorrowed;
- private string _borrowScratchSurfaceReason = string.Empty;
-
- /// The scratch, stencil, accumulation, whatever buffer. This is used by many parts
- /// of Paint.NET as a temporary area for which to store data.
- /// This surface is 'owned' by any Tool that is active. If you want to use this you
- /// must first deactivate the Tool using PushNullTool() and then reactivate it when
- /// you are finished by calling PopNullTool().
- /// Tools should use Tool.ScratchSurface instead of these API's.
-
- public Surface BorrowScratchSurface(string reason)
- {
- if (_isScratchSurfaceBorrowed)
- {
- throw new InvalidOperationException(
- "ScratchSurface already borrowed: '" +
- _borrowScratchSurfaceReason +
- "' (trying to borrow for: '" + reason + "')");
- }
-
- Tracing.Ping("Borrowing scratchSurface: " + reason);
- _isScratchSurfaceBorrowed = true;
- _borrowScratchSurfaceReason = reason;
- return _scratchSurface;
- }
-
- public void ReturnScratchSurface(Surface borrowedScratchSurface)
- {
- if (!_isScratchSurfaceBorrowed)
- {
- throw new InvalidOperationException("ScratchSurface wasn't borrowed");
- }
-
- if (_scratchSurface != borrowedScratchSurface)
- {
- throw new InvalidOperationException("returned ScratchSurface doesn't match the real one");
- }
-
- Tracing.Ping("Returning scratchSurface: " + this._borrowScratchSurfaceReason);
- _isScratchSurfaceBorrowed = false;
- _borrowScratchSurfaceReason = string.Empty;
- }
-
- /// <summary>
- /// Updates any pertinent EXIF tags, such as "Creation Software", to be
- /// relevant or up-to-date.
- /// </summary>
- /// <param name="document"></param>
- private static void UpdateExifTags(Document document)
- {
- // We want it to say "Creation Software: Paint.NET vX.Y"
- // I have verified that other image editing software overwrites this tag,
- // and does not just add it when it does not exist.
- PropertyItem pi = Exif.CreateAscii(ExifTagID.Software, PdnInfo.GetProductName(false));
- document.Metadata.ReplaceExifValues(ExifTagID.Software, new[] { pi });
- }
-
- private ZoomBasis _savedZb;
- private ScaleFactor _savedSf;
- private int _savedAli;
- protected override void OnDocumentChanging(Document newDocument)
- {
- base.OnDocumentChanging(newDocument);
-
- _savedZb = ZoomBasis;
- _savedSf = ScaleFactor;
-
- if (ActiveLayer != null)
- {
- _savedAli = ActiveLayerIndex;
- }
- else
- {
- _savedAli = -1;
- }
-
- if (newDocument != null)
- {
- UpdateExifTags(newDocument);
- }
-
- if (Document != null)
- {
- foreach (Layer layer in Document.Layers)
- {
- layer.PropertyChanging -= LayerPropertyChangingHandler;
- layer.PropertyChanged -= LayerPropertyChangedHandler;
- }
-
- Document.Layers.RemovingAt -= LayerRemovingHandler;
- Document.Layers.RemovedAt -= LayerRemovedHandler;
- Document.Layers.Inserted -= LayerInsertedHandler;
- }
-
- _staticToolData.Clear();
-
- PushNullTool(); // matching Pop is in OnDocumetChanged()
- ActiveLayer = null;
-
- if (_scratchSurface != null)
- {
- if (_isScratchSurfaceBorrowed)
- {
- throw new InvalidOperationException("scratchSurface is currently borrowed: " + _borrowScratchSurfaceReason);
- }
-
- if (newDocument == null || newDocument.Size != _scratchSurface.Size)
- {
- _scratchSurface.Dispose();
- _scratchSurface = null;
- }
- }
-
- if (!Selection.IsEmpty)
- {
- Selection.Reset();
- }
- }
-
- protected override void OnDocumentChanged()
- {
- // if the ActiveLayer is not in this new document, then
- // we try to set ActiveLayer to the first layer in this
- // new document. But if the document contains no layers,
- // or is null, we just null the ActiveLayer.
- if (Document == null)
- {
- ActiveLayer = null;
- }
- else
- {
- if (Tool != null)
- {
- throw new InvalidOperationException("Tool was not deactivated while Document was being changed");
- }
-
- if (_scratchSurface != null)
- {
- if (_isScratchSurfaceBorrowed)
- {
- throw new InvalidOperationException("scratchSurface is currently borrowed: " + _borrowScratchSurfaceReason);
- }
-
- if (_scratchSurface.Size != Document.Size)
- {
- _scratchSurface.Dispose();
- _scratchSurface = null;
- }
- }
-
- _scratchSurface = new Surface(Document.Size);
-
- Selection.ClipRectangle = Document.Bounds;
-
- foreach (Layer layer in Document.Layers)
- {
- layer.PropertyChanging += LayerPropertyChangingHandler;
- layer.PropertyChanged += LayerPropertyChangedHandler;
- }
-
- Document.Layers.RemovingAt += LayerRemovingHandler;
- Document.Layers.RemovedAt += LayerRemovedHandler;
- Document.Layers.Inserted += LayerInsertedHandler;
-
- if (!Document.Layers.Contains(ActiveLayer))
- {
- if (Document.Layers.Count > 0)
- {
- if (_savedAli >= 0 && _savedAli < Document.Layers.Count)
- {
- ActiveLayer = (Layer)Document.Layers[_savedAli];
- }
- …
Large files files are truncated, but you can click here to view the full file