PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/DocumentWorkspace.cs

https://bitbucket.org/tcz001/openpdn
C# | 2944 lines | 2357 code | 451 blank | 136 comment | 287 complexity | 5edb570d764eb19ca36f67c71e1b848a MD5 | raw file
Possible License(s): Unlicense

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

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