PageRenderTime 64ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/src/DocumentWorkspace.cs

https://bitbucket.org/tuldok89/openpdn
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

  1. /////////////////////////////////////////////////////////////////////////////////
  2. // Paint.NET //
  3. // Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
  4. // Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
  5. // See src/Resources/Files/License.txt for full licensing and attribution //
  6. // details. //
  7. // . //
  8. /////////////////////////////////////////////////////////////////////////////////
  9. using PaintDotNet.Base;
  10. using PaintDotNet.HistoryFunctions;
  11. using PaintDotNet.HistoryMementos;
  12. using PaintDotNet.SystemLayer;
  13. using PaintDotNet.Tools;
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Generic;
  17. using System.ComponentModel;
  18. using System.Drawing;
  19. using System.Drawing.Imaging;
  20. using System.Globalization;
  21. using System.IO;
  22. using System.Reflection;
  23. using System.Runtime.Serialization;
  24. using System.Security;
  25. using System.Threading;
  26. using System.Windows.Forms;
  27. namespace PaintDotNet
  28. {
  29. /// <summary>
  30. /// Builds on DocumentView by adding application-specific elements.
  31. /// </summary>
  32. internal class DocumentWorkspace
  33. : DocumentView,
  34. IHistoryWorkspace,
  35. IThumbnailProvider
  36. {
  37. public static readonly DateTime NeverSavedDateTime = DateTime.MinValue;
  38. private static Type[] _tools; // TODO: move to Tool class?
  39. private static ToolInfo[] _toolInfos;
  40. private ZoomBasis _zoomBasis;
  41. private AppWorkspace _appWorkspace;
  42. private SaveConfigToken _saveConfigToken;
  43. private readonly Selection _selection = new Selection();
  44. private Surface _scratchSurface;
  45. private readonly SelectionRenderer _selectionRenderer;
  46. private readonly Hashtable _staticToolData = Hashtable.Synchronized(new Hashtable());
  47. private Type _preNullTool;
  48. private int _nullToolCount;
  49. private int _zoomChangesCount;
  50. private readonly HistoryStack _history;
  51. private Layer _activeLayer;
  52. private System.Windows.Forms.Timer _toolPulseTimer;
  53. private DateTime _lastSaveTime = NeverSavedDateTime;
  54. private int _suspendToolCursorChanges;
  55. private readonly string _contextStatusBarWithAngleFormat = PdnResources.GetString("StatusBar.Context.SelectedArea.Text.WithAngle.Format");
  56. private readonly string _contextStatusBarFormat = PdnResources.GetString("StatusBar.Context.SelectedArea.Text.Format");
  57. public void SuspendToolCursorChanges()
  58. {
  59. ++_suspendToolCursorChanges;
  60. }
  61. public void ResumeToolCursorChanges()
  62. {
  63. --_suspendToolCursorChanges;
  64. if (_suspendToolCursorChanges <= 0 && Tool != null)
  65. {
  66. Cursor = Tool.Cursor;
  67. }
  68. }
  69. public ImageResource StatusIcon { get; private set; }
  70. public string StatusText { get; private set; }
  71. public void SetStatus(string newStatusText, ImageResource newStatusIcon)
  72. {
  73. StatusText = newStatusText;
  74. StatusIcon = newStatusIcon;
  75. OnStatusChanged();
  76. }
  77. public event EventHandler StatusChanged;
  78. protected virtual void OnStatusChanged()
  79. {
  80. if (StatusChanged != null)
  81. {
  82. StatusChanged(this, EventArgs.Empty);
  83. }
  84. }
  85. static DocumentWorkspace()
  86. {
  87. InitializeTools();
  88. InitializeToolInfos();
  89. }
  90. public DateTime LastSaveTime
  91. {
  92. get
  93. {
  94. return _lastSaveTime;
  95. }
  96. }
  97. public bool IsZoomChanging
  98. {
  99. get
  100. {
  101. return (_zoomChangesCount > 0);
  102. }
  103. }
  104. private void BeginZoomChanges()
  105. {
  106. ++_zoomChangesCount;
  107. }
  108. private void EndZoomChanges()
  109. {
  110. --_zoomChangesCount;
  111. }
  112. protected override void OnSizeChanged(EventArgs e)
  113. {
  114. PerformLayout();
  115. base.OnSizeChanged(e);
  116. }
  117. protected override void OnLayout(LayoutEventArgs e)
  118. {
  119. if (_zoomBasis == ZoomBasis.FitToWindow)
  120. {
  121. ZoomToWindow();
  122. // This bizarre ordering of setting PanelAutoScroll prevents some very weird layout/scroll-without-scrollbars stuff.
  123. PanelAutoScroll = true;
  124. PanelAutoScroll = false;
  125. }
  126. base.OnLayout(e);
  127. }
  128. protected override void OnResize(EventArgs e)
  129. {
  130. if (_zoomBasis == ZoomBasis.FitToWindow)
  131. {
  132. PerformLayout();
  133. }
  134. base.OnResize(e);
  135. }
  136. public DocumentWorkspace()
  137. {
  138. FileType = null;
  139. FilePath = null;
  140. _activeLayer = null;
  141. _history = new HistoryStack(this);
  142. InitializeComponent();
  143. // hook the DocumentWorkspace with its selectedPath ...
  144. _selectionRenderer = new SelectionRenderer(RendererList, Selection, this);
  145. RendererList.Add(_selectionRenderer, true);
  146. _selectionRenderer.EnableOutlineAnimation = true;
  147. _selectionRenderer.EnableSelectionTinting = false;
  148. _selectionRenderer.EnableSelectionOutline = true;
  149. _selection.Changed += SelectionChanged;
  150. _zoomBasis = ZoomBasis.FitToWindow;
  151. }
  152. protected override void OnUnitsChanged()
  153. {
  154. if (!Selection.IsEmpty)
  155. {
  156. UpdateSelectionInfoInStatusBar();
  157. }
  158. base.OnUnitsChanged();
  159. }
  160. public void UpdateStatusBarToToolHelpText(Tool tool)
  161. {
  162. if (tool == null)
  163. {
  164. SetStatus(string.Empty, null);
  165. }
  166. else
  167. {
  168. string toolName = tool.Name;
  169. string helpText = tool.HelpText;
  170. string contextFormat = PdnResources.GetString("StatusBar.Context.Help.Text.Format");
  171. string contextText = string.Format(contextFormat, toolName, helpText);
  172. SetStatus(contextText, PdnResources.GetImageResource("Icons.MenuHelpHelpTopicsIcon.png"));
  173. }
  174. }
  175. public void UpdateStatusBarToToolHelpText()
  176. {
  177. UpdateStatusBarToToolHelpText(Tool);
  178. }
  179. private void UpdateSelectionInfoInStatusBar()
  180. {
  181. if (Selection.IsEmpty)
  182. {
  183. UpdateStatusBarToToolHelpText();
  184. }
  185. else
  186. {
  187. string newStatusText;
  188. int area;
  189. Rectangle bounds;
  190. using (PdnRegion tempSelection = Selection.CreateRegionRaw())
  191. {
  192. tempSelection.Intersect(Document.Bounds);
  193. bounds = Utility.GetRegionBounds(tempSelection);
  194. area = tempSelection.GetArea();
  195. }
  196. string unitsAbbreviationXY;
  197. string xString;
  198. string yString;
  199. string unitsAbbreviationWH;
  200. string widthString;
  201. string heightString;
  202. Document.CoordinatesToStrings(Units, bounds.X, bounds.Y, out xString, out yString, out unitsAbbreviationXY);
  203. Document.CoordinatesToStrings(Units, bounds.Width, bounds.Height, out widthString, out heightString, out unitsAbbreviationWH);
  204. var nfi = (NumberFormatInfo)CultureInfo.CurrentCulture.NumberFormat.Clone();
  205. string areaString;
  206. if (Units == MeasurementUnit.Pixel)
  207. {
  208. nfi.NumberDecimalDigits = 0;
  209. areaString = area.ToString("N", nfi);
  210. }
  211. else
  212. {
  213. nfi.NumberDecimalDigits = 2;
  214. double areaD = Document.PixelAreaToPhysicalArea(area, Units);
  215. areaString = areaD.ToString("N", nfi);
  216. }
  217. string pluralUnits = PdnResources.GetString("MeasurementUnit." + Units + ".Plural");
  218. var moveTool = Tool as MoveToolBase;
  219. if (moveTool != null && moveTool.HostShouldShowAngle)
  220. {
  221. var nfi2 = (NumberFormatInfo)nfi.Clone();
  222. nfi2.NumberDecimalDigits = 2;
  223. float angle = moveTool.HostAngle;
  224. while (angle > 180.0f)
  225. {
  226. angle -= 360.0f;
  227. }
  228. while (angle < -180.0f)
  229. {
  230. angle += 360.0f;
  231. }
  232. newStatusText = string.Format(
  233. _contextStatusBarWithAngleFormat,
  234. xString,
  235. unitsAbbreviationXY,
  236. yString,
  237. unitsAbbreviationXY,
  238. widthString,
  239. unitsAbbreviationWH,
  240. heightString,
  241. unitsAbbreviationWH,
  242. areaString,
  243. pluralUnits.ToLower(),
  244. moveTool.HostAngle.ToString("N", nfi2));
  245. }
  246. else
  247. {
  248. newStatusText = string.Format(
  249. _contextStatusBarFormat,
  250. xString,
  251. unitsAbbreviationXY,
  252. yString,
  253. unitsAbbreviationXY,
  254. widthString,
  255. unitsAbbreviationWH,
  256. heightString,
  257. unitsAbbreviationWH,
  258. areaString,
  259. pluralUnits.ToLower());
  260. }
  261. SetStatus(newStatusText, PdnResources.GetImageResource("Icons.SelectionIcon.png"));
  262. }
  263. }
  264. private void SelectionChanged(object sender, EventArgs e)
  265. {
  266. UpdateRulerSelectionTinting();
  267. UpdateSelectionInfoInStatusBar();
  268. }
  269. private void InitializeComponent()
  270. {
  271. _toolPulseTimer = new System.Windows.Forms.Timer {Interval = 16};
  272. _toolPulseTimer.Tick += ToolPulseTimerTick;
  273. }
  274. protected override void Dispose(bool disposing)
  275. {
  276. if (disposing)
  277. {
  278. if (Tool != null)
  279. {
  280. Tool.Dispose();
  281. Tool = null;
  282. }
  283. }
  284. base.Dispose(disposing);
  285. }
  286. public void PerformActionAsync(DocumentWorkspaceAction action)
  287. {
  288. BeginInvoke(new Procedure<DocumentWorkspaceAction>(PerformAction), new object[] { action });
  289. }
  290. public void PerformAction(DocumentWorkspaceAction action)
  291. {
  292. bool nullTool = false;
  293. if ((action.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
  294. {
  295. PushNullTool();
  296. Update();
  297. nullTool = true;
  298. }
  299. try
  300. {
  301. using (new WaitCursorChanger(this))
  302. {
  303. HistoryMemento ha = action.PerformAction(this);
  304. if (ha != null)
  305. {
  306. History.PushNewMemento(ha);
  307. }
  308. }
  309. }
  310. finally
  311. {
  312. if (nullTool)
  313. {
  314. PopNullTool();
  315. }
  316. }
  317. }
  318. /// <summary>
  319. /// Executes a HistoryFunction in the context of this DocumentWorkspace.
  320. /// </summary>
  321. /// <param name="function">The HistoryFunction to execute.</param>
  322. /// <remarks>
  323. /// Depending on the HistoryFunction, the currently active tool may be refreshed.
  324. /// </remarks>
  325. public HistoryFunctionResult ExecuteFunction(HistoryFunction function)
  326. {
  327. HistoryFunctionResult result;
  328. bool nullTool = false;
  329. if ((function.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
  330. {
  331. PushNullTool();
  332. Update();
  333. nullTool = true;
  334. }
  335. try
  336. {
  337. using (new WaitCursorChanger(this))
  338. {
  339. HistoryMemento hm = null;
  340. string errorText;
  341. try
  342. {
  343. bool cancelled = false;
  344. if ((function.ActionFlags & ActionFlags.ReportsProgress) != ActionFlags.ReportsProgress)
  345. {
  346. hm = function.Execute(this);
  347. }
  348. else
  349. {
  350. var pd = new ProgressDialog();
  351. bool pdLoaded = false;
  352. bool closeAtLoad = false;
  353. EventHandler loadCallback =
  354. delegate
  355. {
  356. pdLoaded = true;
  357. if (closeAtLoad)
  358. {
  359. pd.Close();
  360. }
  361. };
  362. ProgressEventHandler progressCallback =
  363. delegate(object sender, ProgressEventArgs e)
  364. {
  365. if (!pdLoaded)
  366. {
  367. }
  368. else
  369. {
  370. double newValue = Utility.Clamp(e.Percent, 0.0, 100.0);
  371. pd.Value = newValue;
  372. }
  373. };
  374. EventHandler<EventArgs<HistoryMemento>> finishedCallback =
  375. delegate(object sender, EventArgs<HistoryMemento> e)
  376. {
  377. hm = e.Data;
  378. if (pdLoaded)
  379. {
  380. // TODO: fix ProgressDialog's very weird interface
  381. pd.ExternalFinish();
  382. pd.Close();
  383. }
  384. else
  385. {
  386. closeAtLoad = true;
  387. }
  388. };
  389. EventHandler cancelClickCallback =
  390. delegate
  391. {
  392. cancelled = true;
  393. function.RequestCancel();
  394. //pd.Cancellable = false;
  395. };
  396. pd.Text = PdnInfo.GetBareProductName();
  397. pd.Description = PdnResources.GetString("ExecuteFunction.ProgressDialog.Description.Text");
  398. pd.Load += loadCallback;
  399. pd.Cancellable = false; //function.Cancellable;
  400. pd.CancelClick += cancelClickCallback;
  401. function.Progress += progressCallback;
  402. function.BeginExecute(this, this, finishedCallback);
  403. pd.ShowDialog(this);
  404. pd.Dispose();
  405. }
  406. if (hm == null && !cancelled)
  407. {
  408. result = HistoryFunctionResult.SuccessNoOp;
  409. }
  410. else if (hm == null && cancelled)
  411. {
  412. result = HistoryFunctionResult.Cancelled;
  413. }
  414. else
  415. {
  416. result = HistoryFunctionResult.Success;
  417. }
  418. errorText = null;
  419. }
  420. catch (HistoryFunctionNonFatalException hfnfex)
  421. {
  422. if (hfnfex.InnerException is OutOfMemoryException)
  423. {
  424. result = HistoryFunctionResult.OutOfMemory;
  425. }
  426. else
  427. {
  428. result = HistoryFunctionResult.NonFatalError;
  429. }
  430. if (hfnfex.LocalizedErrorText != null)
  431. {
  432. errorText = hfnfex.LocalizedErrorText;
  433. }
  434. else
  435. {
  436. if (hfnfex.InnerException is OutOfMemoryException)
  437. {
  438. errorText = PdnResources.GetString("ExecuteFunction.GenericOutOfMemory");
  439. }
  440. else
  441. {
  442. errorText = PdnResources.GetString("ExecuteFunction.GenericError");
  443. }
  444. }
  445. }
  446. if (errorText != null)
  447. {
  448. Utility.ErrorBox(this, errorText);
  449. }
  450. if (hm != null)
  451. {
  452. History.PushNewMemento(hm);
  453. }
  454. }
  455. }
  456. finally
  457. {
  458. if (nullTool)
  459. {
  460. PopNullTool();
  461. }
  462. }
  463. return result;
  464. }
  465. public override void ZoomIn()
  466. {
  467. ZoomBasis = ZoomBasis.ScaleFactor;
  468. base.ZoomIn();
  469. }
  470. public override void ZoomIn(double factor)
  471. {
  472. ZoomBasis = ZoomBasis.ScaleFactor;
  473. base.ZoomIn(factor);
  474. }
  475. public override void ZoomOut()
  476. {
  477. ZoomBasis = ZoomBasis.ScaleFactor;
  478. base.ZoomOut();
  479. }
  480. public override void ZoomOut(double factor)
  481. {
  482. ZoomBasis = ZoomBasis.ScaleFactor;
  483. base.ZoomOut(factor);
  484. }
  485. // TODO:
  486. /// <summary>
  487. /// Same as PerformAction(Type) except it lets you rename the HistoryMemento's name.
  488. /// </summary>
  489. /// <param name="actionType"></param>
  490. /// <param name="newName"></param>
  491. /// <param name="icon"></param>
  492. public void PerformAction(Type actionType, string newName, ImageResource icon)
  493. {
  494. using (new WaitCursorChanger(this))
  495. {
  496. ConstructorInfo ci = actionType.GetConstructor(new[] { typeof(DocumentWorkspace) });
  497. object actionAsObject = ci.Invoke(new object[] { this });
  498. var action = actionAsObject as DocumentWorkspaceAction;
  499. if (action == null)
  500. {
  501. }
  502. else
  503. {
  504. bool nullTool = false;
  505. if ((action.ActionFlags & ActionFlags.KeepToolActive) != ActionFlags.KeepToolActive)
  506. {
  507. PushNullTool();
  508. Update();
  509. nullTool = true;
  510. }
  511. try
  512. {
  513. HistoryMemento ha = action.PerformAction(this);
  514. if (ha != null)
  515. {
  516. ha.Name = newName;
  517. ha.Image = icon;
  518. History.PushNewMemento(ha);
  519. }
  520. }
  521. finally
  522. {
  523. if (nullTool)
  524. {
  525. PopNullTool();
  526. }
  527. }
  528. }
  529. }
  530. }
  531. public event EventHandler ZoomBasisChanging;
  532. protected virtual void OnZoomBasisChanging()
  533. {
  534. if (ZoomBasisChanging != null)
  535. {
  536. ZoomBasisChanging(this, EventArgs.Empty);
  537. }
  538. }
  539. public event EventHandler ZoomBasisChanged;
  540. protected virtual void OnZoomBasisChanged()
  541. {
  542. if (ZoomBasisChanged != null)
  543. {
  544. ZoomBasisChanged(this, EventArgs.Empty);
  545. }
  546. }
  547. public ZoomBasis ZoomBasis
  548. {
  549. get
  550. {
  551. return _zoomBasis;
  552. }
  553. set
  554. {
  555. if (_zoomBasis == value) return;
  556. OnZoomBasisChanging();
  557. _zoomBasis = value;
  558. switch (_zoomBasis)
  559. {
  560. case ZoomBasis.FitToWindow:
  561. ZoomToWindow();
  562. // Enable PanelAutoScroll only long enough to recenter the view
  563. PanelAutoScroll = true;
  564. PanelAutoScroll = false;
  565. // this would be unset by the scalefactor changes in ZoomToWindow
  566. _zoomBasis = ZoomBasis.FitToWindow;
  567. break;
  568. case ZoomBasis.ScaleFactor:
  569. PanelAutoScroll = true;
  570. break;
  571. default:
  572. throw new InvalidEnumArgumentException();
  573. }
  574. OnZoomBasisChanged();
  575. }
  576. }
  577. public void ZoomToSelection()
  578. {
  579. if (Selection.IsEmpty)
  580. {
  581. ZoomToWindow();
  582. }
  583. else
  584. {
  585. using (PdnRegion region = Selection.CreateRegion())
  586. {
  587. ZoomToRectangle(region.GetBoundsInt());
  588. }
  589. }
  590. }
  591. public void ZoomToRectangle(Rectangle selectionBounds)
  592. {
  593. var selectionCenter = new PointF((selectionBounds.Left + selectionBounds.Right + 1) / 2,
  594. (selectionBounds.Top + selectionBounds.Bottom + 1) / 2);
  595. ScaleFactor zoom = ScaleFactor.Min(ClientRectangleMin.Width, selectionBounds.Width + 2,
  596. ClientRectangleMin.Height, selectionBounds.Height + 2,
  597. ScaleFactor.MinValue);
  598. // Zoom out to fit the image
  599. ZoomBasis = ZoomBasis.ScaleFactor;
  600. ScaleFactor = zoom;
  601. var cornerPosition = new PointF(selectionCenter.X - (VisibleDocumentRectangleF.Width / 2),
  602. selectionCenter.Y - (VisibleDocumentRectangleF.Height / 2));
  603. DocumentScrollPositionF = cornerPosition;
  604. }
  605. protected override void HandleMouseWheel(Control sender, MouseEventArgs e)
  606. {
  607. if (ModifierKeys == Keys.Control)
  608. {
  609. double mouseDelta = (double)e.Delta / 120.0f;
  610. Rectangle visibleDocBoundsStart = VisibleDocumentBounds;
  611. Point mouseDocPt = MouseToDocument(sender, new Point(e.X, e.Y));
  612. RectangleF visibleDocDocRect1 = VisibleDocumentRectangleF;
  613. var mouseNPt = new PointF(
  614. (mouseDocPt.X - visibleDocDocRect1.X) / visibleDocDocRect1.Width,
  615. (mouseDocPt.Y - visibleDocDocRect1.Y) / visibleDocDocRect1.Height);
  616. const double factor = 1.12;
  617. double mouseFactor = Math.Pow(factor, Math.Abs(mouseDelta));
  618. if (e.Delta > 0)
  619. {
  620. ZoomIn(mouseFactor);
  621. }
  622. else if (e.Delta < 0)
  623. {
  624. ZoomOut(mouseFactor);
  625. }
  626. RectangleF visibleDocDocRect2 = VisibleDocumentRectangleF;
  627. var scrollPt2 = new PointF(
  628. mouseDocPt.X - visibleDocDocRect2.Width * mouseNPt.X,
  629. mouseDocPt.Y - visibleDocDocRect2.Height * mouseNPt.Y);
  630. DocumentScrollPositionF = scrollPt2;
  631. Rectangle visibleDocBoundsEnd = VisibleDocumentBounds;
  632. if (visibleDocBoundsEnd != visibleDocBoundsStart)
  633. {
  634. // Make sure the screen updates, otherwise it can get a little funky looking
  635. Update();
  636. }
  637. }
  638. base.HandleMouseWheel(sender, e);
  639. }
  640. public void SelectClosestVisibleLayer(Layer layer)
  641. {
  642. int oldLayerIndex = Document.Layers.IndexOf(layer);
  643. int newLayerIndex = oldLayerIndex;
  644. // find the closest layer that is still visible
  645. for (int i = 0; i < Document.Layers.Count; ++i)
  646. {
  647. int lower = oldLayerIndex - i;
  648. int upper = oldLayerIndex + i;
  649. if (lower >= 0 && lower < Document.Layers.Count && ((Layer)Document.Layers[lower]).Visible)
  650. {
  651. newLayerIndex = lower;
  652. break;
  653. }
  654. if (upper < 0 || upper >= Document.Layers.Count || !((Layer) Document.Layers[upper]).Visible) continue;
  655. newLayerIndex = upper;
  656. break;
  657. }
  658. if (newLayerIndex != oldLayerIndex)
  659. {
  660. ActiveLayer = (Layer)Document.Layers[newLayerIndex];
  661. }
  662. }
  663. public void UpdateRulerSelectionTinting()
  664. {
  665. if (!RulersEnabled) return;
  666. Rectangle bounds = Selection.GetBounds();
  667. SetHighlightRectangle(bounds);
  668. }
  669. private void LayerRemovingHandler(object sender, IndexEventArgs e)
  670. {
  671. var layer = (Layer)Document.Layers[e.Index];
  672. layer.PropertyChanging -= LayerPropertyChangingHandler;
  673. layer.PropertyChanged -= LayerPropertyChangedHandler;
  674. // pick a new valid layer!
  675. int newLayerIndex;
  676. if (e.Index == Document.Layers.Count - 1)
  677. {
  678. newLayerIndex = e.Index - 1;
  679. }
  680. else
  681. {
  682. newLayerIndex = e.Index + 1;
  683. }
  684. if (newLayerIndex >= 0 && newLayerIndex < Document.Layers.Count)
  685. {
  686. ActiveLayer = (Layer)Document.Layers[newLayerIndex];
  687. }
  688. else
  689. {
  690. if (Document.Layers.Count == 0)
  691. {
  692. ActiveLayer = null;
  693. }
  694. else
  695. {
  696. ActiveLayer = (Layer)Document.Layers[0];
  697. }
  698. }
  699. }
  700. private static void LayerRemovedHandler(object sender, IndexEventArgs e)
  701. {
  702. }
  703. private void LayerInsertedHandler(object sender, IndexEventArgs e)
  704. {
  705. var layer = (Layer)Document.Layers[e.Index];
  706. ActiveLayer = layer;
  707. layer.PropertyChanging += LayerPropertyChangingHandler;
  708. layer.PropertyChanged += LayerPropertyChangedHandler;
  709. }
  710. private void LayerPropertyChangingHandler(object sender, PropertyEventArgs e)
  711. {
  712. string nameFormat = PdnResources.GetString("LayerPropertyChanging.HistoryMementoNameFormat");
  713. string haName = string.Format(nameFormat, e.PropertyName);
  714. var lpha = new LayerPropertyHistoryMemento(
  715. haName,
  716. PdnResources.GetImageResource("Icons.MenuLayersLayerPropertiesIcon.png"),
  717. this,
  718. Document.Layers.IndexOf(sender));
  719. History.PushNewMemento(lpha);
  720. }
  721. private void LayerPropertyChangedHandler(object sender, PropertyEventArgs e)
  722. {
  723. var layer = (Layer)sender;
  724. if (!layer.Visible &&
  725. layer == ActiveLayer &&
  726. Document.Layers.Count > 1 &&
  727. !History.IsExecutingMemento)
  728. {
  729. SelectClosestVisibleLayer(layer);
  730. }
  731. }
  732. private void ToolPulseTimerTick(object sender, EventArgs e)
  733. {
  734. if (FindForm() == null || FindForm().WindowState == FormWindowState.Minimized)
  735. {
  736. return;
  737. }
  738. if (Tool != null && Tool.Active)
  739. {
  740. Tool.PerformPulse();
  741. }
  742. }
  743. protected override void OnLoad(EventArgs e)
  744. {
  745. if (_appWorkspace == null)
  746. {
  747. throw new InvalidOperationException("Must set the Workspace property");
  748. }
  749. base.OnLoad(e);
  750. }
  751. public event EventHandler ActiveLayerChanging;
  752. protected void OnLayerChanging()
  753. {
  754. if (ActiveLayerChanging != null)
  755. {
  756. ActiveLayerChanging(this, EventArgs.Empty);
  757. }
  758. }
  759. public event EventHandler ActiveLayerChanged;
  760. protected void OnLayerChanged()
  761. {
  762. Focus();
  763. if (ActiveLayerChanged != null)
  764. {
  765. ActiveLayerChanged(this, EventArgs.Empty);
  766. }
  767. }
  768. public Layer ActiveLayer
  769. {
  770. get
  771. {
  772. return _activeLayer;
  773. }
  774. set
  775. {
  776. OnLayerChanging();
  777. bool deactivateTool = Tool != null && Tool.DeactivateOnLayerChange;
  778. if (deactivateTool)
  779. {
  780. PushNullTool();
  781. EnableToolPulse = false;
  782. }
  783. try
  784. {
  785. // Verify that the layer is in the document (sanity checking)
  786. if (Document != null)
  787. {
  788. if (value != null && !Document.Layers.Contains(value))
  789. {
  790. throw new InvalidOperationException("ActiveLayer was changed to a layer that is not contained within the Document");
  791. }
  792. }
  793. else
  794. {
  795. // Document == null
  796. if (value != null)
  797. {
  798. throw new InvalidOperationException("ActiveLayer was set to non-null while Document was null");
  799. }
  800. }
  801. // Finally, set the field.
  802. _activeLayer = value;
  803. }
  804. finally
  805. {
  806. if (deactivateTool)
  807. {
  808. PopNullTool();
  809. EnableToolPulse = true;
  810. }
  811. }
  812. OnLayerChanged();
  813. }
  814. }
  815. public int ActiveLayerIndex
  816. {
  817. get
  818. {
  819. return Document.Layers.IndexOf(ActiveLayer);
  820. }
  821. set
  822. {
  823. ActiveLayer = (Layer)Document.Layers[value];
  824. }
  825. }
  826. public bool EnableToolPulse
  827. {
  828. get
  829. {
  830. return _toolPulseTimer.Enabled;
  831. }
  832. set
  833. {
  834. _toolPulseTimer.Enabled = value;
  835. }
  836. }
  837. public HistoryStack History
  838. {
  839. get
  840. {
  841. return _history;
  842. }
  843. }
  844. public Tool Tool { get; private set; }
  845. public Type GetToolType()
  846. {
  847. return Tool != null ? Tool.GetType() : null;
  848. }
  849. public void SetToolFromType(Type toolType)
  850. {
  851. if (toolType == GetToolType())
  852. {
  853. return;
  854. }
  855. if (toolType == null)
  856. {
  857. SetTool(null);
  858. }
  859. else
  860. {
  861. Tool newTool = CreateTool(toolType);
  862. SetTool(newTool);
  863. }
  864. }
  865. public void PushNullTool()
  866. {
  867. if (_nullToolCount == 0)
  868. {
  869. _preNullTool = GetToolType();
  870. SetTool(null);
  871. _nullToolCount = 1;
  872. }
  873. else
  874. {
  875. ++_nullToolCount;
  876. }
  877. }
  878. public void PopNullTool()
  879. {
  880. --_nullToolCount;
  881. if (_nullToolCount == 0)
  882. {
  883. SetToolFromType(_preNullTool);
  884. _preNullTool = null;
  885. }
  886. else if (_nullToolCount < 0)
  887. {
  888. throw new InvalidOperationException("PopNullTool() call was not matched with PushNullTool()");
  889. }
  890. }
  891. public Type PreviousActiveToolType { get; private set; }
  892. public void SetTool(Tool copyMe)
  893. {
  894. OnToolChanging();
  895. if (Tool != null)
  896. {
  897. PreviousActiveToolType = Tool.GetType();
  898. Tool.CursorChanged -= ToolCursorChangedHandler;
  899. Tool.PerformDeactivate();
  900. Tool.Dispose();
  901. Tool = null;
  902. }
  903. if (copyMe == null)
  904. {
  905. EnableToolPulse = false;
  906. }
  907. else
  908. {
  909. Tracing.LogFeature("SetTool(" + copyMe.GetType().FullName + ")");
  910. Tool = CreateTool(copyMe.GetType());
  911. Tool.PerformActivate();
  912. Tool.CursorChanged += ToolCursorChangedHandler;
  913. if (_suspendToolCursorChanges <= 0)
  914. {
  915. Cursor = Tool.Cursor;
  916. }
  917. EnableToolPulse = true;
  918. }
  919. OnToolChanged();
  920. }
  921. public Tool CreateTool(Type toolType)
  922. {
  923. return CreateTool(toolType, this);
  924. }
  925. private static Tool CreateTool(Type toolType, DocumentWorkspace dc)
  926. {
  927. ConstructorInfo ci = toolType.GetConstructor(new[] { typeof(DocumentWorkspace) });
  928. var tool = (Tool)ci.Invoke(new object[] { dc });
  929. return tool;
  930. }
  931. private static void InitializeTools()
  932. {
  933. // add all the tools
  934. _tools = new[]
  935. {
  936. typeof(RectangleSelectTool),
  937. typeof(MoveTool),
  938. typeof(LassoSelectTool),
  939. typeof(MoveSelectionTool),
  940. typeof(EllipseSelectTool),
  941. typeof(ZoomTool),
  942. typeof(MagicWandTool),
  943. typeof(PanTool),
  944. typeof(PaintBucketTool),
  945. typeof(GradientTool),
  946. typeof(PaintBrushTool),
  947. typeof(EraserTool),
  948. typeof(PencilTool),
  949. typeof(ColorPickerTool),
  950. typeof(CloneStampTool),
  951. typeof(RecolorTool),
  952. typeof(TextTool),
  953. typeof(LineTool),
  954. typeof(RectangleTool),
  955. typeof(RoundedRectangleTool),
  956. typeof(EllipseTool),
  957. typeof(FreeformShapeTool),
  958. };
  959. }
  960. private static void InitializeToolInfos()
  961. {
  962. int i = 0;
  963. _toolInfos = new ToolInfo[_tools.Length];
  964. foreach (Type toolType in _tools)
  965. {
  966. using (Tool tool = CreateTool(toolType, null))
  967. {
  968. _toolInfos[i] = tool.Info;
  969. ++i;
  970. }
  971. }
  972. }
  973. public static Type[] Tools
  974. {
  975. get
  976. {
  977. return (Type[])_tools.Clone();
  978. }
  979. }
  980. public static ToolInfo[] ToolInfos
  981. {
  982. get
  983. {
  984. return (ToolInfo[])_toolInfos.Clone();
  985. }
  986. }
  987. public event EventHandler ToolChanging;
  988. protected void OnToolChanging()
  989. {
  990. if (ToolChanging != null)
  991. {
  992. ToolChanging(this, EventArgs.Empty);
  993. }
  994. }
  995. public event EventHandler ToolChanged;
  996. protected void OnToolChanged()
  997. {
  998. if (ToolChanged != null)
  999. {
  1000. ToolChanged(this, EventArgs.Empty);
  1001. }
  1002. }
  1003. private void ToolCursorChangedHandler(object sender, EventArgs e)
  1004. {
  1005. if (_suspendToolCursorChanges <= 0)
  1006. {
  1007. Cursor = Tool.Cursor;
  1008. }
  1009. }
  1010. // Note: static tool data is removed whenever the Document changes
  1011. // TODO: shouldn't this be moved to the Tool class somehow?
  1012. public object GetStaticToolData(Type toolType)
  1013. {
  1014. return _staticToolData[toolType];
  1015. }
  1016. public void SetStaticToolData(Type toolType, object data)
  1017. {
  1018. _staticToolData[toolType] = data;
  1019. }
  1020. public AppWorkspace AppWorkspace
  1021. {
  1022. get
  1023. {
  1024. return _appWorkspace;
  1025. }
  1026. set
  1027. {
  1028. _appWorkspace = value;
  1029. }
  1030. }
  1031. public Selection Selection
  1032. {
  1033. get
  1034. {
  1035. return _selection;
  1036. }
  1037. }
  1038. public bool EnableOutlineAnimation
  1039. {
  1040. get
  1041. {
  1042. return _selectionRenderer.EnableOutlineAnimation;
  1043. }
  1044. set
  1045. {
  1046. _selectionRenderer.EnableOutlineAnimation = value;
  1047. }
  1048. }
  1049. public bool EnableSelectionOutline
  1050. {
  1051. get
  1052. {
  1053. return _selectionRenderer.EnableSelectionOutline;
  1054. }
  1055. set
  1056. {
  1057. _selectionRenderer.EnableSelectionOutline = value;
  1058. }
  1059. }
  1060. public bool EnableSelectionTinting
  1061. {
  1062. get
  1063. {
  1064. return _selectionRenderer.EnableSelectionTinting;
  1065. }
  1066. set
  1067. {
  1068. _selectionRenderer.EnableSelectionTinting = value;
  1069. }
  1070. }
  1071. public void ResetOutlineWhiteOpacity()
  1072. {
  1073. _selectionRenderer.ResetOutlineWhiteOpacity();
  1074. }
  1075. public event EventHandler FilePathChanged;
  1076. protected virtual void OnFilePathChanged()
  1077. {
  1078. if (FilePathChanged != null)
  1079. {
  1080. FilePathChanged(this, EventArgs.Empty);
  1081. }
  1082. }
  1083. public string FilePath { get; private set; }
  1084. public string GetFriendlyName()
  1085. {
  1086. string friendlyName = FilePath != null ? Path.GetFileName(FilePath) : PdnResources.GetString("Untitled.FriendlyName");
  1087. return friendlyName;
  1088. }
  1089. public FileType FileType { get; private set; }
  1090. public SaveConfigToken SaveConfigToken
  1091. {
  1092. get
  1093. {
  1094. if (_saveConfigToken == null)
  1095. {
  1096. return null;
  1097. }
  1098. return (SaveConfigToken)_saveConfigToken.Clone();
  1099. }
  1100. }
  1101. public event EventHandler SaveOptionsChanged;
  1102. protected virtual void OnSaveOptionsChanged()
  1103. {
  1104. if (SaveOptionsChanged != null)
  1105. {
  1106. SaveOptionsChanged(this, EventArgs.Empty);
  1107. }
  1108. }
  1109. /// <summary>
  1110. /// Sets the FileType and SaveConfigToken parameters that are used if the
  1111. /// user chooses "Save" from the File menu. These are not used by the
  1112. /// DocumentControl class and should be used by whoever actually goes
  1113. /// to save the Document instance.
  1114. /// </summary>
  1115. /// <param name="fileType"></param>
  1116. /// <param name="saveParameters"></param>
  1117. /// <param name="newFilePath"></param>
  1118. /// <param name="newFileType"></param>
  1119. /// <param name="newSaveConfigToken"></param>
  1120. public void SetDocumentSaveOptions(string newFilePath, FileType newFileType, SaveConfigToken newSaveConfigToken)
  1121. {
  1122. FilePath = newFilePath;
  1123. OnFilePathChanged();
  1124. FileType = newFileType;
  1125. if (newSaveConfigToken == null)
  1126. {
  1127. _saveConfigToken = null;
  1128. }
  1129. else
  1130. {
  1131. _saveConfigToken = (SaveConfigToken)newSaveConfigToken.Clone();
  1132. }
  1133. OnSaveOptionsChanged();
  1134. }
  1135. public void GetDocumentSaveOptions(out string filePathResult, out FileType fileTypeResult, out SaveConfigToken saveConfigTokenResult)
  1136. {
  1137. filePathResult = FilePath;
  1138. fileTypeResult = FileType;
  1139. if (_saveConfigToken == null)
  1140. {
  1141. saveConfigTokenResult = null;
  1142. }
  1143. else
  1144. {
  1145. saveConfigTokenResult = (SaveConfigToken)_saveConfigToken.Clone();
  1146. }
  1147. }
  1148. private bool _isScratchSurfaceBorrowed;
  1149. private string _borrowScratchSurfaceReason = string.Empty;
  1150. /// The scratch, stencil, accumulation, whatever buffer. This is used by many parts
  1151. /// of Paint.NET as a temporary area for which to store data.
  1152. /// This surface is 'owned' by any Tool that is active. If you want to use this you
  1153. /// must first deactivate the Tool using PushNullTool() and then reactivate it when
  1154. /// you are finished by calling PopNullTool().
  1155. /// Tools should use Tool.ScratchSurface instead of these API's.
  1156. public Surface BorrowScratchSurface(string reason)
  1157. {
  1158. if (_isScratchSurfaceBorrowed)
  1159. {
  1160. throw new InvalidOperationException(
  1161. "ScratchSurface already borrowed: '" +
  1162. _borrowScratchSurfaceReason +
  1163. "' (trying to borrow for: '" + reason + "')");
  1164. }
  1165. Tracing.Ping("Borrowing scratchSurface: " + reason);
  1166. _isScratchSurfaceBorrowed = true;
  1167. _borrowScratchSurfaceReason = reason;
  1168. return _scratchSurface;
  1169. }
  1170. public void ReturnScratchSurface(Surface borrowedScratchSurface)
  1171. {
  1172. if (!_isScratchSurfaceBorrowed)
  1173. {
  1174. throw new InvalidOperationException("ScratchSurface wasn't borrowed");
  1175. }
  1176. if (_scratchSurface != borrowedScratchSurface)
  1177. {
  1178. throw new InvalidOperationException("returned ScratchSurface doesn't match the real one");
  1179. }
  1180. Tracing.Ping("Returning scratchSurface: " + this._borrowScratchSurfaceReason);
  1181. _isScratchSurfaceBorrowed = false;
  1182. _borrowScratchSurfaceReason = string.Empty;
  1183. }
  1184. /// <summary>
  1185. /// Updates any pertinent EXIF tags, such as "Creation Software", to be
  1186. /// relevant or up-to-date.
  1187. /// </summary>
  1188. /// <param name="document"></param>
  1189. private static void UpdateExifTags(Document document)
  1190. {
  1191. // We want it to say "Creation Software: Paint.NET vX.Y"
  1192. // I have verified that other image editing software overwrites this tag,
  1193. // and does not just add it when it does not exist.
  1194. PropertyItem pi = Exif.CreateAscii(ExifTagID.Software, PdnInfo.GetProductName(false));
  1195. document.Metadata.ReplaceExifValues(ExifTagID.Software, new[] { pi });
  1196. }
  1197. private ZoomBasis _savedZb;
  1198. private ScaleFactor _savedSf;
  1199. private int _savedAli;
  1200. protected override void OnDocumentChanging(Document newDocument)
  1201. {
  1202. base.OnDocumentChanging(newDocument);
  1203. _savedZb = ZoomBasis;
  1204. _savedSf = ScaleFactor;
  1205. if (ActiveLayer != null)
  1206. {
  1207. _savedAli = ActiveLayerIndex;
  1208. }
  1209. else
  1210. {
  1211. _savedAli = -1;
  1212. }
  1213. if (newDocument != null)
  1214. {
  1215. UpdateExifTags(newDocument);
  1216. }
  1217. if (Document != null)
  1218. {
  1219. foreach (Layer layer in Document.Layers)
  1220. {
  1221. layer.PropertyChanging -= LayerPropertyChangingHandler;
  1222. layer.PropertyChanged -= LayerPropertyChangedHandler;
  1223. }
  1224. Document.Layers.RemovingAt -= LayerRemovingHandler;
  1225. Document.Layers.RemovedAt -= LayerRemovedHandler;
  1226. Document.Layers.Inserted -= LayerInsertedHandler;
  1227. }
  1228. _staticToolData.Clear();
  1229. PushNullTool(); // matching Pop is in OnDocumetChanged()
  1230. ActiveLayer = null;
  1231. if (_scratchSurface != null)
  1232. {
  1233. if (_isScratchSurfaceBorrowed)
  1234. {
  1235. throw new InvalidOperationException("scratchSurface is currently borrowed: " + _borrowScratchSurfaceReason);
  1236. }
  1237. if (newDocument == null || newDocument.Size != _scratchSurface.Size)
  1238. {
  1239. _scratchSurface.Dispose();
  1240. _scratchSurface = null;
  1241. }
  1242. }
  1243. if (!Selection.IsEmpty)
  1244. {
  1245. Selection.Reset();
  1246. }
  1247. }
  1248. protected override void OnDocumentChanged()
  1249. {
  1250. // if the ActiveLayer is not in this new document, then
  1251. // we try to set ActiveLayer to the first layer in this
  1252. // new document. But if the document contains no layers,
  1253. // or is null, we just null the ActiveLayer.
  1254. if (Document == null)
  1255. {
  1256. ActiveLayer = null;
  1257. }
  1258. else
  1259. {
  1260. if (Tool != null)
  1261. {
  1262. throw new InvalidOperationException("Tool was not deactivated while Document was being changed");
  1263. }
  1264. if (_scratchSurface != null)
  1265. {
  1266. if (_isScratchSurfaceBorrowed)
  1267. {
  1268. throw new InvalidOperationException("scratchSurface is currently borrowed: " + _borrowScratchSurfaceReason);
  1269. }
  1270. if (_scratchSurface.Size != Document.Size)
  1271. {
  1272. _scratchSurface.Dispose();
  1273. _scratchSurface = null;
  1274. }
  1275. }
  1276. _scratchSurface = new Surface(Document.Size);
  1277. Selection.ClipRectangle = Document.Bounds;
  1278. foreach (Layer layer in Document.Layers)
  1279. {
  1280. layer.PropertyChanging += LayerPropertyChangingHandler;
  1281. layer.PropertyChanged += LayerPropertyChangedHandler;
  1282. }
  1283. Document.Layers.RemovingAt += LayerRemovingHandler;
  1284. Document.Layers.RemovedAt += LayerRemovedHandler;
  1285. Document.Layers.Inserted += LayerInsertedHandler;
  1286. if (!Document.Layers.Contains(ActiveLayer))
  1287. {
  1288. if (Document.Layers.Count > 0)
  1289. {
  1290. if (_savedAli >= 0 && _savedAli < Document.Layers.Count)
  1291. {
  1292. ActiveLayer = (Layer)Document.Layers[_savedAli];
  1293. }

Large files files are truncated, but you can click here to view the full file