KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > SaveablesList


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 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  *******************************************************************************/

11
12 package org.eclipse.ui.internal;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Set JavaDoc;
22
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.AssertionFailedException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.ListenerList;
28 import org.eclipse.core.runtime.SubProgressMonitor;
29 import org.eclipse.jface.dialogs.IDialogConstants;
30 import org.eclipse.jface.dialogs.MessageDialog;
31 import org.eclipse.jface.dialogs.MessageDialogWithToggle;
32 import org.eclipse.jface.operation.IRunnableContext;
33 import org.eclipse.jface.operation.IRunnableWithProgress;
34 import org.eclipse.jface.preference.IPreferenceStore;
35 import org.eclipse.jface.viewers.ArrayContentProvider;
36 import org.eclipse.jface.viewers.ILabelProvider;
37 import org.eclipse.jface.viewers.IStructuredContentProvider;
38 import org.eclipse.jface.window.IShellProvider;
39 import org.eclipse.osgi.util.NLS;
40 import org.eclipse.swt.SWT;
41 import org.eclipse.swt.events.SelectionAdapter;
42 import org.eclipse.swt.events.SelectionEvent;
43 import org.eclipse.swt.layout.GridData;
44 import org.eclipse.swt.layout.GridLayout;
45 import org.eclipse.swt.widgets.Button;
46 import org.eclipse.swt.widgets.Composite;
47 import org.eclipse.swt.widgets.Control;
48 import org.eclipse.swt.widgets.Label;
49 import org.eclipse.swt.widgets.Shell;
50 import org.eclipse.ui.ISaveablePart;
51 import org.eclipse.ui.ISaveablePart2;
52 import org.eclipse.ui.ISaveablesLifecycleListener;
53 import org.eclipse.ui.ISaveablesSource;
54 import org.eclipse.ui.IWorkbenchPart;
55 import org.eclipse.ui.IWorkbenchPreferenceConstants;
56 import org.eclipse.ui.IWorkbenchWindow;
57 import org.eclipse.ui.PlatformUI;
58 import org.eclipse.ui.Saveable;
59 import org.eclipse.ui.SaveablesLifecycleEvent;
60 import org.eclipse.ui.dialogs.ListSelectionDialog;
61 import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
62 import org.eclipse.ui.internal.misc.StatusUtil;
63 import org.eclipse.ui.internal.util.PrefUtil;
64 import org.eclipse.ui.model.WorkbenchPartLabelProvider;
65
66 /**
67  * The model manager maintains a list of open saveable models.
68  *
69  * @see Saveable
70  * @see ISaveablesSource
71  *
72  * @since 3.2
73  */

