View | Details | Raw Unified | Return to bug 112225
Collapse All | Expand All

(-)Eclipse UI/org/eclipse/ui/internal/PartList.java (+5 lines)
Lines 14-19 Link Here
14
import org.eclipse.ui.IEditorPart;
14
import org.eclipse.ui.IEditorPart;
15
import org.eclipse.ui.IEditorReference;
15
import org.eclipse.ui.IEditorReference;
16
import org.eclipse.ui.IPropertyListener;
16
import org.eclipse.ui.IPropertyListener;
17
import org.eclipse.ui.ISaveableModelManager;
17
import org.eclipse.ui.IWorkbenchPart;
18
import org.eclipse.ui.IWorkbenchPart;
18
import org.eclipse.ui.IWorkbenchPartConstants;
19
import org.eclipse.ui.IWorkbenchPartConstants;
19
import org.eclipse.ui.IWorkbenchPartReference;
20
import org.eclipse.ui.IWorkbenchPartReference;
Lines 201-206 Link Here
201
        // open event was fired or that a closed editor was somehow activated)
202
        // open event was fired or that a closed editor was somehow activated)
202
        Assert.isTrue(activeEditorReference != ref);
203
        Assert.isTrue(activeEditorReference != ref);
203
        
204
        
205
        SaveableModelManager modelManager = (SaveableModelManager) actualPart
206
				.getSite().getService(ISaveableModelManager.class);
207
        modelManager.postOpen(actualPart);
208
        
204
        // Fire the "part opened" event
209
        // Fire the "part opened" event
205
        firePartOpened(ref);
210
        firePartOpened(ref);
206
    }
211
    }
(-)Eclipse UI/org/eclipse/ui/internal/WorkbenchPage.java (-22 / +43 lines)
Lines 69-74 Link Here
69
import org.eclipse.ui.IPerspectiveDescriptor;
69
import org.eclipse.ui.IPerspectiveDescriptor;
70
import org.eclipse.ui.IPerspectiveRegistry;
70
import org.eclipse.ui.IPerspectiveRegistry;
71
import org.eclipse.ui.IReusableEditor;
71
import org.eclipse.ui.IReusableEditor;
72
import org.eclipse.ui.ISaveableModelManager;
72
import org.eclipse.ui.ISaveablePart;
73
import org.eclipse.ui.ISaveablePart;
73
import org.eclipse.ui.ISelectionListener;
74
import org.eclipse.ui.ISelectionListener;
74
import org.eclipse.ui.IShowEditorInput;
75
import org.eclipse.ui.IShowEditorInput;
Lines 1217-1246 Link Here
1217
        
1218
        
1218
        IEditorReference[] editorRefs = (IEditorReference[]) toClose.toArray(new IEditorReference[toClose.size()]);
1219
        IEditorReference[] editorRefs = (IEditorReference[]) toClose.toArray(new IEditorReference[toClose.size()]);
1219
        
1220
        
1220
        if (save) { 
1221
        // notify the model manager before the close
1221
            // Intersect the dirty editors with the editors that are closing
1222
        List partsToClose = new ArrayList();
1222
            IEditorPart[] dirty = getDirtyEditors();
1223
        for (int i = 0; i < editorRefs.length; i++) {
1223
            List intersect = new ArrayList();
1224
            IEditorPart refPart = editorRefs[i].getEditor(false);
1224
            for (int i = 0; i < editorRefs.length; i++) {
1225
            if (refPart != null) {
1225
                IEditorReference reference = editorRefs[i];
1226
            	partsToClose.add(refPart);
1226
                IEditorPart refPart = reference.getEditor(false);
1227
                if (refPart != null) {
1228
                    for (int j = 0; j < dirty.length; j++) {
1229
                        if (refPart.equals(dirty[j]) && refPart.isSaveOnCloseNeeded()) {
1230
                            intersect.add(refPart);
1231
                            break;
1232
                        }
1233
                    }
1234
                }
1235
            }
1236
            // Save parts, exit the method if cancel is pressed.
1237
            if (intersect.size() > 0) {
1238
                if (!EditorManager.saveAll(intersect, true, true,
1239
                        getWorkbenchWindow())) {
1240
					return false;
1241
				}
1242
            }
1227
            }
1243
        }
1228
        }
1229
        SaveableModelManager modelManager = null;
1230
        Object postCloseInfo = null;
1231
        if(partsToClose.size()>0) {
1232
        	modelManager = (SaveableModelManager) getWorkbenchWindow().getService(ISaveableModelManager.class);
1233
        	// this may prompt for saving and return null if the user canceled:
1234
        	postCloseInfo = modelManager.preCloseParts(partsToClose, save, getWorkbenchWindow());
1235
        	if (postCloseInfo==null) {
1236
        		return false;
1237
        	}
1238
        }
1244
1239
1245
        // Fire pre-removal changes 
1240
        // Fire pre-removal changes 
1246
        for (int i = 0; i < editorRefs.length; i++) {
1241
        for (int i = 0; i < editorRefs.length; i++) {
Lines 1270-1275 Link Here
1270
        // Notify interested listeners after the close
1265
        // Notify interested listeners after the close
1271
        window.firePerspectiveChanged(this, getPerspective(),
1266
        window.firePerspectiveChanged(this, getPerspective(),
1272
                CHANGE_EDITOR_CLOSE);
1267
                CHANGE_EDITOR_CLOSE);
1268
        
1269
        if(modelManager!=null) {
1270
        	modelManager.postClose(postCloseInfo);
1271
        }
1273
1272
1274
        // Return true on success.
1273
        // Return true on success.
1275
        return true;
1274
        return true;
Lines 2113-2118 Link Here
2113
			}
2112
			}
