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

/src/DocumentWorkspace.cs

https://bitbucket.org/tuldok89/openpdn
C# | 2824 lines | 2228 code | 451 blank | 145 comment | 276 complexity | 9c25685eadb589abf2a89f59d80d7308 MD5 | raw 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. }
  1294. else
  1295. {
  1296. ActiveLayer = (Layer)Document.Layers[0];
  1297. }
  1298. }
  1299. else
  1300. {
  1301. ActiveLayer = null;
  1302. }
  1303. }
  1304. // we invalidate each layer so that the layer previews refresh themselves
  1305. foreach (Layer layer in Document.Layers)
  1306. {
  1307. layer.Invalidate();
  1308. }
  1309. bool oldDirty = Document.Dirty;
  1310. Document.Invalidate();
  1311. Document.Dirty = oldDirty;
  1312. ZoomBasis = _savedZb;
  1313. if (_savedZb == ZoomBasis.ScaleFactor)
  1314. {
  1315. ScaleFactor = _savedSf;
  1316. }
  1317. }
  1318. PopNullTool();
  1319. AutoScrollPosition = new Point(0, 0);
  1320. base.OnDocumentChanged();
  1321. }
  1322. /// <summary>
  1323. /// Takes the current Document from this DocumentWorkspace instance and adds it to the MRU list.
  1324. /// </summary>
  1325. /// <param name="fileName"></param>
  1326. public void AddToMruList()
  1327. {
  1328. using (new PushNullToolMode(this))
  1329. {
  1330. string fullFileName = Path.GetFullPath(FilePath);
  1331. int edgeLength = AppWorkspace.MostRecentFiles.IconSize;
  1332. Surface thumb1 = RenderThumbnail(edgeLength, true, true);
  1333. // Put it inside a square bitmap
  1334. var thumb = new Surface(4 + edgeLength, 4 + edgeLength);
  1335. thumb.Clear(ColorBgra.Transparent);
  1336. var dstRect = new Rectangle((thumb.Width - thumb1.Width) / 2,
  1337. (thumb.Height - thumb1.Height) / 2, thumb1.Width, thumb1.Height);
  1338. thumb.CopySurface(thumb1, dstRect.Location);
  1339. using (var ra = new RenderArgs(thumb))
  1340. {
  1341. // Draw black border
  1342. var borderRect = new Rectangle(dstRect.Left - 1, dstRect.Top - 1, dstRect.Width + 2, dstRect.Height + 2);
  1343. --borderRect.Width;
  1344. --borderRect.Height;
  1345. ra.Graphics.DrawRectangle(Pens.Black, borderRect);
  1346. Rectangle shadowRect = Rectangle.Inflate(borderRect, 1, 1);
  1347. ++shadowRect.Width;
  1348. ++shadowRect.Height;
  1349. Utility.DrawDropShadow1Px(ra.Graphics, shadowRect);
  1350. thumb1.Dispose();
  1351. thumb1 = null;
  1352. var mrf = new MostRecentFile(fullFileName, Utility.FullCloneBitmap(ra.Bitmap));
  1353. if (AppWorkspace.MostRecentFiles.Contains(fullFileName))
  1354. {
  1355. AppWorkspace.MostRecentFiles.Remove(fullFileName);
  1356. }
  1357. AppWorkspace.MostRecentFiles.Add(mrf);
  1358. AppWorkspace.MostRecentFiles.SaveMruList();
  1359. }
  1360. }
  1361. }
  1362. /// <summary>
  1363. /// Shows an OpenFileDialog or SaveFileDialog and populates the InitialDirectory from the global
  1364. /// settings repository if possible.
  1365. /// </summary>
  1366. /// <param name="owner"></param>
  1367. /// <param name="fd">The FileDialog to show.</param>
  1368. /// <remarks>
  1369. /// The FileDialog should already have its InitialDirectory populated as a suggestion of where to start.
  1370. /// </remarks>
  1371. public static DialogResult ShowFileDialog(Control owner, IFileDialog fd)
  1372. {
  1373. string initialDirectory = Settings.CurrentUser.GetString(SettingNames.LastFileDialogDirectory, fd.InitialDirectory);
  1374. // TODO: spawn this in a background thread, if it doesn't respond within ~500ms?, assume the dir doesn't exist
  1375. bool dirExists = false;
  1376. try
  1377. {
  1378. var dirInfo = new DirectoryInfo(initialDirectory);
  1379. using (new WaitCursorChanger(owner))
  1380. {
  1381. dirExists = dirInfo.Exists;
  1382. if (!dirInfo.Exists)
  1383. {
  1384. initialDirectory = fd.InitialDirectory;
  1385. }
  1386. }
  1387. }
  1388. catch (Exception)
  1389. {
  1390. initialDirectory = fd.InitialDirectory;
  1391. }
  1392. fd.InitialDirectory = initialDirectory;
  1393. var ouc = new OurFileDialogUICallbacks();
  1394. DialogResult result = fd.ShowDialog(owner, ouc);
  1395. if (result == DialogResult.OK)
  1396. {
  1397. string fileName;
  1398. if (fd is IFileOpenDialog)
  1399. {
  1400. string[] fileNames = ((IFileOpenDialog)fd).FileNames;
  1401. fileName = fileNames.Length > 0 ? fileNames[0] : null;
  1402. }
  1403. else if (fd is IFileSaveDialog)
  1404. {
  1405. fileName = ((IFileSaveDialog)fd).FileName;
  1406. }
  1407. else
  1408. {
  1409. throw new InvalidOperationException();
  1410. }
  1411. if (fileName != null)
  1412. {
  1413. string newDir = Path.GetDirectoryName(fileName);
  1414. Settings.CurrentUser.SetString(SettingNames.LastFileDialogDirectory, newDir);
  1415. }
  1416. else
  1417. {
  1418. throw new FileNotFoundException();
  1419. }
  1420. }
  1421. return result;
  1422. }
  1423. private sealed class OurFileDialogUICallbacks
  1424. : IFileDialogUICallbacks
  1425. {
  1426. public FileOverwriteAction ShowOverwritePrompt(IWin32Window owner, string pathName)
  1427. {
  1428. FileOverwriteAction returnVal;
  1429. string title = PdnResources.GetString("SaveAs.OverwriteConfirmation.Title");
  1430. string textFormat = PdnResources.GetString("SaveAs.OverwriteConfirmation.Text.Format");
  1431. string fileName;
  1432. try
  1433. {
  1434. fileName = Path.GetFileName(pathName);
  1435. }
  1436. catch (Exception)
  1437. {
  1438. fileName = pathName;
  1439. }
  1440. string text = string.Format(textFormat, fileName);
  1441. DialogResult result = MessageBox.Show(owner, text, title, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2);
  1442. switch (result)
  1443. {
  1444. case DialogResult.Yes:
  1445. returnVal = FileOverwriteAction.Overwrite;
  1446. break;
  1447. case DialogResult.No:
  1448. returnVal = FileOverwriteAction.Cancel;
  1449. break;
  1450. default:
  1451. throw new InvalidEnumArgumentException();
  1452. }
  1453. return returnVal;
  1454. }
  1455. public bool ShowError(IWin32Window owner, string filePath, Exception ex)
  1456. {
  1457. if (!(ex is PathTooLongException))
  1458. {
  1459. return false;
  1460. }
  1461. string title = PdnInfo.GetBareProductName();
  1462. string message = PdnResources.GetString("FileDialog.PathTooLongException.Message");
  1463. MessageBox.Show(owner, message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
  1464. return true;
  1465. }
  1466. public IFileTransferProgressEvents CreateFileTransferProgressEvents()
  1467. {
  1468. return new OurProgressEvents();
  1469. }
  1470. }
  1471. private sealed class OurProgressEvents
  1472. : IFileTransferProgressEvents
  1473. {
  1474. private TransferProgressDialog _progressDialog;
  1475. private ICancelable _cancelSink;
  1476. private int _itemCount;
  1477. private int _itemOrdinal;
  1478. private string _itemName = string.Empty;
  1479. private long _totalWork;
  1480. private long _totalProgress;
  1481. private const int MaxPBValue = 200; // granularity of progress bar. 100 means 1%, 200 means 0.5%, etc.
  1482. private bool _cancelRequested;
  1483. private ManualResetEvent _operationEnded = new ManualResetEvent(false);
  1484. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "cancelSink")]
  1485. public void BeginOperation(IWin32Window owner, EventHandler callWhenUIShown, ICancelable cancelSink)
  1486. {
  1487. if (_progressDialog != null)
  1488. {
  1489. throw new InvalidOperationException("Operation already in progress");
  1490. }
  1491. _progressDialog = new TransferProgressDialog
  1492. {
  1493. Text =
  1494. PdnResources.GetString(
  1495. "DocumentWorkspace.ShowFileDialog.TransferProgress.Title"),
  1496. Icon =
  1497. Utility.ImageToIcon(
  1498. PdnResources.GetImageResource("Icons.MenuFileOpenIcon.png").Reference),
  1499. ItemText =
  1500. PdnResources.GetString(
  1501. "DocumentWorkspace.ShowFileDialog.ItemText.Initializing"),
  1502. ProgressBar =
  1503. {
  1504. Style = ProgressBarStyle.Marquee,
  1505. Maximum = MaxPBValue
  1506. }
  1507. };
  1508. _progressDialog.CancelClicked +=
  1509. delegate
  1510. {
  1511. _cancelRequested = true;
  1512. _cancelSink.RequestCancel();
  1513. UpdateUI();
  1514. };
  1515. EventHandler progressDialogShown =
  1516. (sender, e) => callWhenUIShown(this, EventArgs.Empty);
  1517. _cancelSink = cancelSink;
  1518. _itemOrdinal = 0;
  1519. _cancelRequested = false;
  1520. _itemName = string.Empty;
  1521. _itemCount = 0;
  1522. _itemOrdinal = 0;
  1523. _totalProgress = 0;
  1524. _totalWork = 0;
  1525. _progressDialog.Shown += progressDialogShown;
  1526. _progressDialog.ShowDialog(owner);
  1527. _progressDialog.Shown -= progressDialogShown;
  1528. _progressDialog.Dispose();
  1529. _progressDialog = null;
  1530. _cancelSink = null;
  1531. }
  1532. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "itemCount")]
  1533. public void SetItemCount(int itemCount)
  1534. {
  1535. if (_progressDialog.InvokeRequired)
  1536. {
  1537. _progressDialog.BeginInvoke(new Procedure<int>(SetItemCount), new object[] { itemCount });
  1538. }
  1539. else
  1540. {
  1541. _itemCount = itemCount;
  1542. UpdateUI();
  1543. }
  1544. }
  1545. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "itemOrdinal")]
  1546. public void SetItemOrdinal(int itemOrdinal)
  1547. {
  1548. if (_progressDialog.InvokeRequired)
  1549. {
  1550. _progressDialog.BeginInvoke(new Procedure<int>(SetItemOrdinal), new object[] { itemOrdinal });
  1551. }
  1552. else
  1553. {
  1554. _itemOrdinal = itemOrdinal;
  1555. _totalWork = 0;
  1556. _totalProgress = 0;
  1557. UpdateUI();
  1558. }
  1559. }
  1560. public void SetItemInfo(string itemInfo)
  1561. {
  1562. if (_progressDialog.InvokeRequired)
  1563. {
  1564. _progressDialog.BeginInvoke(new Procedure<string>(SetItemInfo), new object[] { itemInfo });
  1565. }
  1566. else
  1567. {
  1568. _itemName = itemInfo;
  1569. UpdateUI();
  1570. }
  1571. }
  1572. public void BeginItem()
  1573. {
  1574. if (_progressDialog.InvokeRequired)
  1575. {
  1576. _progressDialog.BeginInvoke(new Procedure(BeginItem), null);
  1577. }
  1578. else
  1579. {
  1580. _progressDialog.ProgressBar.Style = ProgressBarStyle.Continuous;
  1581. }
  1582. }
  1583. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "totalWork")]
  1584. public void SetItemWorkTotal(long totalWork)
  1585. {
  1586. if (_progressDialog.InvokeRequired)
  1587. {
  1588. _progressDialog.BeginInvoke(new Procedure<long>(SetItemWorkTotal), new object[] { totalWork });
  1589. }
  1590. else
  1591. {
  1592. _totalWork = totalWork;
  1593. UpdateUI();
  1594. }
  1595. }
  1596. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "totalProgress")]
  1597. public void SetItemWorkProgress(long totalProgress)
  1598. {
  1599. if (_progressDialog.InvokeRequired)
  1600. {
  1601. _progressDialog.BeginInvoke(new Procedure<long>(SetItemWorkProgress), new object[] { totalProgress });
  1602. }
  1603. else
  1604. {
  1605. _totalProgress = totalProgress;
  1606. UpdateUI();
  1607. }
  1608. }
  1609. public void EndItem(WorkItemResult result)
  1610. {
  1611. if (_progressDialog.InvokeRequired)
  1612. {
  1613. _progressDialog.BeginInvoke(new Procedure<WorkItemResult>(EndItem), new object[] { result });
  1614. }
  1615. }
  1616. public void EndOperation(OperationResult result)
  1617. {
  1618. if (_progressDialog.InvokeRequired)
  1619. {
  1620. _progressDialog.BeginInvoke(new Procedure<OperationResult>(EndOperation), new object[] { result });
  1621. }
  1622. else
  1623. {
  1624. _progressDialog.Close();
  1625. }
  1626. }
  1627. public WorkItemFailureAction ReportItemFailure(Exception ex)
  1628. {
  1629. if (_progressDialog.InvokeRequired)
  1630. {
  1631. object result = _progressDialog.Invoke(
  1632. new Function<WorkItemFailureAction, Exception>(ReportItemFailure),
  1633. new object[] { ex });
  1634. return (WorkItemFailureAction)result;
  1635. }
  1636. else
  1637. {
  1638. WorkItemFailureAction result = ShowFileTransferFailedDialog(ex);
  1639. return result;
  1640. }
  1641. }
  1642. private WorkItemFailureAction ShowFileTransferFailedDialog(Exception ex)
  1643. {
  1644. WorkItemFailureAction result;
  1645. Icon formIcon = _progressDialog.Icon;
  1646. string formTitle = PdnResources.GetString("DocumentWorkspace.ShowFileDialog.ItemFailureDialog.Title");
  1647. Image taskImage = PdnResources.GetImageResource("Icons.WarningIcon.png").Reference;
  1648. string introTextFormat = PdnResources.GetString("DocumentWorkspace.ShowFileDialog.ItemFailureDialog.IntroText.Format");
  1649. string introText = string.Format(introTextFormat, ex.Message);
  1650. var retryTB = new TaskButton(
  1651. PdnResources.GetImageResource("Icons.MenuImageRotate90CWIcon.png").Reference,
  1652. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.RetryTB.ActionText"),
  1653. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.RetryTB.ExplanationText"));
  1654. var skipTB = new TaskButton(
  1655. PdnResources.GetImageResource("Icons.HistoryFastForwardIcon.png").Reference,
  1656. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.SkipTB.ActionText"),
  1657. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.SkipTB.ExplanationText"));
  1658. var cancelTB = new TaskButton(
  1659. PdnResources.GetImageResource("Icons.CancelIcon.png").Reference,
  1660. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.CancelTB.ActionText"),
  1661. PdnResources.GetString("DocumentWorkspace.ShowFileDialog.CancelTB.ExplanationText"));
  1662. var taskButtons = new List<TaskButton> {retryTB};
  1663. // Only have the Skip button if there is more than 1 item being transferred.
  1664. // If only 1 item is begin transferred, Skip and Cancel are essentially synonymous.
  1665. if (_itemCount > 1)
  1666. {
  1667. taskButtons.Add(skipTB);
  1668. }
  1669. taskButtons.Add(cancelTB);
  1670. int width96 = (TaskDialog.DefaultPixelWidth96Dpi * 4) / 3; // 33% wider
  1671. TaskButton clickedTB = TaskDialog.Show(
  1672. _progressDialog,
  1673. formIcon,
  1674. formTitle,
  1675. taskImage,
  1676. true,
  1677. introText,
  1678. taskButtons.ToArray(),
  1679. retryTB,
  1680. cancelTB,
  1681. width96);
  1682. if (clickedTB == retryTB)
  1683. {
  1684. result = WorkItemFailureAction.RetryItem;
  1685. }
  1686. else if (clickedTB == skipTB)
  1687. {
  1688. result = WorkItemFailureAction.SkipItem;
  1689. }
  1690. else
  1691. {
  1692. result = WorkItemFailureAction.CancelOperation;
  1693. }
  1694. return result;
  1695. }
  1696. private void UpdateUI()
  1697. {
  1698. int itemCount2 = Math.Max(1, _itemCount);
  1699. double startValue = (double)_itemOrdinal / (double)itemCount2;
  1700. double endValue = (double)(_itemOrdinal + 1) / (double)itemCount2;
  1701. long totalWork2 = Math.Max(1, _totalWork);
  1702. double lerp = (double)_totalProgress / (double)totalWork2;
  1703. double newValue = Utility.Lerp(startValue, endValue, lerp);
  1704. var newValueInt = (int)Math.Ceiling(MaxPBValue * newValue);
  1705. if (_cancelRequested)
  1706. {
  1707. _progressDialog.CancelEnabled = false;
  1708. _progressDialog.ItemText = PdnResources.GetString("DocumentWorkspace.ShowFileDialog.ItemText.Canceling");
  1709. _progressDialog.OperationProgress = string.Empty;
  1710. _progressDialog.ProgressBar.Style = ProgressBarStyle.Marquee;
  1711. }
  1712. else
  1713. {
  1714. _progressDialog.CancelEnabled = true;
  1715. _progressDialog.ItemText = _itemName;
  1716. string progressFormat = PdnResources.GetString("DocumentWorkspace.ShowFileDialog.ProgressText.Format");
  1717. string progressText = string.Format(progressFormat, _itemOrdinal + 1, _itemCount);
  1718. _progressDialog.OperationProgress = progressText;
  1719. _progressDialog.ProgressBar.Style = ProgressBarStyle.Continuous;
  1720. _progressDialog.ProgressBar.Value = newValueInt;
  1721. }
  1722. }
  1723. }
  1724. public static DialogResult ChooseFile(Control parent, out string fileName)
  1725. {
  1726. return ChooseFile(parent, out fileName, null);
  1727. }
  1728. public static DialogResult ChooseFile(Control parent, out string fileName, string startingDir)
  1729. {
  1730. string[] fileNames;
  1731. DialogResult result = ChooseFiles(parent, out fileNames, false, startingDir);
  1732. fileName = result == DialogResult.OK ? fileNames[0] : null;
  1733. return result;
  1734. }
  1735. public static DialogResult ChooseFiles(Control owner, out string[] fileNames, bool multiselect)
  1736. {
  1737. return ChooseFiles(owner, out fileNames, multiselect, null);
  1738. }
  1739. public static DialogResult ChooseFiles(Control owner, out string[] fileNames, bool multiselect, string startingDir)
  1740. {
  1741. FileTypeCollection fileTypes = FileTypes.GetFileTypes();
  1742. using (IFileOpenDialog ofd = CommonDialogs.CreateFileOpenDialog())
  1743. {
  1744. ofd.InitialDirectory = startingDir ?? GetDefaultSavePath();
  1745. ofd.CheckFileExists = true;
  1746. ofd.CheckPathExists = true;
  1747. ofd.Multiselect = multiselect;
  1748. ofd.Filter = fileTypes.ToString(true, PdnResources.GetString("FileDialog.Types.AllImages"), false, true);
  1749. ofd.FilterIndex = 0;
  1750. DialogResult result = ShowFileDialog(owner, ofd);
  1751. fileNames = result == DialogResult.OK ? ofd.FileNames : new string[0];
  1752. return result;
  1753. }
  1754. }
  1755. /// <summary>
  1756. /// Use this to get a save config token. You should already know the filename and file type.
  1757. /// An existing save config token is optional and will be used to pre-populate the config dialog.
  1758. /// </summary>
  1759. /// <param name="fileType"></param>
  1760. /// <param name="saveConfigToken"></param>
  1761. /// <param name="currentSaveConfigToken"></param>
  1762. /// <param name="newSaveConfigToken"></param>
  1763. /// <param name="currentFileType"></param>
  1764. /// <param name="saveScratchSurface"></param>
  1765. /// <returns>false if the user cancelled, otherwise true</returns>
  1766. private bool GetSaveConfigToken(
  1767. FileType currentFileType,
  1768. SaveConfigToken currentSaveConfigToken,
  1769. out SaveConfigToken newSaveConfigToken,
  1770. Surface saveScratchSurface)
  1771. {
  1772. if (currentFileType.SupportsConfiguration)
  1773. {
  1774. using (var scd = new SaveConfigDialog())
  1775. {
  1776. scd.ScratchSurface = saveScratchSurface;
  1777. ProgressEventHandler peh = delegate(object sender, ProgressEventArgs e)
  1778. {
  1779. if (e.Percent < 0 || e.Percent >= 100)
  1780. {
  1781. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  1782. AppWorkspace.Widgets.StatusBarProgress.EraseProgressStatusBar();
  1783. }
  1784. else
  1785. {
  1786. AppWorkspace.Widgets.StatusBarProgress.SetProgressStatusBar(e.Percent);
  1787. }
  1788. };
  1789. //if (currentFileType.SavesWithProgress)
  1790. {
  1791. scd.Progress += peh;
  1792. }
  1793. scd.Document = Document;
  1794. scd.FileType = currentFileType;
  1795. SaveConfigToken token = currentFileType.GetLastSaveConfigToken();
  1796. if (currentSaveConfigToken != null &&
  1797. token.GetType() == currentSaveConfigToken.GetType())
  1798. {
  1799. scd.SaveConfigToken = currentSaveConfigToken;
  1800. }
  1801. scd.EnableInstanceOpacity = false;
  1802. // show configuration/preview dialog
  1803. DialogResult dr = scd.ShowDialog(this);
  1804. //if (currentFileType.SavesWithProgress)
  1805. {
  1806. scd.Progress -= peh;
  1807. AppWorkspace.Widgets.StatusBarProgress.ResetProgressStatusBar();
  1808. AppWorkspace.Widgets.StatusBarProgress.EraseProgressStatusBar();
  1809. }
  1810. if (dr == DialogResult.OK)
  1811. {
  1812. newSaveConfigToken = scd.SaveConfigToken;
  1813. return true;
  1814. }
  1815. newSaveConfigToken = null;
  1816. return false;
  1817. }
  1818. }
  1819. newSaveConfigToken = currentFileType.GetLastSaveConfigToken();
  1820. return true;
  1821. }
  1822. /// <summary>
  1823. /// Used to set the file name, file type, and save config token
  1824. /// </summary>
  1825. /// <param name="newFileName"></param>
  1826. /// <param name="newFileType"></param>
  1827. /// <param name="newSaveConfigToken"></param>
  1828. /// <param name="saveScratchSurface"></param>
  1829. /// <returns>true if the user clicked through and accepted, or false if they cancelled at any point</returns>
  1830. private bool DoSaveAsDialog(
  1831. out string newFileName,
  1832. out FileType newFileType,
  1833. out SaveConfigToken newSaveConfigToken,
  1834. Surface saveScratchSurface)
  1835. {
  1836. FileTypeCollection fileTypes = FileTypes.GetFileTypes();
  1837. using (IFileSaveDialog sfd = CommonDialogs.CreateFileSaveDialog())
  1838. {
  1839. sfd.AddExtension = true;
  1840. sfd.CheckPathExists = true;
  1841. sfd.OverwritePrompt = true;
  1842. string filter = fileTypes.ToString(false, null, true, false);
  1843. sfd.Filter = filter;
  1844. string localFileName;
  1845. FileType localFileType;
  1846. SaveConfigToken localSaveConfigToken;
  1847. GetDocumentSaveOptions(out localFileName, out localFileType, out localSaveConfigToken);
  1848. if (Document.Layers.Count > 1 &&
  1849. localFileType != null &&
  1850. !localFileType.SupportsLayers)
  1851. {
  1852. localFileType = null;
  1853. }
  1854. if (localFileType == null)
  1855. {
  1856. localFileType = Document.Layers.Count == 1 ? PdnFileTypes.Png : PdnFileTypes.Pdn;
  1857. localFileName = Path.ChangeExtension(localFileName, localFileType.DefaultExtension);
  1858. }
  1859. if (localFileName == null)
  1860. {
  1861. string name = GetDefaultSaveName();
  1862. string newName = Path.ChangeExtension(name, localFileType.DefaultExtension);
  1863. localFileName = Path.Combine(GetDefaultSavePath(), newName);
  1864. }
  1865. // If the filename is only an extension (i.e. ".lmnop") then we must treat it specially
  1866. string fileNameOnly = Path.GetFileName(localFileName);
  1867. if (fileNameOnly != null)
  1868. if (fileNameOnly.Length >= 1 && fileNameOnly[0] == '.')
  1869. {
  1870. sfd.FileName = localFileName;
  1871. }
  1872. else
  1873. {
  1874. sfd.FileName = Path.ChangeExtension(localFileName, null);
  1875. }
  1876. sfd.FilterIndex = 1 + fileTypes.IndexOfFileType(localFileType);
  1877. sfd.InitialDirectory = Path.GetDirectoryName(localFileName);
  1878. sfd.Title = PdnResources.GetString("SaveAsDialog.Title");
  1879. DialogResult dr1 = ShowFileDialog(this, sfd);
  1880. bool result;
  1881. if (dr1 != DialogResult.OK)
  1882. {
  1883. result = false;
  1884. }
  1885. else
  1886. {
  1887. localFileName = sfd.FileName;
  1888. FileType fileType2 = fileTypes[sfd.FilterIndex - 1];
  1889. result = GetSaveConfigToken(fileType2, localSaveConfigToken, out localSaveConfigToken, saveScratchSurface);
  1890. localFileType = fileType2;
  1891. }
  1892. if (result)
  1893. {
  1894. newFileName = localFileName;
  1895. newFileType = localFileType;
  1896. newSaveConfigToken = localSaveConfigToken;
  1897. }
  1898. else
  1899. {
  1900. newFileName = null;
  1901. newFileType = null;
  1902. newSaveConfigToken = null;
  1903. }
  1904. return result;
  1905. }
  1906. }
  1907. /// <summary>
  1908. /// Warns the user that we need to flatten the image.
  1909. /// </summary>
  1910. /// <returns>Returns DialogResult.Yes if they want to proceed or DialogResult.No if they don't.</returns>
  1911. private DialogResult WarnAboutFlattening()
  1912. {
  1913. Icon formIcon = Utility.ImageToIcon(PdnResources.GetImageResource("Icons.MenuFileSaveIcon.png").Reference);
  1914. string formTitle = PdnResources.GetString("WarnAboutFlattening.Title");
  1915. string introText = PdnResources.GetString("WarnAboutFlattening.IntroText");
  1916. Image taskImage = null;
  1917. var flattenTB = new TaskButton(
  1918. PdnResources.GetImageResource("Icons.MenuImageFlattenIcon.png").Reference,
  1919. PdnResources.GetString("WarnAboutFlattening.FlattenTB.ActionText"),
  1920. PdnResources.GetString("WarnAboutFlattening.FlattenTB.ExplanationText"));
  1921. var cancelTB = new TaskButton(
  1922. TaskButton.Cancel.Image,
  1923. PdnResources.GetString("WarnAboutFlattening.CancelTB.ActionText"),
  1924. PdnResources.GetString("WarnAboutFlattening.CancelTB.ExplanationText"));
  1925. TaskButton clickedTB = TaskDialog.Show(
  1926. AppWorkspace,
  1927. formIcon,
  1928. formTitle,
  1929. taskImage,
  1930. true,
  1931. introText,
  1932. new[] { flattenTB, cancelTB },
  1933. flattenTB,
  1934. cancelTB,
  1935. (TaskDialog.DefaultPixelWidth96Dpi * 5) / 4);
  1936. return clickedTB == flattenTB ? DialogResult.Yes : DialogResult.No;
  1937. }
  1938. private static string GetDefaultSaveName()
  1939. {
  1940. return PdnResources.GetString("Untitled.FriendlyName");
  1941. }
  1942. private static string GetDefaultSavePath()
  1943. {
  1944. string myPics;
  1945. try
  1946. {
  1947. myPics = Shell.GetVirtualPath(VirtualFolderName.UserPictures, false);
  1948. var dirInfo = new DirectoryInfo(myPics); // validate
  1949. }
  1950. catch (Exception)
  1951. {
  1952. myPics = "";
  1953. }
  1954. string dir = Settings.CurrentUser.GetString(SettingNames.LastFileDialogDirectory, null);
  1955. if (dir == null)
  1956. {
  1957. dir = myPics;
  1958. }
  1959. else
  1960. {
  1961. try
  1962. {
  1963. var dirInfo = new DirectoryInfo(dir);
  1964. if (!dirInfo.Exists)
  1965. {
  1966. dir = myPics;
  1967. }
  1968. }
  1969. catch (Exception)
  1970. {
  1971. dir = myPics;
  1972. }
  1973. }
  1974. return dir;
  1975. }
  1976. public bool DoSave()
  1977. {
  1978. return DoSave(false);
  1979. }
  1980. /// <summary>
  1981. /// Does the dirty work for a File->Save operation. If any of the "Save Options" in the
  1982. /// DocumentWorkspace are null, this will call DoSaveAs(). If the image has more than 1
  1983. /// layer but the file type they want to save with does not support layers, then it will
  1984. /// ask the user about flattening the image.
  1985. /// </summary>
  1986. /// <param name="tryToFlatten">
  1987. /// If true, will ask the user about flattening if the workspace's saveFileType does not
  1988. /// support layers and the image has more than 1 layer.
  1989. /// If false, then DoSaveAs will be called and the fileType will be prepopulated with
  1990. /// the .PDN type.
  1991. /// </param>
  1992. /// <returns><b>true</b> if the file was saved, <b>false</b> if the user cancelled</returns>
  1993. protected bool DoSave(bool tryToFlatten)
  1994. {
  1995. using (new PushNullToolMode(this))
  1996. {
  1997. string newFileName;
  1998. FileType newFileType;
  1999. SaveConfigToken newSaveConfigToken;
  2000. GetDocumentSaveOptions(out newFileName, out newFileType, out newSaveConfigToken);
  2001. // if they haven't specified a filename, then revert to "Save As" behavior
  2002. if (newFileName == null)
  2003. {
  2004. return DoSaveAs();
  2005. }
  2006. // if we have a filename but no file type, try to infer the file type
  2007. if (newFileType == null)
  2008. {
  2009. FileTypeCollection fileTypes = FileTypes.GetFileTypes();
  2010. string ext = Path.GetExtension(newFileName);
  2011. int index = fileTypes.IndexOfExtension(ext);
  2012. FileType inferredFileType = fileTypes[index];
  2013. newFileType = inferredFileType;
  2014. }
  2015. // if the image has more than 1 layer but is saving with a file type that
  2016. // does not support layers, then we must ask them if we may flatten the
  2017. // image first
  2018. if (Document.Layers.Count > 1 && !newFileType.SupportsLayers)
  2019. {
  2020. if (!tryToFlatten)
  2021. {
  2022. return DoSaveAs();
  2023. }
  2024. DialogResult dr = WarnAboutFlattening();
  2025. if (dr == DialogResult.Yes)
  2026. {
  2027. ExecuteFunction(new FlattenFunction());
  2028. }
  2029. else
  2030. {
  2031. return false;
  2032. }
  2033. }
  2034. // get the configuration!
  2035. if (newSaveConfigToken == null)
  2036. {
  2037. Surface scratch = BorrowScratchSurface(GetType().Name + ".DoSave() calling GetSaveConfigToken()");
  2038. bool result;
  2039. try
  2040. {
  2041. result = GetSaveConfigToken(newFileType, newSaveConfigToken, out newSaveConfigToken, scratch);
  2042. }
  2043. finally
  2044. {
  2045. ReturnScratchSurface(scratch);
  2046. }
  2047. if (!result)
  2048. {
  2049. return false;
  2050. }
  2051. }
  2052. // At this point fileName, fileType, and saveConfigToken must all be non-null
  2053. // if the document supports custom headers, embed a thumbnail in there
  2054. if (newFileType.SupportsCustomHeaders)
  2055. {
  2056. using (new WaitCursorChanger(this))
  2057. {
  2058. Utility.GCFullCollect();
  2059. const int maxDim = 256;
  2060. Surface thumb;
  2061. Surface flattened = BorrowScratchSurface(GetType().Name + ".DoSave() preparing embedded thumbnail");
  2062. try
  2063. {
  2064. Document.Flatten(flattened);
  2065. if (Document.Width > maxDim || Document.Height > maxDim)
  2066. {
  2067. int width;
  2068. int height;
  2069. if (Document.Width > Document.Height)
  2070. {
  2071. width = maxDim;
  2072. height = (Document.Height * maxDim) / Document.Width;
  2073. }
  2074. else
  2075. {
  2076. height = maxDim;
  2077. width = (Document.Width * maxDim) / Document.Height;
  2078. }
  2079. int thumbWidth = Math.Max(1, width);
  2080. int thumbHeight = Math.Max(1, height);
  2081. thumb = new Surface(thumbWidth, thumbHeight);
  2082. thumb.SuperSamplingFitSurface(flattened);
  2083. }
  2084. else
  2085. {
  2086. thumb = new Surface(flattened.Size);
  2087. thumb.CopySurface(flattened);
  2088. }
  2089. }
  2090. finally
  2091. {
  2092. ReturnScratchSurface(flattened);
  2093. }
  2094. var thumbDoc = new Document(thumb.Width, thumb.Height);
  2095. var thumbLayer = new BitmapLayer(thumb);
  2096. var backLayer = new BitmapLayer(thumb.Width, thumb.Height);
  2097. backLayer.Surface.Clear(ColorBgra.Transparent);
  2098. thumb.Dispose();
  2099. thumbDoc.Layers.Add(backLayer);
  2100. thumbDoc.Layers.Add(thumbLayer);
  2101. var thumbPng = new MemoryStream();
  2102. PropertyBasedSaveConfigToken pngToken = PdnFileTypes.Png.CreateDefaultSaveConfigToken();
  2103. PdnFileTypes.Png.Save(thumbDoc, thumbPng, pngToken, null, null, false);
  2104. byte[] thumbBytes = thumbPng.ToArray();
  2105. string thumbString = Convert.ToBase64String(thumbBytes, Base64FormattingOptions.None);
  2106. thumbDoc.Dispose();
  2107. string thumbXml = "<thumb png=\"" + thumbString + "\" />";
  2108. Document.CustomHeaders = thumbXml;
  2109. }
  2110. }
  2111. // save!
  2112. bool success = false;
  2113. Stream stream = null;
  2114. try
  2115. {
  2116. stream = new FileStream(newFileName, FileMode.Create, FileAccess.Write);
  2117. using (new WaitCursorChanger(this))
  2118. {
  2119. Utility.GCFullCollect();
  2120. var sd = new SaveProgressDialog(this);
  2121. Surface scratch = BorrowScratchSurface(GetType().Name + ".DoSave() handing off scratch surface to SaveProgressDialog.Save()");
  2122. try
  2123. {
  2124. sd.Save(stream, Document, newFileType, newSaveConfigToken, scratch);
  2125. }
  2126. finally
  2127. {
  2128. ReturnScratchSurface(scratch);
  2129. }
  2130. success = true;
  2131. _lastSaveTime = DateTime.Now;
  2132. stream.Close();
  2133. stream = null;
  2134. }
  2135. }
  2136. catch (UnauthorizedAccessException)
  2137. {
  2138. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.UnauthorizedAccessException"));
  2139. }
  2140. catch (SecurityException)
  2141. {
  2142. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.SecurityException"));
  2143. }
  2144. catch (DirectoryNotFoundException)
  2145. {
  2146. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.DirectoryNotFoundException"));
  2147. }
  2148. catch (IOException)
  2149. {
  2150. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.IOException"));
  2151. }
  2152. catch (OutOfMemoryException)
  2153. {
  2154. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.OutOfMemoryException"));
  2155. }
  2156. #if !DEBUG
  2157. catch (Exception)
  2158. {
  2159. Utility.ErrorBox(this, PdnResources.GetString("SaveImage.Error.Exception"));
  2160. }
  2161. #endif
  2162. finally
  2163. {
  2164. if (stream != null)
  2165. {
  2166. stream.Close();
  2167. stream = null;
  2168. }
  2169. }
  2170. if (success)
  2171. {
  2172. Shell.AddToRecentDocumentsList(newFileName);
  2173. }
  2174. else
  2175. {
  2176. return false;
  2177. }
  2178. // reset the dirty bit so they won't be asked to save on quitting
  2179. Document.Dirty = false;
  2180. // some misc. book keeping ...
  2181. AddToMruList();
  2182. // and finally, shout happiness by way of ...
  2183. return true;
  2184. }
  2185. }
  2186. /// <summary>
  2187. /// Does the grunt work to do a File->Save As operation.
  2188. /// </summary>
  2189. /// <returns><b>true</b> if the file was saved correctly, <b>false</b> if the user cancelled</returns>
  2190. public bool DoSaveAs()
  2191. {
  2192. using (new PushNullToolMode(this))
  2193. {
  2194. string newFileName;
  2195. FileType newFileType;
  2196. SaveConfigToken newSaveConfigToken;
  2197. Surface scratch = BorrowScratchSurface(GetType() + ".DoSaveAs() handing off scratch surface to DoSaveAsDialog()");
  2198. bool result;
  2199. try
  2200. {
  2201. result = DoSaveAsDialog(out newFileName, out newFileType, out newSaveConfigToken, scratch);
  2202. }
  2203. finally
  2204. {
  2205. ReturnScratchSurface(scratch);
  2206. }
  2207. if (!result)
  2208. {
  2209. return false;
  2210. }
  2211. string oldFileName;
  2212. FileType oldFileType;
  2213. SaveConfigToken oldSaveConfigToken;
  2214. GetDocumentSaveOptions(out oldFileName, out oldFileType, out oldSaveConfigToken);
  2215. SetDocumentSaveOptions(newFileName, newFileType, newSaveConfigToken);
  2216. bool result2 = DoSave(true);
  2217. if (!result2)
  2218. {
  2219. SetDocumentSaveOptions(oldFileName, oldFileType, oldSaveConfigToken);
  2220. }
  2221. return result2;
  2222. }
  2223. }
  2224. public static Document LoadDocument(Control owner, string fileName, out FileType fileTypeResult, ProgressEventHandler progressCallback)
  2225. {
  2226. FileTypeCollection fileTypes;
  2227. FileType fileType;
  2228. fileTypeResult = null;
  2229. try
  2230. {
  2231. fileTypes = FileTypes.GetFileTypes();
  2232. int ftIndex = fileTypes.IndexOfExtension(Path.GetExtension(fileName));
  2233. if (ftIndex == -1)
  2234. {
  2235. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.ImageTypeNotRecognized"));
  2236. return null;
  2237. }
  2238. fileType = fileTypes[ftIndex];
  2239. fileTypeResult = fileType;
  2240. }
  2241. catch (ArgumentException)
  2242. {
  2243. string format = PdnResources.GetString("LoadImage.Error.InvalidFileName.Format");
  2244. string error = string.Format(format, fileName);
  2245. Utility.ErrorBox(owner, error);
  2246. return null;
  2247. }
  2248. Document document = null;
  2249. using (new WaitCursorChanger(owner))
  2250. {
  2251. Utility.GCFullCollect();
  2252. Stream stream = null;
  2253. try
  2254. {
  2255. try
  2256. {
  2257. stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
  2258. long totalBytes = 0;
  2259. var siphonStream = new SiphonStream(stream);
  2260. IOEventHandler ioEventHandler = delegate(object sender, IOEventArgs e)
  2261. {
  2262. if (progressCallback == null)
  2263. {
  2264. }
  2265. else
  2266. {
  2267. totalBytes += e.Count;
  2268. double percent =
  2269. Utility.Clamp(100.0*((double) totalBytes/(double) siphonStream.Length), 0, 100);
  2270. progressCallback(null, new ProgressEventArgs(percent));
  2271. }
  2272. };
  2273. siphonStream.IOFinished += ioEventHandler;
  2274. using (new WaitCursorChanger(owner))
  2275. {
  2276. document = fileType.Load(siphonStream);
  2277. if (progressCallback != null)
  2278. {
  2279. progressCallback(null, new ProgressEventArgs(100.0));
  2280. }
  2281. }
  2282. siphonStream.IOFinished -= ioEventHandler;
  2283. siphonStream.Close();
  2284. }
  2285. catch (WorkerThreadException ex)
  2286. {
  2287. Type innerExType = ex.InnerException.GetType();
  2288. ConstructorInfo ci = innerExType.GetConstructor(new[] { typeof(string), typeof(Exception) });
  2289. if (ci == null)
  2290. {
  2291. throw;
  2292. }
  2293. else
  2294. {
  2295. var ex2 = (Exception)ci.Invoke(new object[] { "Worker thread threw an exception of this type", ex.InnerException });
  2296. throw ex2;
  2297. }
  2298. }
  2299. }
  2300. catch (ArgumentException)
  2301. {
  2302. Utility.ErrorBox(owner,
  2303. fileName.Length == 0
  2304. ? PdnResources.GetString("LoadImage.Error.BlankFileName")
  2305. : PdnResources.GetString("LoadImage.Error.ArgumentException"));
  2306. }
  2307. catch (UnauthorizedAccessException)
  2308. {
  2309. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.UnauthorizedAccessException"));
  2310. }
  2311. catch (SecurityException)
  2312. {
  2313. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.SecurityException"));
  2314. }
  2315. catch (FileNotFoundException)
  2316. {
  2317. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.FileNotFoundException"));
  2318. }
  2319. catch (DirectoryNotFoundException)
  2320. {
  2321. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.DirectoryNotFoundException"));
  2322. }
  2323. catch (PathTooLongException)
  2324. {
  2325. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.PathTooLongException"));
  2326. }
  2327. catch (IOException)
  2328. {
  2329. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.IOException"));
  2330. }
  2331. catch (SerializationException)
  2332. {
  2333. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.SerializationException"));
  2334. }
  2335. catch (OutOfMemoryException)
  2336. {
  2337. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.OutOfMemoryException"));
  2338. }
  2339. catch (Exception)
  2340. {
  2341. Utility.ErrorBox(owner, PdnResources.GetString("LoadImage.Error.Exception"));
  2342. }
  2343. finally
  2344. {
  2345. if (stream != null)
  2346. {
  2347. stream.Close();
  2348. stream = null;
  2349. }
  2350. }
  2351. }
  2352. return document;
  2353. }
  2354. public Surface RenderThumbnail(int maxEdgeLength, bool highQuality, bool forceUpToDate)
  2355. {
  2356. if (Document == null)
  2357. {
  2358. var ret = new Surface(maxEdgeLength, maxEdgeLength);
  2359. ret.Clear(ColorBgra.Transparent);
  2360. return ret;
  2361. }
  2362. Size thumbSize = Utility.ComputeThumbnailSize(Document.Size, maxEdgeLength);
  2363. var thumb = new Surface(thumbSize);
  2364. thumb.Clear(ColorBgra.Transparent);
  2365. RenderCompositionTo(thumb, highQuality, forceUpToDate);
  2366. return thumb;
  2367. }
  2368. Surface IThumbnailProvider.RenderThumbnail(int maxEdgeLength)
  2369. {
  2370. return RenderThumbnail(maxEdgeLength, true, false);
  2371. }
  2372. }
  2373. }