74 public class SaveablesList implements ISaveablesLifecycleListener {
75
76     private ListenerList listeners = new ListenerList();
77
78     // event source (mostly ISaveablesSource) -> Set of Saveable
79
private Map JavaDoc modelMap = new HashMap JavaDoc();
80
81     // reference counting map, Saveable -> Integer
82
private Map JavaDoc modelRefCounts = new HashMap JavaDoc();
83
84     private Set JavaDoc nonPartSources = new HashSet JavaDoc();
85
86     /**
87      * Returns the list of open models managed by this model manager.
88      *
89      * @return a list of models
90      */

91     public Saveable[] getOpenModels() {
92         Set JavaDoc allDistinctModels = new HashSet JavaDoc();
93         Iterator JavaDoc saveables = modelMap.values().iterator();
94         while (saveables.hasNext())
95             allDistinctModels.addAll((Set JavaDoc)saveables.next());
96         
97         return (Saveable[]) allDistinctModels.toArray(
98                 new Saveable[allDistinctModels.size()]);
99     }
100
101     // returns true if this model has not yet been in getModels()
102
private boolean addModel(Object JavaDoc source, Saveable model) {
103         boolean result = false;
104         Set JavaDoc modelsForSource = (Set JavaDoc) modelMap.get(source);
105         if (modelsForSource == null) {
106             modelsForSource = new HashSet JavaDoc();
107             modelMap.put(source, modelsForSource);
108         }
109         if (modelsForSource.add(model)) {
110             result = incrementRefCount(modelRefCounts, model);
111         } else {
112             logWarning(
113                     "Ignored attempt to add saveable that was already registered", source, model); //$NON-NLS-1$
114
}
115         return result;
116     }
117
118     /**
119      * returns true if the given key was added for the first time
120      *
121      * @param referenceMap
122      * @param key
123      * @return true if the ref count of the given key is now 1
124      */

125     private boolean incrementRefCount(Map JavaDoc referenceMap, Object JavaDoc key) {
126         boolean result = false;
127         Integer JavaDoc refCount = (Integer JavaDoc) referenceMap.get(key);
128         if (refCount == null) {
129             result = true;
130             refCount = new Integer JavaDoc(0);
131         }
132         referenceMap.put(key, new Integer JavaDoc(refCount.intValue() + 1));
133         return result;
134     }
135
136     /**
137      * returns true if the given key has been removed
138      *
139      * @param referenceMap
140      * @param key
141      * @return true if the ref count of the given key was 1
142      */

143     private boolean decrementRefCount(Map JavaDoc referenceMap, Object JavaDoc key) {
144         boolean result = false;
145         Integer JavaDoc refCount = (Integer JavaDoc) referenceMap.get(key);
146         Assert.isTrue(refCount != null);
147         if (refCount.intValue() == 1) {
148             referenceMap.remove(key);
149             result = true;
150         } else {
151             referenceMap.put(key, new Integer JavaDoc(refCount.intValue() - 1));
152         }
153         return result;
154     }
155
156     // returns true if this model was removed from getModels();
157
private boolean removeModel(Object JavaDoc source, Saveable model) {
158         boolean result = false;
159         Set JavaDoc modelsForSource = (Set JavaDoc) modelMap.get(source);
160         if (modelsForSource == null) {
161             logWarning(
162                     "Ignored attempt to remove a saveable when no saveables were known", source, model); //$NON-NLS-1$
163
} else {
164             if (modelsForSource.remove(model)) {
165                 result = decrementRefCount(modelRefCounts, model);
166                 if (modelsForSource.isEmpty()) {
167                     modelMap.remove(source);
168                 }
169             } else {
170                 logWarning(
171                         "Ignored attempt to remove a saveable that was not registered", source, model); //$NON-NLS-1$
172
}
173         }
174         return result;
175     }
176
177     private void logWarning(String JavaDoc message, Object JavaDoc source, Saveable model) {
178         // create a new exception
179
AssertionFailedException assertionFailedException = new AssertionFailedException("unknown saveable: " + model //$NON-NLS-1$
180
+ " from part: " + source); //$NON-NLS-1$
181
// record the current stack trace to help with debugging
182
assertionFailedException.fillInStackTrace();
183         WorkbenchPlugin.log(StatusUtil.newStatus(IStatus.WARNING, message,
184                 assertionFailedException));
185     }
186
187     /**
188      * This implementation of handleModelLifecycleEvent must be called by
189      * implementers of ISaveablesSource whenever the list of models of the model
190      * source changes, or when the dirty state of models changes. The
191      * ISaveablesSource instance must be passed as the source of the event
192      * object.
193      * <p>
194      * This method may also be called by objects that hold on to models but are
195      * not workbench parts. In this case, the event source must be set to an
196      * object that is not an instanceof IWorkbenchPart.
197      * </p>
198      * <p>
199      * Corresponding open and close events must originate from the same
200      * (identical) event source.
201      * </p>
202      * <p>
203      * This method must be called on the UI thread.
204      * </p>
205      */

206     public void handleLifecycleEvent(SaveablesLifecycleEvent event) {
207         if (!(event.getSource() instanceof IWorkbenchPart)) {
208             // just update the set of non-part sources. No prompting necessary.
209
// See bug 139004.
210
updateNonPartSource((ISaveablesSource) event.getSource());
211             return;
212         }
213         Saveable[] modelArray = event.getSaveables();
214         switch (event.getEventType()) {
215         case SaveablesLifecycleEvent.POST_OPEN:
216             addModels(event.getSource(), modelArray);
217             break;
218         case SaveablesLifecycleEvent.PRE_CLOSE:
219             Saveable[] models = event.getSaveables();
220             Map JavaDoc modelsDecrementing = new HashMap JavaDoc();
221             Set JavaDoc modelsClosing = new HashSet JavaDoc();
222             for (int i = 0; i < models.length; i++) {
223                 incrementRefCount(modelsDecrementing, models[i]);
224             }
225
226             fillModelsClosing(modelsClosing, modelsDecrementing);
227             boolean canceled = promptForSavingIfNecessary(PlatformUI
228                     .getWorkbench().getActiveWorkbenchWindow(), modelsClosing, modelsDecrementing,
229                     !event.isForce());
230             if (canceled) {
231                 event.setVeto(true);
232             }
233             break;
234         case SaveablesLifecycleEvent.POST_CLOSE:
235             removeModels(event.getSource(), modelArray);
236             break;
237         case SaveablesLifecycleEvent.DIRTY_CHANGED:
238             fireModelLifecycleEvent(new SaveablesLifecycleEvent(this, event
239                     .getEventType(), event.getSaveables(), false));
240             break;
241         }
242     }
243
244     /**
245      * Updates the set of non-part saveables sources.
246      * @param source
247      */

248     private void updateNonPartSource(ISaveablesSource source) {
249         Saveable[] saveables = source.getSaveables();
250         if (saveables.length == 0) {
251             nonPartSources.remove(source);
252         } else {
253             nonPartSources.add(source);
254         }
255     }
256
257     /**
258      * @param source
259      * @param modelArray
260      */

261     private void removeModels(Object JavaDoc source, Saveable[] modelArray) {
262         List JavaDoc removed = new ArrayList JavaDoc();
263         for (int i = 0; i < modelArray.length; i++) {
264             Saveable model = modelArray[i];
265             if (removeModel(source, model)) {
266                 removed.add(model);
267             }
268         }
269         if (removed.size() > 0) {
270             fireModelLifecycleEvent(new SaveablesLifecycleEvent(this,
271                     SaveablesLifecycleEvent.POST_OPEN, (Saveable[]) removed
272                             .toArray(new Saveable[removed.size()]), false));
273         }
274     }
275
276     /**
277      * @param source
278      * @param modelArray
279      */

280     private void addModels(Object JavaDoc source, Saveable[] modelArray) {
281         List JavaDoc added = new ArrayList JavaDoc();
282         for (int i = 0; i < modelArray.length; i++) {
283             Saveable model = modelArray[i];
284             if (addModel(source, model)) {
285                 added.add(model);
286             }
287         }
288         if (added.size() > 0) {
289             fireModelLifecycleEvent(new SaveablesLifecycleEvent(this,
290                     SaveablesLifecycleEvent.POST_OPEN, (Saveable[]) added
291                             .toArray(new Saveable[added.size()]), false));
292         }
293     }
294
295     /**
296      * @param event
297      */

298     private void fireModelLifecycleEvent(SaveablesLifecycleEvent event) {
299         Object JavaDoc[] listenerArray = listeners.getListeners();
300         for (int i = 0; i < listenerArray.length; i++) {
301             ((ISaveablesLifecycleListener) listenerArray[i])
302                     .handleLifecycleEvent(event);
303         }
304     }
305
306     /**
307      * Adds the given listener to the list of listeners. Has no effect if the
308      * same (identical) listener has already been added. The listener will be
309      * notified about changes to the models managed by this model manager. Event
310      * types include: <br>
311      * POST_OPEN when models were added to the list of models <br>
312      * POST_CLOSE when models were removed from the list of models <br>
313      * DIRTY_CHANGED when the dirty state of models changed
314      * <p>
315      * Listeners should ignore all other event types, including PRE_CLOSE. There
316      * is no guarantee that listeners are notified before models are closed.
317      *
318      * @param listener
319      */

320     public void addModelLifecycleListener(ISaveablesLifecycleListener listener) {
321         listeners.add(listener);
322     }
323
324     /**
325      * Removes the given listener from the list of listeners. Has no effect if
326      * the given listener is not contained in the list.
327      *
328      * @param listener
329      */

330     public void removeModelLifecycleListener(ISaveablesLifecycleListener listener) {
331         listeners.remove(listener);
332     }
333
334     /**
335      * @param partsToClose
336      * @param save
337      * @param window
338      * @return the post close info to be passed to postClose
339      */

340     public Object JavaDoc preCloseParts(List JavaDoc partsToClose, boolean save,
341             final IWorkbenchWindow window) {
342         // reference count (how many occurrences of a model will go away?)
343
PostCloseInfo postCloseInfo = new PostCloseInfo();
344         for (Iterator JavaDoc it = partsToClose.iterator(); it.hasNext();) {
345             IWorkbenchPart part = (IWorkbenchPart) it.next();
346             postCloseInfo.partsClosing.add(part);
347             if (part instanceof ISaveablePart) {
348                 ISaveablePart saveablePart = (ISaveablePart) part;
349                 if (save && !saveablePart.isSaveOnCloseNeeded()) {
350                     // pretend for now that this part is not closing
351
continue;
352                 }
353             }
354             if (save && part instanceof ISaveablePart2) {
355                 ISaveablePart2 saveablePart2 = (ISaveablePart2) part;
356                 // TODO show saveablePart2 before prompting, see
357
// EditorManager.saveAll
358
int response = SaveableHelper.savePart(saveablePart2, window,
359                         true);
360                 if (response == ISaveablePart2.CANCEL) {
361                     // user canceled
362
return null;
363                 } else if (response != ISaveablePart2.DEFAULT) {
364                     // only include this part in the following logic if it returned
365
// DEFAULT
366
continue;
367                 }
368             }
369             Saveable[] modelsFromSource = getSaveables(part);
370             for (int i = 0; i < modelsFromSource.length; i++) {
371                 incrementRefCount(postCloseInfo.modelsDecrementing,
372                         modelsFromSource[i]);
373             }
374         }
375         fillModelsClosing(postCloseInfo.modelsClosing,
376                 postCloseInfo.modelsDecrementing);
377         if (save) {
378             boolean canceled = promptForSavingIfNecessary(window,
379                     postCloseInfo.modelsClosing, postCloseInfo.modelsDecrementing, true);
380             if (canceled) {
381                 return null;
382             }
383         }
384         return postCloseInfo;
385     }
386
387     /**
388      * @param window
389      * @param modelsClosing
390      * @param canCancel
391      * @return true if the user canceled
392      */

393     private boolean promptForSavingIfNecessary(final IWorkbenchWindow window,
394             Set JavaDoc modelsClosing, Map JavaDoc modelsDecrementing, boolean canCancel) {
395         List JavaDoc modelsToOptionallySave = new ArrayList JavaDoc();
396         for (Iterator JavaDoc it = modelsDecrementing.keySet().iterator(); it.hasNext();) {
397             Saveable modelDecrementing = (Saveable) it.next();
398             if (modelDecrementing.isDirty() && !modelsClosing.contains(modelDecrementing)) {
399                 modelsToOptionallySave.add(modelDecrementing);
400             }
401         }
402         
403         boolean shouldCancel = modelsToOptionallySave.isEmpty() ? false : promptForSaving(modelsToOptionallySave,
404                 window, window, canCancel, true);
405         
406         if (shouldCancel) {
407             return true;
408         }
409
410         List JavaDoc modelsToSave = new ArrayList JavaDoc();
411         for (Iterator JavaDoc it = modelsClosing.iterator(); it.hasNext();) {
412             Saveable modelClosing = (Saveable) it.next();
413             if (modelClosing.isDirty()) {
414                 modelsToSave.add(modelClosing);
415             }
416         }
417         return modelsToSave.isEmpty() ? false : promptForSaving(modelsToSave,
418                 window, window, canCancel, false);
419     }
420
421     /**
422      * @param modelsClosing
423      * @param modelsDecrementing
424      */

425     private void fillModelsClosing(Set JavaDoc modelsClosing, Map JavaDoc modelsDecrementing) {
426         for (Iterator JavaDoc it = modelsDecrementing.keySet().iterator(); it.hasNext();) {
427             Saveable model = (Saveable) it.next();
428             if (modelsDecrementing.get(model).equals(modelRefCounts.get(model))) {
429                 modelsClosing.add(model);
430             }
431         }
432     }
433
434     /**
435      * Prompt the user to save the given saveables.
436      * @param modelsToSave the saveables to be saved
437      * @param shellProvider the provider used to obtain a shell in prompting is
438      * required. Clients can use a workbench window for this.
439      * @param runnableContext a runnable context that will be used to provide a
440      * progress monitor while the save is taking place. Clients can
441      * use a workbench window for this.
442      * @param canCancel whether the operation can be canceled
443      * @param stillOpenElsewhere whether the models are referenced by open parts
444      * @return true if the user canceled
445      */

446     public boolean promptForSaving(List JavaDoc modelsToSave,
447             final IShellProvider shellProvider, IRunnableContext runnableContext, final boolean canCancel, boolean stillOpenElsewhere) {
448         // Save parts, exit the method if cancel is pressed.
449
if (modelsToSave.size() > 0) {
450             boolean canceled = SaveableHelper.waitForBackgroundSaveJobs(modelsToSave);
451             if (canceled) {
452                 return true;
453             }
454
455             IPreferenceStore apiPreferenceStore = PrefUtil.getAPIPreferenceStore();
456             boolean dontPrompt = stillOpenElsewhere && !apiPreferenceStore.getBoolean(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN);
457
458             if (dontPrompt) {
459                 modelsToSave.clear();
460                 return false;
461             } else if (modelsToSave.size() == 1) {
462                 Saveable model = (Saveable) modelsToSave.get(0);
463                 // Show a dialog.
464
String JavaDoc[] buttons;
465                 if(canCancel) {
466                     buttons = new String JavaDoc[] { IDialogConstants.YES_LABEL,
467                             IDialogConstants.NO_LABEL,
468                             IDialogConstants.CANCEL_LABEL };
469                 } else {
470                     buttons = new String JavaDoc[] { IDialogConstants.YES_LABEL,
471                             IDialogConstants.NO_LABEL};
472                 }
473
474                 // don't save if we don't prompt
475
int choice = ISaveablePart2.NO;
476                 
477                 MessageDialog dialog;
478                 if (stillOpenElsewhere) {
479                     String JavaDoc message = NLS
480                             .bind(
481                                     WorkbenchMessages.EditorManager_saveChangesOptionallyQuestion,
482                                     model.getName());
483                     MessageDialogWithToggle dialogWithToggle = new MessageDialogWithToggle(shellProvider.getShell(),
484                             WorkbenchMessages.Save_Resource, null, message,
485                             MessageDialog.QUESTION, buttons, 0, WorkbenchMessages.EditorManager_closeWithoutPromptingOption, false) {
486                         protected int getShellStyle() {
487                             return (canCancel ? SWT.CLOSE : SWT.NONE)
488                                     | SWT.TITLE | SWT.BORDER
489                                     | SWT.APPLICATION_MODAL
490                                     | getDefaultOrientation();
491                         }
492                     };
493                     dialog = dialogWithToggle;
494                 } else {
495                     String JavaDoc message = NLS
496                             .bind(
497                                     WorkbenchMessages.EditorManager_saveChangesQuestion,
498                                     model.getName());
499                     dialog = new MessageDialog(shellProvider.getShell(),
500                             WorkbenchMessages.Save_Resource, null, message,
501                             MessageDialog.QUESTION, buttons, 0) {
502                         protected int getShellStyle() {
503                             return (canCancel ? SWT.CLOSE : SWT.NONE)
504                                     | SWT.TITLE | SWT.BORDER
505                                     | SWT.APPLICATION_MODAL
506                                     | getDefaultOrientation();
507                         }
508                     };
509                 }
510
511                 choice = SaveableHelper.testGetAutomatedResponse();
512                 if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
513                     choice = dialog.open();
514                     
515                     if(stillOpenElsewhere) {
516                         // map value of choice back to ISaveablePart2 values
517
switch (choice) {
518                         case IDialogConstants.YES_ID:
519                             choice = ISaveablePart2.YES;
520                             break;
521                         case IDialogConstants.NO_ID:
522                             choice = ISaveablePart2.NO;
523                             break;
524                         case IDialogConstants.CANCEL_ID:
525                             choice = ISaveablePart2.CANCEL;
526                             break;
527                         default:
528                             break;
529                         }
530                         MessageDialogWithToggle dialogWithToggle = (MessageDialogWithToggle) dialog;
531                         if (choice != ISaveablePart2.CANCEL && dialogWithToggle.getToggleState()) {
532                             apiPreferenceStore.setValue(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN, false);
533                         }
534                     }
535                 }
536
537                 // Branch on the user choice.
538
// The choice id is based on the order of button labels
539
// above.
540
switch (choice) {
541                 case ISaveablePart2.YES: // yes
542
break;
543                 case ISaveablePart2.NO: // no
544
modelsToSave.clear();
545                     break;
546                 default:
547                 case ISaveablePart2.CANCEL: // cancel
548
return true;
549                 }
550             } else {
551                 MyListSelectionDialog dlg = new MyListSelectionDialog(
552                         shellProvider.getShell(),
553                         modelsToSave,
554                         new ArrayContentProvider(),
555                         new WorkbenchPartLabelProvider(),
556                         stillOpenElsewhere ? WorkbenchMessages.EditorManager_saveResourcesOptionallyMessage
557                                 : WorkbenchMessages.EditorManager_saveResourcesMessage,
558                         canCancel, stillOpenElsewhere);
559                 dlg.setInitialSelections(modelsToSave.toArray());
560                 dlg.setTitle(EditorManager.SAVE_RESOURCES_TITLE);
561
562                 // this "if" statement aids in testing.
563
if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
564                     int result = dlg.open();
565                     // Just return null to prevent the operation continuing
566
if (result == IDialogConstants.CANCEL_ID)
567                         return true;
568
569                     if (dlg.getDontPromptSelection()) {
570                         apiPreferenceStore.setValue(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN, false);
571                     }
572                     
573                     modelsToSave = Arrays.asList(dlg.getResult());
574                 }
575             }
576         }
577         // Create save block.
578
return saveModels(modelsToSave, shellProvider, runnableContext);
579     }
580
581     /**
582      * Save the given models.
583      * @param finalModels the list of models to be saved
584      * @param shellProvider the provider used to obtain a shell in prompting is
585      * required. Clients can use a workbench window for this.
586      * @param runnableContext a runnable context that will be used to provide a
587      * progress monitor while the save is taking place. Clients can
588      * use a workbench window for this.
589      * @return <code>true</code> if the operation was canceled
590      */