2114
        }
2113
        }
2115
        
2114
        
2115
        int refCount = getViewFactory().getReferenceCount(ref);
2116
        SaveableModelManager saveableModelManager = null;
2117
        Object postCloseInfo = null;
2118
        if (refCount == 1) {
2119
        	IWorkbenchPart actualPart = ref.getPart(false);
2120
        	if (actualPart != null) {
2121
				saveableModelManager = (SaveableModelManager) actualPart
2122
						.getSite().getService(ISaveableModelManager.class);
2123
				postCloseInfo = saveableModelManager.preCloseParts(Collections
2124
						.singletonList(actualPart), true, this
2125
						.getWorkbenchWindow());
2126
				if (postCloseInfo==null) {
2127
					// cancel
2128
					return;
2129
				}
2130
			}
2131
        }
2132
        
2116
        // Notify interested listeners before the hide
2133
        // Notify interested listeners before the hide
2117
        window.firePerspectiveChanged(this, persp.getDesc(), ref,
2134
        window.firePerspectiveChanged(this, persp.getDesc(), ref,
2118
                CHANGE_VIEW_HIDE);
2135
                CHANGE_VIEW_HIDE);
Lines 2127-2132 Link Here
2127
2144
2128
        // Notify interested listeners after the hide
2145
        // Notify interested listeners after the hide
2129
        window.firePerspectiveChanged(this, getPerspective(), CHANGE_VIEW_HIDE);
2146
        window.firePerspectiveChanged(this, getPerspective(), CHANGE_VIEW_HIDE);
2147
        
2148
        if (saveableModelManager != null) {
2149
        	saveableModelManager.postClose(postCloseInfo);
2150
        }
2130
    }
2151
    }
2131
2152
2132
    /* package */void refreshActiveView() {
2153
    /* package */void refreshActiveView() {
(-)Eclipse UI/org/eclipse/ui/internal/ViewFactory.java (+11 lines)
Lines 225-230 Link Here
225
    }
225
    }
226
226
227
    /**
227
    /**
228
     * 
229
     * @param viewRef
230
     * @return the current reference count for the given view
231
     */
232
    public int getReferenceCount(IViewReference viewRef) {
233
        String key = getKey(viewRef);
234
        IViewReference ref = (IViewReference) counter.get(key);
235
        return ref==null ? 0 : counter.getRef(key);
236
    }
237
    
238
    /**
228
     * Releases an instance of a view.
239
     * Releases an instance of a view.
229
     *
240
     *
230
     * This factory does reference counting.  For more info see
241
     * This factory does reference counting.  For more info see
(-)Eclipse UI/org/eclipse/ui/internal/Workbench.java (-2 / +7 lines)
Lines 85-90 Link Here
85
import org.eclipse.ui.IMemento;
85
import org.eclipse.ui.IMemento;
86
import org.eclipse.ui.IPerspectiveDescriptor;
86
import org.eclipse.ui.IPerspectiveDescriptor;
87
import org.eclipse.ui.IPerspectiveRegistry;
87
import org.eclipse.ui.IPerspectiveRegistry;
88
import org.eclipse.ui.ISaveableModelManager;
88
import org.eclipse.ui.ISaveablePart;
89
import org.eclipse.ui.ISaveablePart;
89
import org.eclipse.ui.ISharedImages;
90
import org.eclipse.ui.ISharedImages;
90
import org.eclipse.ui.IWindowListener;
91
import org.eclipse.ui.IWindowListener;
Lines 1210-1219 Link Here
1210
	}
1211
	}
1211
1212
1212
	/**
1213
	/**
1213
	 * Initializes all of the default command-based services for the workbench.
1214
	 * Initializes all of the default services for the workbench. For
1214
	 * This also parses the registry and hooks up all the required listeners.
1215
	 * initializing the command-based services, this also parses the registry
1216
	 * and hooks up all the required listeners.
1215
	 */
1217
	 */
