HistoryMemento.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Threading;
  3. namespace PaintDotNet.Measurement
  4. {
  5. /// <summary>
  6. /// A HistoryMemento is generally used to save part of the state of the Document
  7. /// so that an action that is yet to be performed can be undone at a later time.
  8. /// For example, if you are going to paint in a certain region, you first create a
  9. /// HistoryMemento that saves the contents of the area you are painting to. Then you
  10. /// paint. Then you push the history action on to the history stack.
  11. ///
  12. /// Using the HistoryMementoData class you can serialize your data to disk so that it
  13. /// doesn't fester in memory. There are important rules to follow here though:
  14. /// 1. Don't hold a reference to a Layer. Store a reference to the DocumentWorkspace and
  15. /// the layer's index instead, and access it via Workspace.Document.Layers[index].
  16. /// 2. The exception to #1 is if you are deleting a layer. But you should use
  17. /// DeleteLayerHistoryMemento for that. If you need to delete a layer as part of a
  18. /// compound action, use CompoundHistoryMemento in conjunction with
  19. /// DeleteLayerHistoryMemento.
  20. /// 3. To generalize, avoid serializing something unless you're replacing or deleting it.
  21. /// (and by 'serializing' I mean 'putting it in your HistoryMementoData class')
  22. /// It is better to hold a 'navigation reference' as opposed to a real reference.
  23. /// An example of a 'navigation reference' is listed in #1, where we don't store a ref
  24. /// to the layer itself but we store the information needed to navigate to it.
  25. /// The reasoning for this is made clear if you consider the following case. Assume you
  26. /// are holding on to a layer reference ("private Layer theLayer;"). Next, assume that
  27. /// the layer is deleted. Then the deletion is undone. The new layer in memory is not
  28. /// the layer you have a reference to even though they hold the same data. Changes made
  29. /// to one do not show up in the other one. Put another way, history actions should
  30. /// store large objects and their locations "by value," and not "by reference."
  31. /// </summary>
  32. public abstract class HistoryMemento
  33. {
  34. private string name;
  35. public string Name
  36. {
  37. get
  38. {
  39. return this.name;
  40. }
  41. set
  42. {
  43. this.name = value;
  44. }
  45. }
  46. private ImageResource image;
  47. public ImageResource Image
  48. {
  49. get
  50. {
  51. return this.image;
  52. }
  53. set
  54. {
  55. this.image = value;
  56. }
  57. }
  58. protected int id;
  59. private static int nextId = 0;
  60. public int ID
  61. {
  62. get
  63. {
  64. return this.id;
  65. }
  66. set
  67. {
  68. this.id = value;
  69. }
  70. }
  71. private Guid seriesGuid = Guid.Empty;
  72. public Guid SeriesGuid
  73. {
  74. get
  75. {
  76. return this.seriesGuid;
  77. }
  78. set
  79. {
  80. this.seriesGuid = value;
  81. }
  82. }
  83. private PersistedObject<HistoryMementoData> historyMementoData = null;
  84. /// <summary>
  85. /// Gets or sets the HistoryMementoData associated with this HistoryMemento.
  86. /// </summary>
  87. /// <remarks>
  88. /// Setting this property will immediately serialize the given object to disk.
  89. /// </remarks>
  90. protected HistoryMementoData Data
  91. {
  92. get
  93. {
  94. if (historyMementoData == null)
  95. {
  96. return null;
  97. }
  98. else
  99. {
  100. return (HistoryMementoData)historyMementoData.Object;
  101. }
  102. }
  103. set
  104. {
  105. this.historyMementoData = new PersistedObject<HistoryMementoData>(value, false);
  106. }
  107. }
  108. /// <summary>
  109. /// Ensures that the memory held by the Data property is serialized to disk and
  110. /// freed from memory.
  111. /// </summary>
  112. public void Flush()
  113. {
  114. if (historyMementoData != null)
  115. {
  116. historyMementoData.Flush();
  117. }
  118. OnFlush();
  119. }
  120. protected virtual void OnFlush()
  121. {
  122. }
  123. /// <summary>
  124. /// This will perform the necessary work required to undo an action.
  125. /// Note that the returned HistoryMemento should have the same ID.
  126. /// </summary>
  127. /// <returns>
  128. /// Returns a HistoryMemento that can be used to redo the action.
  129. /// Note that this property should hold: undoAction = undoAction.PerformUndo().PerformUndo()
  130. /// </returns>
  131. protected abstract HistoryMemento OnUndo();
  132. /// <summary>
  133. /// This method ensures that the returned HistoryMemento has the appropriate ID tag.
  134. /// </summary>
  135. /// <returns>Returns a HistoryMemento that can be used to redo the action.
  136. /// The ID of this HistoryMemento will be the same as the object that this
  137. /// method was called on.</returns>
  138. public HistoryMemento PerformUndo()
  139. {
  140. HistoryMemento ha = OnUndo();
  141. ha.ID = this.ID;
  142. ha.SeriesGuid = this.SeriesGuid;
  143. return ha;
  144. }
  145. public HistoryMemento(string name, ImageResource image)
  146. {
  147. SystemLayer.Tracing.LogFeature("HM(" + GetType().Name + ")");
  148. this.name = name;
  149. this.image = image;
  150. this.id = Interlocked.Increment(ref nextId);
  151. }
  152. }
  153. }