591     public boolean saveModels(final List JavaDoc finalModels, final IShellProvider shellProvider, IRunnableContext runnableContext) {
592         IRunnableWithProgress progressOp = new IRunnableWithProgress() {
593             public void run(IProgressMonitor monitor) {
594                 IProgressMonitor monitorWrap = new EventLoopProgressMonitor(
595                         monitor);
596                 monitorWrap.beginTask("", finalModels.size()); //$NON-NLS-1$
597
for (Iterator JavaDoc i = finalModels.iterator(); i.hasNext();) {
598                     Saveable model = (Saveable) i.next();
599                     // handle case where this model got saved as a result of
600
// saving another
601
if (!model.isDirty()) {
602                         monitor.worked(1);
603                         continue;
604                     }
605                     SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), shellProvider, true);
606                     if (monitorWrap.isCanceled())
607                         break;
608                 }
609                 monitorWrap.done();
610             }
611         };
612
613         // Do the save.
614
return !SaveableHelper.runProgressMonitorOperation(
615                 WorkbenchMessages.Save_All, progressOp, runnableContext,
616                 shellProvider);
617     }
618
619     private static class PostCloseInfo {
620         private List JavaDoc partsClosing = new ArrayList JavaDoc();
621
622         private Map JavaDoc modelsDecrementing = new HashMap JavaDoc();
623
624         private Set JavaDoc modelsClosing = new HashSet JavaDoc();
625     }
626
627     /**
628      * @param postCloseInfoObject
629      */