1216
	private final void initializeDefaultServices() {
1218
	private final void initializeDefaultServices() {
1219
		
1220
		serviceLocator.registerService(ISaveableModelManager.class, new SaveableModelManager());
1221
		
1217
		/*
1222
		/*
1218
		 * Phase 1 of the initialization of commands. When this phase completes,
1223
		 * Phase 1 of the initialization of commands. When this phase completes,
1219
		 * all the services and managers will exist, and be accessible via the
1224
		 * all the services and managers will exist, and be accessible via the
(-)Eclipse UI/org/eclipse/ui/internal/EditorManager.java (-2 / +2 lines)
Lines 129-137 Link Here
129
	// Handler for the pin editor keyboard shortcut
129
	// Handler for the pin editor keyboard shortcut
130
	private IHandlerActivation pinEditorHandlerActivation = null;
130
	private IHandlerActivation pinEditorHandlerActivation = null;
131
131
132
	private static final String RESOURCES_TO_SAVE_MESSAGE = WorkbenchMessages.EditorManager_saveResourcesMessage;
132
	static final String RESOURCES_TO_SAVE_MESSAGE = WorkbenchMessages.EditorManager_saveResourcesMessage;
133
133
134
	private static final String SAVE_RESOURCES_TITLE = WorkbenchMessages.EditorManager_saveResourcesTitle;
134
	static final String SAVE_RESOURCES_TITLE = WorkbenchMessages.EditorManager_saveResourcesTitle;
135
135
136
	/**
136
	/**
137
	 * EditorManager constructor comment.
137
	 * EditorManager constructor comment.
(-)Eclipse UI/org/eclipse/ui/internal/DefaultSaveableModel.java (+26 lines)
Lines 97-100 Link Here
97
		return false;
97
		return false;
98
	}
98
	}
99
99
100
	/* (non-Javadoc)
101
	 * @see java.lang.Object#hashCode()
102
	 */
103
	public int hashCode() {
104
		return part.hashCode();
105
	}
106
107
	/* (non-Javadoc)
108
	 * @see java.lang.Object#equals(java.lang.Object)
109
	 */
110
	public boolean equals(Object obj) {
111
		if (this == obj)
112
			return true;
113
		if (obj == null)
114
			return false;
115
		if (getClass() != obj.getClass())
116
			return false;
117
		final DefaultSaveableModel other = (DefaultSaveableModel) obj;
118
		if (part == null) {
119
			if (other.part != null)
120
				return false;
121
		} else if (!part.equals(other.part))
122
			return false;
123
		return true;
124
	}
125
100
}
126
}
(-)Eclipse UI/org/eclipse/ui/internal/WorkbenchPartReference.java (-1 / +10 lines)
Lines 24-29 Link Here
24
import org.eclipse.swt.widgets.Control;
24
import org.eclipse.swt.widgets.Control;
25
import org.eclipse.swt.widgets.Display;
25
import org.eclipse.swt.widgets.Display;
26
import org.eclipse.ui.IPropertyListener;
26
import org.eclipse.ui.IPropertyListener;
27
import org.eclipse.ui.ISaveableModelManager;
27
import org.eclipse.ui.ISaveablePart;
28
import org.eclipse.ui.ISaveablePart;
28
import org.eclipse.ui.ISharedImages;
29
import org.eclipse.ui.ISharedImages;
29
import org.eclipse.ui.IWorkbenchPart;
30
import org.eclipse.ui.IWorkbenchPart;
Lines 272-278 Link Here
272
            // Any other properties are just reported to listeners verbatim
273
            // Any other properties are just reported to listeners verbatim
273
            firePropertyChange(propId);
274
            firePropertyChange(propId);
274
        }
275
        }
275
276
        
277
        // Let the model manager know as well
278
        if (propId == IWorkbenchPartConstants.PROP_DIRTY) {
279
        	IWorkbenchPart actualPart = getPart(false);
280
        	if (actualPart != null) {
281
				SaveableModelManager modelManager = (SaveableModelManager) actualPart.getSite().getService(ISaveableModelManager.class);
282
	        	modelManager.dirtyChanged(actualPart);
283
        	}
284
        }
276
    }
285
    }
277
286
278
    /**
287
    /**
(-)Eclipse UI/org/eclipse/ui/internal/ReferenceCounter.java (-10 / +22 lines)
Lines 127-132 Link Here
127
    }
127
    }
128
128
129
    /**
129
    /**
130
     * @param id is a unique ID for the object.
131
     * @return the current ref count
132
     */
133
    public int getRef(Object id) {
134
        RefRec rec = (RefRec) mapIdToRec.get(id);
135
        if (rec == null) {
136
			return 0;
137
		}
138
        return rec.refCount;
139
    }
140
141
    /**
130
     * Removes one reference from an object in the counter.
142
     * Removes one reference from an object in the counter.
131
     * If the ref count drops to 0 the object is removed from
143
     * If the ref count drops to 0 the object is removed from
132
     * the counter completely.
144
     * the counter completely.
Lines 135-151 Link Here
135
     * @return the new ref count
147
     * @return the new ref count
136
     */
148
     */
137
    public int removeRef(Object id) {
149
    public int removeRef(Object id) {
138
        RefRec rec = (RefRec) mapIdToRec.get(id);
150
    	RefRec rec = (RefRec) mapIdToRec.get(id);
139
        if (rec == null) {
151
    	if (rec == null) {
140
			return 0;
152
    		return 0;
141
		}
153
    	}
142
        int newCount = rec.removeRef();
154
    	int newCount = rec.removeRef();
143
        if (newCount <= 0) {
155
    	if (newCount <= 0) {
144
			mapIdToRec.remove(id);
156
    		mapIdToRec.remove(id);
145
		}
157
    	}
146
        return newCount;
158
    	return newCount;
147
    }
159
    }
148
160
    
149
    /**
161
    /**
150
     * Returns a complete list of the values in the counter.
162
     * Returns a complete list of the values in the counter.
151
     *
163
     *
(-)Eclipse UI/org/eclipse/ui/ISaveableModelSource.java (-2 / +12 lines)
Lines 21-29 Link Here
21
public interface ISaveableModelSource {
21
public interface ISaveableModelSource {
22
22
23
	/**
23
	/**
24
	 * Returns the saveable models presented by the workbench part.
24
	 * Returns the saveable models presented by the workbench part. If the
25
	 * return value of this method changes during the lifetime of this part, the
26
	 * model manager must be notified about these changes by calling
27
	 * {@link ISaveableModelManager#handleModelLifecycleEvent(ModelLifecycleEvent)}.
28
	 * <p>
29
	 * The model manager is available as a service from the part site, by
30
	 * calling <code>partSite.getService(ISaveableModelManager.class)</code>.
31
	 * </p>
25
	 * 
32
	 * 
26
	 * @return the saveable models presented by the workbench part
33
	 * @return the saveable models presented by the workbench part
34
	 * 
35
	 * @see ISaveableModelManager
27
	 */
