fbf082e93164488d31f1dfd8b6e2dfdb117cb759
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Model / EditingScope.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4
5 namespace System.Activities.Presentation.Model
6 {
7     using System;
8     using System.Activities.Presentation.Validation;
9     using System.Collections.Generic;
10     using System.Collections.ObjectModel;
11     using System.Linq;
12     using System.Runtime;
13     using System.Text;
14
15     [Fx.Tag.XamlVisible(false)]
16     public class EditingScope : ModelEditingScope
17     {
18         ModelTreeManager modelTreeManager;
19         EditingScope outerScope;
20         List<Change> changes;
21         List<Change> appliedChanges;
22         bool suppressUndo;
23         private HashSet<ModelItem> itemsAdded;
24         private HashSet<ModelItem> itemsRemoved;
25
26         // this is intended for ImmediateEditingScope only
27         private bool? hasEffectiveChanges;
28
29         internal EditingScope(ModelTreeManager modelTreeManager, EditingScope outerScope)
30         {
31             this.modelTreeManager = modelTreeManager;
32             this.changes = new List<Change>();
33             this.outerScope = outerScope;
34             this.HasModelChanges = false;
35             this.itemsAdded = new HashSet<ModelItem>();
36             this.itemsRemoved = new HashSet<ModelItem>();
37         }
38
39         private EditingScope()
40         {
41         }
42
43         public bool HasEffectiveChanges
44         {
45             get
46             {
47                 if (this.hasEffectiveChanges.HasValue)
48                 {
49                     Fx.Assert(this.GetType() == typeof(ImmediateEditingScope), "we should only set this property on an ImmediateEditingScope");
50
51                     return this.hasEffectiveChanges.Value;
52                 }
53
54                 return this.appliedChanges != null && this.appliedChanges.Count > 0;
55             }
56
57             internal set
58             {
59                 Fx.Assert(this.GetType() == typeof(ImmediateEditingScope), "we should only set this property on an ImmediateEditingScope");
60
61                 this.hasEffectiveChanges = value;
62             }
63         }
64
65         internal bool HasModelChanges
66         {
67             get;
68
69             // setter is only expected to be called by EditingScope and ImmediateEditingScope
70             set;
71         }
72
73         internal bool SuppressUndo
74         {
75             get
76             {
77                 return this.suppressUndo;
78             }
79             set
80             {
81                 Fx.Assert(!value || this.outerScope == null, "If we are suppressing undo, then we are not nested within another editingScope, otherwise suppress undo won't work.");
82                 this.suppressUndo = value;
83             }
84         }
85
86         internal ReadOnlyCollection<ModelItem> ItemsAdded
87         {
88             get
89             {
90                 return new ReadOnlyCollection<ModelItem>(this.itemsAdded.ToList());
91             }
92         }
93
94         internal ReadOnlyCollection<ModelItem> ItemsRemoved
95         {
96             get
97             {
98                 return new ReadOnlyCollection<ModelItem>(this.itemsRemoved.ToList());
99             }
100         }
101
102         /// <summary>
103         /// Gets or sets whether validation should be suppressed when this EditingScope completes.
104         /// </summary>
105         internal bool SuppressValidationOnComplete
106         {
107             get;
108             set;
109         }
110
111         public List<Change> Changes
112         {
113             get
114             {
115                 return changes;
116             }
117         }
118
119         internal void EditingScope_ModelItemsAdded(object sender, ModelItemsAddedEventArgs e)
120         {
121             this.HandleModelItemsAdded(e.ModelItemsAdded);
122         }
123
124         internal void EditingScope_ModelItemsRemoved(object sender, ModelItemsRemovedEventArgs e)
125         {
126             this.HandleModelItemsRemoved(e.ModelItemsRemoved);
127         }
128
129         internal void HandleModelItemsAdded(IEnumerable<ModelItem> modelItems)
130         {
131             foreach (ModelItem addedItem in modelItems)
132             {
133                 if (this.itemsRemoved.Contains(addedItem))
134                 {
135                     this.itemsRemoved.Remove(addedItem);
136                 }
137                 else
138                 {
139                     Fx.Assert(!this.itemsAdded.Contains(addedItem), "One ModelItem should not be added more than once.");
140                     this.itemsAdded.Add(addedItem);
141                 }
142             }
143         }
144
145         internal void HandleModelItemsRemoved(IEnumerable<ModelItem> modelItems)
146         {
147             foreach (ModelItem removedItem in modelItems)
148             {
149                 if (this.itemsAdded.Contains(removedItem))
150                 {
151                     this.itemsAdded.Remove(removedItem);
152                 }
153                 else
154                 {
155                     Fx.Assert(!itemsRemoved.Contains(removedItem), "One ModelItem should not be removed more than once.");
156                     this.itemsRemoved.Add(removedItem);
157                 }
158             }
159         }
160
161         protected override void OnComplete()
162         {
163             Fx.Assert(this.itemsAdded.Count == 0 && this.itemsRemoved.Count == 0, "There should not be items changed before completed.");
164             this.modelTreeManager.RegisterModelTreeChangeEvents(this);
165
166             bool modelChangeBegin = false;
167             try
168             {
169                 if (this.outerScope == null)
170                 {
171                     appliedChanges = new List<Change>();
172                     int startIndex = 0;
173                     // pump all changes, applying changes can add more changes to the end of the change list.
174                     while (startIndex < this.Changes.Count)
175                     {
176                         // pickup the new changes
177                         List<Change> changesToApply = this.Changes.GetRange(startIndex, this.Changes.Count - startIndex);
178                         startIndex = this.Changes.Count;
179
180                         foreach (Change change in changesToApply)
181                         {
182                             if (change != null)
183                             {
184                                 if (change is ModelChange && !modelChangeBegin)
185                                 {
186                                     this.BeginModelChange();
187                                     modelChangeBegin = true;
188                                 }
189
190                                 if (change.Apply())
191                                 {
192                                     appliedChanges.Add(change);
193                                 }
194                             }
195
196                             if (change is ModelChange)
197                             {
198                                 this.HasModelChanges = true;
199                             }
200                         }
201                     }
202                 }
203                 else
204                 {
205                     outerScope.Changes.AddRange(this.Changes);
206                 }
207             }
208             finally
209             {
210                 if (modelChangeBegin)
211                 {
212                     this.EndModelChange();
213                 }
214
215                 this.modelTreeManager.UnregisterModelTreeChangeEvents(this);
216             }
217
218             this.modelTreeManager.OnEditingScopeCompleted(this);
219         }
220
221         private void BeginModelChange()
222         {
223             ValidationService validationService = this.modelTreeManager.Context.Services.GetService<ValidationService>();
224             if (validationService != null)
225             {
226                 validationService.DeactivateValidation();
227             }
228         }
229
230         private void EndModelChange()
231         {
232             ValidationService validationService = this.modelTreeManager.Context.Services.GetService<ValidationService>();
233             if (validationService != null)
234             {
235                 validationService.ActivateValidation();
236             }
237         }
238
239         protected override bool CanComplete()
240         {
241             return this.modelTreeManager.CanEditingScopeComplete(this);
242         }
243
244         protected override void OnRevert(bool finalizing)
245         {
246             bool modelChangeBegin = false;
247             try
248             {
249                 if (this.appliedChanges != null)
250                 {
251                     List<Change> revertChanges = new List<Change>();
252                     foreach (Change change in this.appliedChanges)
253                     {
254                         revertChanges.Add(change.GetInverse());
255                     }
256                     revertChanges.Reverse();
257                     foreach (Change change in revertChanges)
258                     {
259                         if (change is ModelChange && !modelChangeBegin)
260                         {
261                             this.BeginModelChange();
262                             modelChangeBegin = true;
263                         }
264
265                         change.Apply();
266                         this.appliedChanges.RemoveAt(this.appliedChanges.Count - 1);
267                     }
268                 }
269             }
270             finally
271             {
272                 if (modelChangeBegin)
273                 {
274                     this.EndModelChange();
275                 }
276             }
277
278             this.modelTreeManager.UnregisterModelTreeChangeEvents(this);
279             this.modelTreeManager.OnEditingScopeReverted(this);
280         }
281
282         protected override bool OnException(Exception e)
283         {
284             return false;
285         }
286     }
287 }