630     public void postClose(Object JavaDoc postCloseInfoObject) {
631         PostCloseInfo postCloseInfo = (PostCloseInfo) postCloseInfoObject;
632         List JavaDoc removed = new ArrayList JavaDoc();
633         for (Iterator JavaDoc it = postCloseInfo.partsClosing.iterator(); it.hasNext();) {
634             IWorkbenchPart part = (IWorkbenchPart) it.next();
635             Set JavaDoc saveables = (Set JavaDoc) modelMap.get(part);
636             if (saveables != null) {
637                 // make a copy to avoid a ConcurrentModificationException - we
638
// will remove from the original set as we iterate
639
saveables = new HashSet JavaDoc(saveables);
640                 for (Iterator JavaDoc it2 = saveables.iterator(); it2.hasNext();) {
641                     Saveable saveable = (Saveable) it2.next();
642                     if (removeModel(part, saveable)) {
643                         removed.add(saveable);
644                     }
645                 }
646             }
647         }
648         if (removed.size() > 0) {
649             fireModelLifecycleEvent(new SaveablesLifecycleEvent(this,
650                     SaveablesLifecycleEvent.POST_CLOSE, (Saveable[]) removed
651                             .toArray(new Saveable[removed.size()]), false));
652         }
653     }
654
655     /**
656      * Returns the saveable models provided by the given part. If the part does
657      * not provide any models, a default model is returned representing the
658      * part.
659      *
660      * @param part
661      * the workbench part
662      * @return the saveable models
663      */