36
	 */
28
	ISaveableModel[] getModels();
37
	ISaveableModel[] getModels();
29
38
Lines 31-37 Link Here
31
	 * Returns the saveable models currently active in the workbench part.
40
	 * Returns the saveable models currently active in the workbench part.
32
	 * <p>
41
	 * <p>
33
	 * Certain workbench actions, such as Save, target only the active models in
42
	 * Certain workbench actions, such as Save, target only the active models in
34
	 * the active part.
43
	 * the active part. For example, the active saveable models could be
44
	 * determined based on the current selection in the part.
35
	 * </p>
45
	 * </p>
36
	 * 
46
	 * 
37
	 * @return the saveable models currently active in the workbench part
47
	 * @return the saveable models currently active in the workbench part
(-)Eclipse (+135 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 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
 ******************************************************************************/
11
12
package org.eclipse.ui;
13
14
import java.util.EventObject;
15
16
17
/**
18
 * Event object describing a change to a set of ISaveableModel objects.
19
 * 
20
 * @since 3.2
21
 */
22
public class ModelLifecycleEvent extends EventObject {
23
24
	/**
25
	 * Serial version UID for this class.
26
	 * <p>
27
	 * Note: This class is not intended to be serialized.
28
	 * </p>
29
	 */
30
	private static final long serialVersionUID = -3530773637989046452L;
31
32
	/**
33
	 * Event type constant specifying that the given models have been opened.
34
	 */
35
	public static final int POST_OPEN = 1;
36
37
	/**
38
	 * Event type constant specifying that the given models are about to be
39
	 * closed. Listeners may veto the closing if isForce() is false.
40
	 */
41
	public static final int PRE_CLOSE = 2;
42
43
	/**
44
	 * Event type constant specifying that the given models have been closed.
45
	 */
46
	public static final int POST_CLOSE = 3;
47
48
	/**
49
	 * Event type constant specifying that the dirty state of the given models
50
	 * has changed.
51
	 */
52
	public static final int DIRTY_CHANGED = 4;
53
54
	private int eventType;
55
56
	private ISaveableModel[] models;
57
58
	private boolean force;
59
60
	private boolean veto = false;
61
62
	/**
63
	 * Creates a new ModelLifecycleEvent.
64
	 * 
65
	 * @param source
66
	 *            The source of the event. If an ISaveableModelSource notifies
67
	 *            about changes to the models returned by
68
	 *            {@link ISaveableModelSource#getModels()}, the source must be
69
	 *            the ISaveableModelSource object.
70
	 * @param eventType
71
	 *            the event type, currently one of POST_OPEN, PRE_CLOSE,
72
	 *            POST_CLOSE, DIRTY_CHANGED
73
	 * @param models
74
	 *            The affected models
75
	 * @param force
76
	 *            true if the event type is PRE_CLOSE and this is a closed force
77
	 *            that cannot be canceled.
78
	 */
79
	public ModelLifecycleEvent(Object source, int eventType,
80
			ISaveableModel[] models, boolean force) {
81
		super(source);
82
		this.eventType = eventType;
83
		this.models = models;
84
		this.force = force;
85
	}
86
87
	/**
88
	 * Returns the eventType, currently one of POST_OPEN, PRE_CLOSE, POST_CLOSE,
89
	 * DIRTY_CHANGED. Listeners should silently ignore unknown event types since
90
	 * new event types might be added in the future.
91
	 * 
92
	 * @return the eventType
93
	 */
94
	public int getEventType() {
95
		return eventType;
96
	}
97
98
	/**
99
	 * Returns the affected models.
100
	 * 
101
	 * @return the models
102
	 */
103
	public ISaveableModel[] getModels() {
104
		return models;
105
	}
106
107
	/**
108
	 * Returns the veto. This value is ignored for POST_OPEN,POST_CLOSE, and
109
	 * DIRTY_CHANGED.
110
	 * 
111
	 * @return Returns the veto.
112
	 */
113
	public boolean isVeto() {
114
		return veto;
115
	}
116
117
	/**
118
	 * @param veto
119
	 *            The veto to set.
120
	 */
121
	public void setVeto(boolean veto) {
122
		this.veto = veto;
123
	}
124
125
	/**
126
	 * Sets the veto. This value is ignored for POST_OPEN, POST_CLOSE, and
127
	 * DIRTY_CHANGED.
128
	 * 
129
	 * @return Returns the force.
130
	 */
131
	public boolean isForce() {
132
		return force;
133
	}
134
135
}
(-)Eclipse (+527 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 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
 ******************************************************************************/
11
12
package org.eclipse.ui.internal;
13
14
import java.util.ArrayList;
15
import java.util.Arrays;
16
import java.util.HashMap;
17
import java.util.HashSet;
18
import java.util.Iterator;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Set;
22
23
import org.eclipse.core.runtime.Assert;
24
import org.eclipse.core.runtime.CoreException;
25
import org.eclipse.core.runtime.IProgressMonitor;
26
import org.eclipse.core.runtime.ListenerList;
27
import org.eclipse.core.runtime.SubProgressMonitor;
28
import org.eclipse.jface.dialogs.ErrorDialog;
29
import org.eclipse.jface.dialogs.IDialogConstants;
30
import org.eclipse.jface.dialogs.MessageDialog;
31
import org.eclipse.jface.operation.IRunnableWithProgress;
32
import org.eclipse.jface.viewers.ArrayContentProvider;
33
import org.eclipse.jface.viewers.ILabelProvider;
34
import org.eclipse.jface.viewers.IStructuredContentProvider;
35
import org.eclipse.osgi.util.NLS;
36
import org.eclipse.swt.SWT;
37
import org.eclipse.swt.widgets.Composite;
38
import org.eclipse.swt.widgets.Shell;
39
import org.eclipse.ui.IModelLifecycleListener;
40
import org.eclipse.ui.ISaveableModel;
41
import org.eclipse.ui.ISaveableModelManager;
42
import org.eclipse.ui.ISaveableModelSource;
43
import org.eclipse.ui.ISaveablePart;
44
import org.eclipse.ui.ISaveablePart2;
45
import org.eclipse.ui.IWorkbenchPart;
46
import org.eclipse.ui.IWorkbenchWindow;
47
import org.eclipse.ui.ModelLifecycleEvent;
48
import org.eclipse.ui.PlatformUI;
49
import org.eclipse.ui.dialogs.ListSelectionDialog;
50
import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
51
import org.eclipse.ui.model.WorkbenchPartLabelProvider;
52
53
/**
54
 * @since 3.2
55
 * 
56
 */
57
public class SaveableModelManager implements ISaveableModelManager {
58
59
	private ListenerList listeners = new ListenerList();
60
61
	// event source (mostly ISaveableModelSource) -> Set of ISaveableModel
62
	private Map modelMap = new HashMap();
63
64
	// reference counting map, ISaveableModel -> Integer
65
	private Map modelRefCounts = new HashMap();
66
67
	public ISaveableModel[] getOpenModels() {
68
		return (ISaveableModel[]) modelRefCounts.keySet().toArray(
69
				new ISaveableModel[modelRefCounts.size()]);
70
	}
71
72
	// returns true if this model has not yet been in getModels()
73
	private boolean addModel(Object source, ISaveableModel model) {
74
		boolean result = false;
75
		Set modelsForSource = (Set) modelMap.get(source);
76
		if (modelsForSource == null) {
77
			modelsForSource = new HashSet();
78
			modelMap.put(source, modelsForSource);
79
		}
80
		if (modelsForSource.add(model)) {
81
			result = incrementRefCount(modelRefCounts, model);
82
		}
83
		return result;
84
	}
85
86
	/**
87
	 * returns true if the given key was added for the first time
88
	 * 
89
	 * @param referenceMap
90
	 * @param key
91
	 * @return true if the ref count of the given key is now 1
92
	 */
93
	private boolean incrementRefCount(Map referenceMap, Object key) {
94
		boolean result = false;
95
		Integer refCount = (Integer) referenceMap.get(key);
96
		if (refCount == null) {
97
			result = true;
98
			refCount = new Integer(0);
99
		}
100
		referenceMap.put(key, new Integer(refCount.intValue() + 1));
101
		return result;
102
	}
103
104
	/**
105
	 * returns true if the given key has been removed
106
	 * 
107
	 * @param referenceMap
108
	 * @param key
109
	 * @return true if the ref count of the given key was 1
110
	 */
111
	private boolean decrementRefCount(Map referenceMap, Object key) {
112
		boolean result = false;
113
		Integer refCount = (Integer) referenceMap.get(key);
114
		Assert.isTrue(refCount != null);
115
		if (refCount.intValue() == 1) {
116
			referenceMap.remove(key);
117
			result = true;
118
		} else {
119
			referenceMap.put(key, new Integer(refCount.intValue() - 1));
120
		}
121
		return result;
122
	}
123
124
	// returns true if this model was removed from getModels();
125
	private boolean removeModel(Object source, ISaveableModel model) {
126
		boolean result = false;
127
		Set modelsForSource = (Set) modelMap.get(source);
128
		if (modelsForSource == null) {
129
			modelsForSource = new HashSet();
130
			modelMap.put(source, modelsForSource);
131
		}
132
		if (modelsForSource.remove(model)) {
133
			result = decrementRefCount(modelRefCounts, model);
134
			if (modelsForSource.isEmpty()) {
135
				modelMap.remove(source);
136
			}
137
		}
138
		return result;
139
	}
140
141
	public void handleModelLifecycleEvent(ModelLifecycleEvent event) {
142
		ISaveableModel[] modelArray = event.getModels();
143
		switch (event.getEventType()) {
144
		case ModelLifecycleEvent.POST_OPEN:
145
			addModels(event.getSource(), modelArray);
146
			break;
147
		case ModelLifecycleEvent.PRE_CLOSE:
148
			ISaveableModel[] models = event.getModels();
149
			Map modelsDecrementing = new HashMap();
150
			Set modelsClosing = new HashSet();
151
			for (int i = 0; i < models.length; i++) {
152
				incrementRefCount(modelsDecrementing, models[i]);
153
			}
154
155
			fillModelsClosing(modelsClosing, modelsDecrementing);
156
			boolean canceled = promptForSavingIfNecessary(PlatformUI
157
					.getWorkbench().getActiveWorkbenchWindow(), modelsClosing,
158
					!event.isForce());
159
			if (canceled) {
160
				event.setVeto(true);
161
			}
162
			break;
163
		case ModelLifecycleEvent.POST_CLOSE:
164
			removeModels(event.getSource(), modelArray);
165
			break;
166
		case ModelLifecycleEvent.DIRTY_CHANGED:
167
			fireModelLifecycleEvent(new ModelLifecycleEvent(this, event
168
					.getEventType(), event.getModels(), false));
169
			break;
170
		}
171
	}
172
173
	/**
174
	 * @param source
175
	 * @param modelArray
176
	 */
177
	private void removeModels(Object source, ISaveableModel[] modelArray) {
178
		List removed = new ArrayList();
179
		for (int i = 0; i < modelArray.length; i++) {
180
			ISaveableModel model = modelArray[i];
181
			if (removeModel(source, model)) {
182
				removed.add(model);
183
			}
184
		}
185
		if (removed.size() > 0) {
186
			fireModelLifecycleEvent(new ModelLifecycleEvent(this,
187
					ModelLifecycleEvent.POST_OPEN, (ISaveableModel[]) removed
188
							.toArray(new ISaveableModel[removed.size()]), false));
189
		}
190
	}
191
192
	/**
193
	 * @param source
194
	 * @param modelArray
195
	 */
196
	private void addModels(Object source, ISaveableModel[] modelArray) {
197
		List added = new ArrayList();
198
		for (int i = 0; i < modelArray.length; i++) {
199
			ISaveableModel model = modelArray[i];
200
			if (addModel(source, model)) {
201
				added.add(model);
202
			}
203
		}
204
		if (added.size() > 0) {
205
			fireModelLifecycleEvent(new ModelLifecycleEvent(this,
206
					ModelLifecycleEvent.POST_OPEN, (ISaveableModel[]) added
207
							.toArray(new ISaveableModel[added.size()]), false));
208
		}
209
	}
210
211
	/**
212
	 * @param event
213
	 */
214
	private void fireModelLifecycleEvent(ModelLifecycleEvent event) {
215
		Object[] listenerArray = listeners.getListeners();
216
		for (int i = 0; i < listenerArray.length; i++) {
217
			((IModelLifecycleListener) listenerArray[i])
218
					.handleModelLifecycleEvent(event);
219
		}
220
	}
221
222
	public void addModelLifecycleListener(IModelLifecycleListener listener) {
223
		listeners.add(listener);
224
	}
225
226
	public void removeModelLifecycleListener(IModelLifecycleListener listener) {
227
		listeners.remove(listener);
228
	}
229
230
	/**
231
	 * @param editorsToClose
232
	 * @param save
233
	 * @param window
234
	 * @return the post close info to be passed to postClose
235
	 */
236
	public Object preCloseParts(List editorsToClose, boolean save,
237
			final IWorkbenchWindow window) {
238
		// reference count (how many occurrences of a model will go away?)
239
		PostCloseInfo postCloseInfo = new PostCloseInfo();
240
		for (Iterator it = editorsToClose.iterator(); it.hasNext();) {
241
			IWorkbenchPart part = (IWorkbenchPart) it.next();
242
			postCloseInfo.partsClosing.add(part);
243
			if (part instanceof ISaveablePart) {
244
				ISaveablePart saveablePart = (ISaveablePart) part;
245
				if (save && !saveablePart.isSaveOnCloseNeeded()) {
246
					// pretend for now that this part is not closing
247
					continue;
248
				}
249
			}
250
			if (save && part instanceof ISaveablePart2) {
251
				ISaveablePart2 saveablePart2 = (ISaveablePart2) part;
252
				// TODO show saveablePart2 before prompting, see
253
				// EditorManager.saveAll
254
				int response = SaveableHelper.savePart(saveablePart2, window,
255
						true);
256
				// only include this part in the following logic if it returned
257
				// DEFAULT
258
				if (response != ISaveablePart2.DEFAULT) {
259
					continue;
260
				}
261
			}
262
			ISaveableModel[] modelsFromSource = getSaveableModels(part);
263
			for (int i = 0; i < modelsFromSource.length; i++) {
264
				incrementRefCount(postCloseInfo.modelsDecrementing,
265
						modelsFromSource[i]);
266
			}
267
		}
268
		fillModelsClosing(postCloseInfo.modelsClosing,
269
				postCloseInfo.modelsDecrementing);
270
		if (save) {
271
			boolean canceled = promptForSavingIfNecessary(window,
272
					postCloseInfo.modelsClosing, true);
273
			if (canceled) {
274
				return null;
275
			}
276
		}
277
		return postCloseInfo;
278
	}
279
280
	/**
281
	 * @param window
282
	 * @param modelsClosing
283
	 * @param canCancel
284
	 * @return true if the user canceled
285
	 */
286
	private boolean promptForSavingIfNecessary(final IWorkbenchWindow window,
287
			Set modelsClosing, boolean canCancel) {
288
		// TODO prompt for saving of dirty modelsDecrementing but not closing
289
		// (changes
290
		// won't be lost)
291
292
		List modelsToSave = new ArrayList();
293
		for (Iterator it = modelsClosing.iterator(); it.hasNext();) {
294
			ISaveableModel modelClosing = (ISaveableModel) it.next();
295
			if (modelClosing.isDirty()) {
296
				modelsToSave.add(modelClosing);
297
			}
298
		}
299
		return modelsToSave.isEmpty() ? false : promptForSaving(modelsToSave,
300
				window, canCancel);
301
	}
302
303
	/**
304
	 * @param modelsClosing
305
	 * @param modelsDecrementing
306
	 */
307
	private void fillModelsClosing(Set modelsClosing, Map modelsDecrementing) {
308
		for (Iterator it = modelsDecrementing.keySet().iterator(); it.hasNext();) {
309
			ISaveableModel model = (ISaveableModel) it.next();
310
			if (modelsDecrementing.get(model).equals(modelRefCounts.get(model))) {
311
				modelsClosing.add(model);
312
			}
313
		}
314
	}
315
316
	/**
317
	 * @param modelsToSave
318
	 * @param window
319
	 * @param canCancel
320
	 * @return true if the user canceled
321
	 */
322
	private boolean promptForSaving(List modelsToSave,
323
			final IWorkbenchWindow window, boolean canCancel) {
324
		// Save parts, exit the method if cancel is pressed.
325
		if (modelsToSave.size() > 0) {
326
			if (modelsToSave.size() == 1) {
327
				ISaveableModel model = (ISaveableModel) modelsToSave.get(0);
328
				String message = NLS.bind(
329
						WorkbenchMessages.EditorManager_saveChangesQuestion,
330
						model.getName());
331
				// Show a dialog.
332
				String[] buttons = new String[] { IDialogConstants.YES_LABEL,
333
						IDialogConstants.NO_LABEL,
334
						IDialogConstants.CANCEL_LABEL };
335
				MessageDialog d = new MessageDialog(window.getShell(),
336
						WorkbenchMessages.Save_Resource, null, message,
337
						MessageDialog.QUESTION, buttons, 0);
338
339
				int choice = SaveableHelper.testGetAutomatedResponse();
340
				if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
341
					choice = d.open();
342
				}
343
344
				// Branch on the user choice.
345
				// The choice id is based on the order of button labels
346
				// above.
347
				switch (choice) {
348
				case ISaveablePart2.YES: // yes
349
					break;
350
				case ISaveablePart2.NO: // no
351
					modelsToSave.clear();
352
					break;
353
				default:
354
				case ISaveablePart2.CANCEL: // cancel
355
					return true;
356
				}
357
			} else {
358
				ListSelectionDialog dlg = new MyListSelectionDialog(window
359
						.getShell(), modelsToSave, new ArrayContentProvider(),
360
						new WorkbenchPartLabelProvider(),
361
						EditorManager.RESOURCES_TO_SAVE_MESSAGE, canCancel);
362
				dlg.setInitialSelections(modelsToSave.toArray());
363
				dlg.setTitle(EditorManager.SAVE_RESOURCES_TITLE);
364
365
				// this "if" statement aids in testing.
366
				if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
367
					int result = dlg.open();
368
					// Just return null to prevent the operation continuing
369
					if (result == IDialogConstants.CANCEL_ID)
370
						return true;
371
372
					modelsToSave = Arrays.asList(dlg.getResult());
373
				}
374
			}
375
		}
376
		// Create save block.
377
		final List finalModels = modelsToSave;
378
		IRunnableWithProgress progressOp = new IRunnableWithProgress() {
379
			public void run(IProgressMonitor monitor) {
380
				IProgressMonitor monitorWrap = new EventLoopProgressMonitor(
381
						monitor);
382
				monitorWrap.beginTask("", finalModels.size()); //$NON-NLS-1$
383
				for (Iterator i = finalModels.iterator(); i.hasNext();) {
384
					ISaveableModel model = (ISaveableModel) i.next();
385
					// handle case where this model got saved as a result of
386
					// saving another
387
					if (!model.isDirty()) {
388
						monitor.worked(1);
389
						continue;
390
					}
391
					try {
392
						model.doSave(new SubProgressMonitor(monitorWrap, 1));
393
					} catch (CoreException e) {
394
						ErrorDialog.openError(window.getShell(),
395
								WorkbenchMessages.Error, e.getMessage(), e
396
										.getStatus());
397
					}
398
					if (monitorWrap.isCanceled())
399
						break;
400
				}
401
				monitorWrap.done();
402
			}
403
		};
404
405
		// Do the save.
406
		if (!SaveableHelper.runProgressMonitorOperation(
407
				WorkbenchMessages.Save_All, progressOp, window)) {
408
			// cancelled
409
			return true;
410
		}
411
		return false;
412
	}
413
414
	private static class PostCloseInfo {
415
		private List partsClosing = new ArrayList();
416
417
		private Map modelsDecrementing = new HashMap();
418
419
		private Set modelsClosing = new HashSet();
420
	}
421
422
	/**
423
	 * @param postCloseInfoObject
424
	 */
425
	public void postClose(Object postCloseInfoObject) {
426
		PostCloseInfo postCloseInfo = (PostCloseInfo) postCloseInfoObject;
427
		List removed = new ArrayList();
428
		for (Iterator it = postCloseInfo.partsClosing.iterator(); it.hasNext();) {
429
			IWorkbenchPart part = (IWorkbenchPart) it.next();
430
			ISaveableModel[] modelArray = getSaveableModels(part);
431
			for (int i = 0; i < modelArray.length; i++) {
432
				ISaveableModel model = modelArray[i];
433
				if (removeModel(part, model)) {
434
					removed.add(model);
435
				}
436
			}
437
		}
438
		if (removed.size() > 0) {
439
			fireModelLifecycleEvent(new ModelLifecycleEvent(this,
440
					ModelLifecycleEvent.POST_CLOSE, (ISaveableModel[]) removed
441
							.toArray(new ISaveableModel[removed.size()]), false));
442
		}
443
	}
444
445
	/**
446
	 * Returns the saveable models provided by the given part. If the part does
447
	 * not provide any models, a default model is returned representing the
448
	 * part.
449
	 * 
450
	 * @param part
451
	 *            the workbench part
452
	 * @return the saveable models
453
	 */
454
	private ISaveableModel[] getSaveableModels(IWorkbenchPart part) {
455
		if (part instanceof ISaveableModelSource) {
456
			ISaveableModelSource source = (ISaveableModelSource) part;
457
			return source.getModels();
458
		} else if (part instanceof ISaveablePart) {
459
			return new ISaveableModel[] { new DefaultSaveableModel(part) };
460
		} else {
461
			return new ISaveableModel[0];
462
		}
463
	}
464
465
	/**
466
	 * @param actualPart
467
	 */
468
	public void postOpen(IWorkbenchPart part) {
469
		addModels(part, getSaveableModels(part));
470
	}
471
472
	/**
473
	 * @param actualPart
474
	 */
475
	public void dirtyChanged(IWorkbenchPart part) {
476
		ISaveableModel[] saveableModels = getSaveableModels(part);
477
		if (saveableModels.length > 0) {
478
			fireModelLifecycleEvent(new ModelLifecycleEvent(this,
479
					ModelLifecycleEvent.DIRTY_CHANGED, saveableModels, false));
480
		}
481
	}
482
483
	/**
484
	 * For testing purposes. Not to be called by clients.
485
	 * 
486
	 * @param model
487
	 * @return
488
	 */
489
	public Object[] testGetSourcesForModel(ISaveableModel model) {
490
		List result = new ArrayList();
491
		for (Iterator it = modelMap.entrySet().iterator(); it.hasNext();) {
492
			Map.Entry entry = (Map.Entry) it.next();
493
			Set values = (Set) entry.getValue();
494
			if (values.contains(model)) {
495
				result.add(entry.getKey());
496
			}
497
		}
498
		return result.toArray();
499
	}
500
501
	private static final class MyListSelectionDialog extends
502
			ListSelectionDialog {
503
		private final boolean canCancel;
504
505
		private MyListSelectionDialog(Shell shell, Object input,
506
				IStructuredContentProvider contentprovider,
507
				ILabelProvider labelProvider, String message, boolean canCancel) {
508
			super(shell, input, contentprovider, labelProvider, message);
509
			this.canCancel = canCancel;
510
			if (!canCancel) {
511
				int shellStyle = getShellStyle();
512
				shellStyle &= ~SWT.CLOSE;
513
				setShellStyle(shellStyle);
514
			}
515
		}
516
517
		protected void createButtonsForButtonBar(Composite parent) {
518
			createButton(parent, IDialogConstants.OK_ID,
519
					IDialogConstants.OK_LABEL, true);
520
			if (canCancel) {
521
				createButton(parent, IDialogConstants.CANCEL_ID,
522
						IDialogConstants.CANCEL_LABEL, false);
523
			}
524
		}
525
	}
526
527
}
(-)Eclipse (+77 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 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
 ******************************************************************************/
11
12
package org.eclipse.ui;
13
14
15
/**
16
 * The model manager maintains a list of open saveable models.
17
 * 
18
 * @see SaveableModel
19
 * @see ISaveableModelSource
20
 * 
21
 * @since 3.2
22
 */
23
public interface ISaveableModelManager extends IModelLifecycleListener {
24
25
	/**
26
	 * Returns the list of open models managed by this model manager.
27
	 * 
28
	 * @return a list of models
29
	 */
30
	public ISaveableModel[] getOpenModels();
31
32
	/**
33
	 * This implementation of handleModelLifecycleEvent must be called by
34
	 * implementers of ISaveableModelSource whenever the list of models of the
35
	 * model source changes, or when the dirty state of models changes. The
36
	 * ISaveableModelSource instance must be passed as the source of the event
37
	 * object.
38
	 * <p>
39
	 * This method may also be called by objects that hold on to models but are
40
	 * not workbench parts. In this case, the event source must be set to an
41
	 * object that is not an instanceof IWorkbenchPart.
42
	 * </p>
43
	 * <p>
44
	 * Corresponding open and close events must originate from the same
45
	 * (identical) event source.
46
	 * </p>
47
	 * <p>
48
	 * This method must be called on the UI thread.
49
	 * </p>
50
	 */
51
	public void handleModelLifecycleEvent(ModelLifecycleEvent event);
52
53
	/**
54
	 * Adds the given listener to the list of listeners. Has no effect if the
55
	 * same (identical) listener has already been added. The listener will be
56
	 * notified about changes to the models managed by this model manager. Event
57
	 * types include: <br>
58
	 * POST_OPEN when models were added to the list of models <br>
59
	 * POST_CLOSE when models were removed from the list of models <br>
60
	 * DIRTY_CHANGED when the dirty state of models changed
61
	 * <p>
62
	 * Listeners should ignore all other event types, including PRE_CLOSE. There
63
	 * is no guarantee that listeners are notified before models are closed.
64
	 * 
65
	 * @param listener
66
	 */
67
	public void addModelLifecycleListener(IModelLifecycleListener listener);
68
69
	/**
70
	 * Removes the given listener from the list of listeners. Has no effect if
71
	 * the given listener is not contained in the list.
72
	 * 
73
	 * @param listener
74
	 */
75
	public void removeModelLifecycleListener(IModelLifecycleListener listener);
76
77
}
(-)Eclipse (+29 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 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
 ******************************************************************************/
11
12
package org.eclipse.ui;
13
14
15
16
/**
17
 * Listener for model lifecycle events.
18
 * 
19
 * @since 3.2
20
 */
21
public interface IModelLifecycleListener {
22
	
23
	/**
24
	 * Handle the given model lifecycle event. 
25
	 * @param event
26
	 */
27
	public void handleModelLifecycleEvent(ModelLifecycleEvent event);
28
29
}

Return to bug 112225