Mercurial > projects > dwt-addons
comparison dwtx/core/commands/operations/TriggeredOperations.d @ 3:6518c18a01f7
eclipse.core package without osgi dependencies
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 26 Mar 2008 00:57:19 +0100 |
parents | |
children | ea8ff534f622 |
comparison
equal
deleted
inserted
replaced
2:a012107a911c | 3:6518c18a01f7 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2005, 2006 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.core.commands.operations.TriggeredOperations; | |
14 | |
15 import tango.util.collection.ArraySeq; | |
16 import tango.util.collection.model.Seq; | |
17 | |
18 import dwtx.core.commands.ExecutionException; | |
19 import dwtx.core.runtime.IAdaptable; | |
20 import dwtx.core.runtime.IProgressMonitor; | |
21 import dwtx.core.runtime.IStatus; | |
22 import dwtx.core.runtime.OperationCanceledException; | |
23 import dwtx.core.runtime.Status; | |
24 | |
25 import dwtx.core.commands.operations.AbstractOperation; | |
26 import dwtx.core.commands.operations.ICompositeOperation; | |
27 import dwtx.core.commands.operations.IAdvancedUndoableOperation; | |
28 import dwtx.core.commands.operations.IContextReplacingOperation; | |
29 import dwtx.core.commands.operations.IUndoableOperation; | |
30 import dwtx.core.commands.operations.IOperationHistory; | |
31 import dwtx.core.commands.operations.IUndoContext; | |
32 import dwtx.core.commands.operations.OperationHistoryEvent; | |
33 | |
34 import dwt.dwthelper.utils; | |
35 | |
36 /** | |
37 * Triggered operations are a specialized implementation of a composite | |
38 * operation that keeps track of operations triggered by the execution of some | |
39 * primary operation. The composite knows which operation was the trigger for | |
40 * subsequent operations, and adds all triggered operations as children. When | |
41 * execution, undo, or redo is performed, only the triggered operation is | |
42 * executed, undone, or redone if it is still present. If the trigger is removed | |
43 * from the triggered operations, then the child operations will replace the | |
44 * triggered operations in the history. | |
45 * <p> | |
46 * This class may be instantiated by clients. | |
47 * </p> | |
48 * | |
49 * @since 3.1 | |
50 */ | |
51 public final class TriggeredOperations : AbstractOperation, | |
52 ICompositeOperation, IAdvancedUndoableOperation, | |
53 IContextReplacingOperation { | |
54 | |
55 private IUndoableOperation triggeringOperation; | |
56 | |
57 private IOperationHistory history; | |
58 | |
59 private Seq!(IUndoableOperation) children; | |
60 | |
61 /** | |
62 * Construct a composite triggered operations using the specified undoable | |
63 * operation as the trigger. Use the label of this trigger as the label of | |
64 * the operation. | |
65 * | |
66 * @param operation | |
67 * the operation that will trigger other operations. | |
68 * @param history | |
69 * the operation history containing the triggered operations. | |
70 */ | |
71 public this(IUndoableOperation operation, | |
72 IOperationHistory history) { | |
73 super(operation.getLabel()); | |
74 children = new ArraySeq!(IUndoableOperation); | |
75 triggeringOperation = operation; | |
76 recomputeContexts(); | |
77 this.history = history; | |
78 } | |
79 | |
80 /* | |
81 * (non-Javadoc) | |
82 * | |
83 * @see dwtx.core.commands.operations.IUndoableOperation#add(dwtx.core.commands.operations.IUndoableOperation) | |
84 */ | |
85 public void add(IUndoableOperation operation) { | |
86 children.append(operation); | |
87 recomputeContexts(); | |
88 } | |
89 | |
90 /* | |
91 * (non-Javadoc) | |
92 * | |
93 * @see dwtx.core.commands.operations.IUndoableOperation#remove(dwtx.core.commands.operations.IUndoableOperation) | |
94 */ | |
95 public void remove(IUndoableOperation operation) { | |
96 if (operation is triggeringOperation) { | |
97 // the triggering operation is being removed, so we must replace | |
98 // this composite with its individual triggers. | |
99 triggeringOperation = null; | |
100 // save the children before replacing the operation, since this | |
101 // operation will be disposed as part of replacing it. We don't want | |
102 // the children to be disposed since they are to replace this | |
103 // operation. | |
104 Seq!(IUndoableOperation) childrenToRestore = children.dup; | |
105 children = new ArraySeq!(IUndoableOperation); | |
106 recomputeContexts(); | |
107 operation.dispose(); | |
108 // now replace the triggering operation | |
109 history.replaceOperation(this, childrenToRestore.toArray()); | |
110 } else { | |
111 children.remove(operation); | |
112 operation.dispose(); | |
113 recomputeContexts(); | |
114 } | |
115 } | |
116 | |
117 /** | |
118 * Remove the specified context from the receiver. This method is typically | |
119 * invoked when the history is being flushed for a certain context. In the | |
120 * case of triggered operations, if the only context for the triggering | |
121 * operation is being removed, then the triggering operation must be | |
122 * replaced in the operation history with the atomic operations that it | |
123 * triggered. If the context being removed is not the only context for the | |
124 * triggering operation, the triggering operation will remain, and the | |
125 * children will each be similarly checked. | |
126 * | |
127 * @param context | |
128 * the undo context being removed from the receiver. | |
129 */ | |
130 public void removeContext(IUndoContext context) { | |
131 | |
132 bool recompute = false; | |
133 // first check to see if we are removing the only context of the | |
134 // triggering operation | |
135 if (triggeringOperation !is null | |
136 && triggeringOperation.hasContext(context)) { | |
137 if (triggeringOperation.getContexts().length is 1) { | |
138 remove(triggeringOperation); | |
139 return; | |
140 } | |
141 triggeringOperation.removeContext(context); | |
142 recompute = true; | |
143 } | |
144 // the triggering operation remains, check all the children | |
145 auto toBeRemoved = new ArraySeq!(IUndoableOperation); | |
146 for (int i = 0; i < children.size(); i++) { | |
147 IUndoableOperation child = cast(IUndoableOperation) children.get(i); | |
148 if (child.hasContext(context)) { | |
149 if (child.getContexts().length is 1) { | |
150 toBeRemoved.append(child); | |
151 } else { | |
152 child.removeContext(context); | |
153 } | |
154 recompute = true; | |
155 } | |
156 } | |
157 for (int i = 0; i < toBeRemoved.size(); i++) { | |
158 remove(cast(IUndoableOperation) toBeRemoved.get(i)); | |
159 } | |
160 if (recompute) { | |
161 recomputeContexts(); | |
162 } | |
163 } | |
164 | |
165 /* | |
166 * (non-Javadoc) | |
167 * | |
168 * @see dwtx.core.commands.operations.IUndoableOperation#execute(dwtx.core.runtime.IProgressMonitor, | |
169 * dwtx.core.runtime.IAdaptable) | |
170 */ | |
171 public IStatus execute(IProgressMonitor monitor, IAdaptable info) { | |
172 if (triggeringOperation !is null) { | |
173 history.openOperation(this, IOperationHistory.EXECUTE); | |
174 try { | |
175 IStatus status = triggeringOperation.execute(monitor, info); | |
176 history.closeOperation(status.isOK(), false, | |
177 IOperationHistory.EXECUTE); | |
178 return status; | |
179 } catch (ExecutionException e) { | |
180 history.closeOperation(false, false, IOperationHistory.EXECUTE); | |
181 throw e; | |
182 } catch (RuntimeException e) { | |
183 history.closeOperation(false, false, IOperationHistory.EXECUTE); | |
184 throw e; | |
185 } | |
186 | |
187 } | |
188 return IOperationHistory.OPERATION_INVALID_STATUS; | |
189 } | |
190 | |
191 /* | |
192 * (non-Javadoc) | |
193 * | |
194 * @see dwtx.core.commands.operations.IUndoableOperation#redo(dwtx.core.runtime.IProgressMonitor, | |
195 * dwtx.core.runtime.IAdaptable) | |
196 */ | |
197 public IStatus redo(IProgressMonitor monitor, IAdaptable info) { | |
198 if (triggeringOperation !is null) { | |
199 history.openOperation(this, IOperationHistory.REDO); | |
200 Seq!(IUndoableOperation) childrenToRestore = children.dup; | |
201 try { | |
202 removeAllChildren(); | |
203 IStatus status = triggeringOperation.redo(monitor, info); | |
204 if (!status.isOK()) { | |
205 children = childrenToRestore; | |
206 } | |
207 history.closeOperation(status.isOK(), false, | |
208 IOperationHistory.REDO); | |
209 return status; | |
210 } catch (ExecutionException e) { | |
211 children = childrenToRestore; | |
212 history.closeOperation(false, false, IOperationHistory.REDO); | |
213 throw e; | |
214 } catch (RuntimeException e) { | |
215 children = childrenToRestore; | |
216 history.closeOperation(false, false, IOperationHistory.REDO); | |
217 throw e; | |
218 } | |
219 } | |
220 return IOperationHistory.OPERATION_INVALID_STATUS; | |
221 } | |
222 | |
223 /* | |
224 * (non-Javadoc) | |
225 * | |
226 * @see dwtx.core.commands.operations.IUndoableOperation#undo(dwtx.core.runtime.IProgressMonitor, | |
227 * dwtx.core.runtime.IAdaptable) | |
228 */ | |
229 public IStatus undo(IProgressMonitor monitor, IAdaptable info) { | |
230 if (triggeringOperation !is null) { | |
231 history.openOperation(this, IOperationHistory.UNDO); | |
232 auto childrenToRestore = children.dup; | |
233 try { | |
234 removeAllChildren(); | |
235 IStatus status = triggeringOperation.undo(monitor, info); | |
236 if (!status.isOK()) { | |
237 children = childrenToRestore; | |
238 } | |
239 history.closeOperation(status.isOK(), false, | |
240 IOperationHistory.UNDO); | |
241 return status; | |
242 } catch (ExecutionException e) { | |
243 children = childrenToRestore; | |
244 history.closeOperation(false, false, IOperationHistory.UNDO); | |
245 throw e; | |
246 } catch (RuntimeException e) { | |
247 children = childrenToRestore; | |
248 history.closeOperation(false, false, IOperationHistory.UNDO); | |
249 throw e; | |
250 } | |
251 } | |
252 return IOperationHistory.OPERATION_INVALID_STATUS; | |
253 } | |
254 | |
255 /* | |
256 * (non-Javadoc) | |
257 * | |
258 * @see dwtx.core.commands.operations.IUndoableOperation#canUndo() | |
259 */ | |
260 public bool canUndo() { | |
261 if (triggeringOperation !is null) { | |
262 return triggeringOperation.canUndo(); | |
263 } | |
264 return false; | |
265 } | |
266 | |
267 /* | |
268 * (non-Javadoc) | |
269 * | |
270 * @see dwtx.core.commands.operations.IUndoableOperation#canExecute() | |
271 */ | |
272 public bool canExecute() { | |
273 if (triggeringOperation !is null) { | |
274 return triggeringOperation.canExecute(); | |
275 } | |
276 return false; | |
277 } | |
278 | |
279 /* | |
280 * (non-Javadoc) | |
281 * | |
282 * @see dwtx.core.commands.operations.IUndoableOperation#canRedo() | |
283 */ | |
284 public bool canRedo() { | |
285 if (triggeringOperation !is null) { | |
286 return triggeringOperation.canRedo(); | |
287 } | |
288 return false; | |
289 } | |
290 | |
291 /* | |
292 * Dispose all operations in the receiver. | |
293 */ | |
294 public void dispose() { | |
295 for (int i = 0; i < children.size(); i++) { | |
296 children.get(i).dispose(); | |
297 } | |
298 if (triggeringOperation !is null) { | |
299 triggeringOperation.dispose(); | |
300 } | |
301 } | |
302 | |
303 /* | |
304 * Recompute contexts in light of some change in the children | |
305 */ | |
306 private void recomputeContexts() { | |
307 auto allContexts = new ArraySeq!(IUndoContext); | |
308 if (triggeringOperation !is null) { | |
309 IUndoContext[] contexts = triggeringOperation.getContexts(); | |
310 for (int i = 0; i < contexts.length; i++) { | |
311 allContexts.append(contexts[i]); | |
312 } | |
313 } | |
314 for (int i = 0; i < children.size(); i++) { | |
315 IUndoContext[] contexts = children.get(i).getContexts(); | |
316 for (int j = 0; j < contexts.length; j++) { | |
317 if (!allContexts.contains(contexts[j])) { | |
318 allContexts.append(contexts[j]); | |
319 } | |
320 } | |
321 } | |
322 contexts = allContexts; | |
323 | |
324 } | |
325 | |
326 /* | |
327 * Remove all non-triggering children | |
328 */ | |
329 private void removeAllChildren() { | |
330 IUndoableOperation[] nonTriggers = children.toArray(); | |
331 for (int i = 0; i < nonTriggers.length; i++) { | |
332 children.remove(nonTriggers[i]); | |
333 nonTriggers[i].dispose(); | |
334 } | |
335 } | |
336 | |
337 /** | |
338 * Return the operation that triggered the other operations in this | |
339 * composite. | |
340 * | |
341 * @return the IUndoableOperation that triggered the other children. | |
342 */ | |
343 public IUndoableOperation getTriggeringOperation() { | |
344 return triggeringOperation; | |
345 } | |
346 | |
347 /* | |
348 * (non-Javadoc) | |
349 * | |
350 * @see dwtx.core.commands.operations.IAdvancedModelOperation#getAffectedObjects() | |
351 */ | |
352 public Object[] getAffectedObjects() { | |
353 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) { | |
354 return trg | |
355 .getAffectedObjects(); | |
356 } | |
357 return null; | |
358 } | |
359 | |
360 /* | |
361 * (non-Javadoc) | |
362 * | |
363 * @see dwtx.core.commands.operations.IAdvancedModelOperation#aboutToNotify(dwtx.core.commands.operations.OperationHistoryEvent) | |
364 */ | |
365 public void aboutToNotify(OperationHistoryEvent event) { | |
366 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) { | |
367 trg.aboutToNotify(event); | |
368 } | |
369 } | |
370 | |
371 /* | |
372 * (non-Javadoc) | |
373 * | |
374 * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeUndoableStatus(dwtx.core.runtime.IProgressMonitor) | |
375 */ | |
376 public IStatus computeUndoableStatus(IProgressMonitor monitor) { | |
377 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) { | |
378 try { | |
379 return trg.computeUndoableStatus(monitor); | |
380 } catch (OperationCanceledException e) { | |
381 return Status.CANCEL_STATUS; | |
382 } | |
383 } | |
384 return Status.OK_STATUS; | |
385 | |
386 } | |
387 | |
388 /* | |
389 * (non-Javadoc) | |
390 * | |
391 * @see dwtx.core.commands.operations.IAdvancedUndoableOperation#computeRedoableStatus(dwtx.core.runtime.IProgressMonitor) | |
392 */ | |
393 public IStatus computeRedoableStatus(IProgressMonitor monitor) { | |
394 if ( auto trg = cast(IAdvancedUndoableOperation)triggeringOperation ) { | |
395 try { | |
396 return trg.computeRedoableStatus(monitor); | |
397 } catch (OperationCanceledException e) { | |
398 return Status.CANCEL_STATUS; | |
399 } | |
400 } | |
401 return Status.OK_STATUS; | |
402 | |
403 } | |
404 | |
405 /** | |
406 * Replace the undo context of the receiver with the provided replacement | |
407 * undo context. In the case of triggered operations, all contained | |
408 * operations are checked and any occurrence of the original context is | |
409 * replaced with the new undo context. | |
410 * <p> | |
411 * This message has no effect if the original undo context is not present in | |
412 * the receiver. | |
413 * | |
414 * @param original | |
415 * the undo context which is to be replaced | |
416 * @param replacement | |
417 * the undo context which is replacing the original | |
418 * @since 3.2 | |
419 */ | |
420 public void replaceContext(IUndoContext original, IUndoContext replacement) { | |
421 | |
422 // first check the triggering operation | |
423 if (triggeringOperation !is null | |
424 && triggeringOperation.hasContext(original)) { | |
425 if ( auto trg = cast(IContextReplacingOperation)triggeringOperation ) { | |
426 trg.replaceContext(original, replacement); | |
427 } else { | |
428 triggeringOperation.removeContext(original); | |
429 triggeringOperation.addContext(replacement); | |
430 } | |
431 } | |
432 // Now check all the children | |
433 for (int i = 0; i < children.size(); i++) { | |
434 IUndoableOperation child = children.get(i); | |
435 if (child.hasContext(original)) { | |
436 if ( auto c = cast(IContextReplacingOperation)child ) { | |
437 c.replaceContext( | |
438 original, replacement); | |
439 } else { | |
440 child.removeContext(original); | |
441 child.addContext(replacement); | |
442 } | |
443 } | |
444 } | |
445 recomputeContexts(); | |
446 } | |
447 | |
448 /** | |
449 * Add the specified context to the operation. Overridden in | |
450 * TriggeredOperations to add the specified undo context to the triggering | |
451 * operation. | |
452 * | |
453 * @param context | |
454 * the context to be added | |
455 * | |
456 * @since 3.2 | |
457 */ | |
458 public void addContext(IUndoContext context) { | |
459 if (triggeringOperation !is null) { | |
460 triggeringOperation.addContext(context); | |
461 recomputeContexts(); | |
462 } | |
463 } | |
464 | |
465 } |