664     private Saveable[] getSaveables(IWorkbenchPart part) {
665         if (part instanceof ISaveablesSource) {
666             ISaveablesSource source = (ISaveablesSource) part;
667             return source.getSaveables();
668         } else if (part instanceof ISaveablePart) {
669             return new Saveable[] { new DefaultSaveable(part) };
670         } else {
671             return new Saveable[0];
672         }
673     }
674
675     /**
676      * @param actualPart
677      */

678     public void postOpen(IWorkbenchPart part) {
679         addModels(part, getSaveables(part));
680     }
681
682     /**
683      * @param actualPart
684      */

685     public void dirtyChanged(IWorkbenchPart part) {
686         Saveable[] saveables = getSaveables(part);
687         if (saveables.length > 0) {
688             fireModelLifecycleEvent(new SaveablesLifecycleEvent(this,
689                     SaveablesLifecycleEvent.DIRTY_CHANGED, saveables, false));
690         }
691     }
692
693     /**
694      * For testing purposes. Not to be called by clients.
695      *
696      * @param model
697      * @return
698      */

699     public Object JavaDoc[] testGetSourcesForModel(Saveable model) {
700         List JavaDoc result = new ArrayList JavaDoc();
701         for (Iterator JavaDoc it = modelMap.entrySet().iterator(); it.hasNext();) {
702             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
703             Set JavaDoc values = (Set JavaDoc) entry.getValue();
704             if (values.contains(model)) {
705                 result.add(entry.getKey());
706             }
707         }
708         return result.toArray();
709     }
710
711     private static final class MyListSelectionDialog extends
712             ListSelectionDialog {
713         private final boolean canCancel;
714         private Button checkbox;
715         private boolean dontPromptSelection;
716         private boolean stillOpenElsewhere;
717
718         private MyListSelectionDialog(Shell shell, Object JavaDoc input,
719                 IStructuredContentProvider contentprovider,
720                 ILabelProvider labelProvider, String JavaDoc message, boolean canCancel, boolean stillOpenElsewhere) {
721             super(shell, input, contentprovider, labelProvider, message);
722             this.canCancel = canCancel;
723             this.stillOpenElsewhere = stillOpenElsewhere;
724             if (!canCancel) {
725                 int shellStyle = getShellStyle();
726                 shellStyle &= ~SWT.CLOSE;
727                 setShellStyle(shellStyle);
728             }
729         }
730
731         /**
732          * @return
733          */

734         public boolean getDontPromptSelection() {
735             return dontPromptSelection;
736         }
737
738         protected void createButtonsForButtonBar(Composite parent) {
739             createButton(parent, IDialogConstants.OK_ID,
740                     IDialogConstants.OK_LABEL, true);
741             if (canCancel) {
742                 createButton(parent, IDialogConstants.CANCEL_ID,
743                         IDialogConstants.CANCEL_LABEL, false);
744             }
745         }
746         
747         protected Control createDialogArea(Composite parent) {
748              Composite dialogAreaComposite = (Composite) super.createDialogArea(parent);
749              
750              if (stillOpenElsewhere) {
751                  Composite checkboxComposite = new Composite(dialogAreaComposite, SWT.NONE);
752                  checkboxComposite.setLayout(new GridLayout(2, false));
753                  
754                  checkbox = new Button(checkboxComposite, SWT.CHECK);
755                  checkbox.addSelectionListener(new SelectionAdapter() {
756                     public void widgetSelected(SelectionEvent e) {
757                         dontPromptSelection = checkbox.getSelection();
758                     }
759                  });
760                  GridData gd = new GridData();
761                  gd.horizontalAlignment = SWT.BEGINNING;
762                  checkbox.setLayoutData(gd);
763                  
764                  Label label = new Label(checkboxComposite, SWT.NONE);
765                  label.setText(WorkbenchMessages.EditorManager_closeWithoutPromptingOption);
766                  gd = new GridData();
767                  gd.grabExcessHorizontalSpace = true;
768                  gd.horizontalAlignment = SWT.BEGINNING;
769              }
770              
771              return dialogAreaComposite;
772         }
773     }
774
775     /**
776      * @return a list of ISaveablesSource objects registered with this saveables
777      * list which are not workbench parts.
778      */

779     public ISaveablesSource[] getNonPartSources() {
780         return (ISaveablesSource[]) nonPartSources
781                 .toArray(new ISaveablesSource[nonPartSources.size()]);
782     }
783
784     /**
785      * @param model
786      */

787     public IWorkbenchPart[] getPartsForSaveable(Saveable model) {
788         List JavaDoc result = new ArrayList JavaDoc();
789         for (Iterator JavaDoc it = modelMap.entrySet().iterator(); it.hasNext();) {
790             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
791             Set JavaDoc values = (Set JavaDoc) entry.getValue();
792             if (values.contains(model) && entry.getKey() instanceof IWorkbenchPart) {
793                 result.add(entry.getKey());
794             }
795         }
796         return (IWorkbenchPart[]) result.toArray(new IWorkbenchPart[result.size()]);
797     }
798
799 }
800
Popular Tags