001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.actions;
011:
012: import java.util.ArrayList;
013: import java.util.Arrays;
014: import java.util.Iterator;
015: import java.util.List;
016:
017: import org.eclipse.core.resources.IFile;
018: import org.eclipse.core.resources.IProject;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.resources.IResourceChangeEvent;
021: import org.eclipse.core.resources.IResourceChangeListener;
022: import org.eclipse.core.resources.IResourceDelta;
023: import org.eclipse.core.resources.IResourceRuleFactory;
024: import org.eclipse.core.resources.ResourcesPlugin;
025: import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
026: import org.eclipse.core.resources.mapping.ResourceChangeValidator;
027: import org.eclipse.core.runtime.CoreException;
028: import org.eclipse.core.runtime.IProgressMonitor;
029: import org.eclipse.core.runtime.NullProgressMonitor;
030: import org.eclipse.core.runtime.jobs.ISchedulingRule;
031: import org.eclipse.core.runtime.jobs.MultiRule;
032: import org.eclipse.jface.dialogs.IDialogConstants;
033: import org.eclipse.jface.viewers.IStructuredSelection;
034: import org.eclipse.osgi.util.NLS;
035: import org.eclipse.swt.custom.BusyIndicator;
036: import org.eclipse.swt.widgets.Shell;
037: import org.eclipse.ui.IEditorPart;
038: import org.eclipse.ui.IWorkbenchPage;
039: import org.eclipse.ui.IWorkbenchWindow;
040: import org.eclipse.ui.PlatformUI;
041: import org.eclipse.ui.dialogs.ListSelectionDialog;
042: import org.eclipse.ui.ide.IDE;
043: import org.eclipse.ui.ide.ResourceUtil;
044: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
045: import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
046: import org.eclipse.ui.model.AdaptableList;
047: import org.eclipse.ui.model.WorkbenchContentProvider;
048: import org.eclipse.ui.model.WorkbenchPartLabelProvider;
049:
050: /**
051: * Standard action for closing the currently selected project(s).
052: * <p>
053: * This class may be instantiated; it is not intended to be subclassed.
054: * </p>
055: */
056: public class CloseResourceAction extends WorkspaceAction implements
057: IResourceChangeListener {
058: /**
059: * The id of this action.
060: */
061: public static final String ID = PlatformUI.PLUGIN_ID
062: + ".CloseResourceAction"; //$NON-NLS-1$
063: private String[] modelProviderIds;
064:
065: /**
066: * Creates a new action.
067: *
068: * @param shell the shell for any dialogs
069: */
070: public CloseResourceAction(Shell shell) {
071: super (shell, IDEWorkbenchMessages.CloseResourceAction_text);
072: setId(ID);
073: setToolTipText(IDEWorkbenchMessages.CloseResourceAction_toolTip);
074: PlatformUI.getWorkbench().getHelpSystem().setHelp(this ,
075: IIDEHelpContextIds.CLOSE_RESOURCE_ACTION);
076: }
077:
078: /**
079: * Override super constructor to allow subclass to
080: * override with unique text.
081: */
082: protected CloseResourceAction(Shell shell, String text) {
083: super (shell, text);
084: }
085:
086: /**
087: * Return a list of dirty editors associated with the given projects. Return
088: * editors from all perspectives.
089: *
090: * @return List the dirty editors
091: */
092: List getDirtyEditors(List projects) {
093: List dirtyEditors = new ArrayList(0);
094: IWorkbenchWindow[] windows = PlatformUI.getWorkbench()
095: .getWorkbenchWindows();
096: for (int i = 0; i < windows.length; i++) {
097: IWorkbenchPage[] pages = windows[i].getPages();
098: for (int j = 0; j < pages.length; j++) {
099: IWorkbenchPage page = pages[j];
100: IEditorPart[] editors = page.getDirtyEditors();
101: for (int k = 0; k < editors.length; k++) {
102: IEditorPart editor = editors[k];
103: IFile inputFile = ResourceUtil.getFile(editor
104: .getEditorInput());
105: if (inputFile != null) {
106: if (projects.contains(inputFile.getProject())) {
107: dirtyEditors.add(editor);
108: }
109: }
110: }
111: }
112: }
113: return dirtyEditors;
114: }
115:
116: /**
117: * Open a dialog that can be used to select which of the given
118: * editors to save. Return the list of editors to save. A value of
119: * null implies that the operation was cancelled.
120: *
121: * @return List the editors to save
122: */
123: List getEditorsToSave(List dirtyEditors) {
124: if (dirtyEditors.isEmpty()) {
125: return new ArrayList(0);
126: }
127:
128: // The list may have multiple editors opened for the same input,
129: // so process the list for duplicates.
130: List saveEditors = new ArrayList(0);
131: List dirtyInputs = new ArrayList(0);
132: Iterator iter = dirtyEditors.iterator();
133: while (iter.hasNext()) {
134: IEditorPart editor = (IEditorPart) iter.next();
135: IFile inputFile = ResourceUtil.getFile(editor
136: .getEditorInput());
137: if (inputFile != null) {
138: // if the same file is open in multiple perspectives,
139: // we don't want to count it as dirty multiple times
140: if (!dirtyInputs.contains(inputFile)) {
141: dirtyInputs.add(inputFile);
142: saveEditors.add(editor);
143: }
144: }
145: }
146: AdaptableList input = new AdaptableList(saveEditors);
147: ListSelectionDialog dlg = new ListSelectionDialog(getShell(),
148: input, new WorkbenchContentProvider(),
149: new WorkbenchPartLabelProvider(),
150: IDEWorkbenchMessages.EditorManager_saveResourcesMessage);
151:
152: dlg.setInitialSelections(saveEditors
153: .toArray(new Object[saveEditors.size()]));
154: dlg
155: .setTitle(IDEWorkbenchMessages.EditorManager_saveResourcesTitle);
156: int result = dlg.open();
157:
158: if (result == IDialogConstants.CANCEL_ID) {
159: return null;
160: }
161: return Arrays.asList(dlg.getResult());
162: }
163:
164: /* (non-Javadoc)
165: * Method declared on WorkspaceAction.
166: */
167: protected String getOperationMessage() {
168: return IDEWorkbenchMessages.CloseResourceAction_operationMessage;
169: }
170:
171: /* (non-Javadoc)
172: * Method declared on WorkspaceAction.
173: */
174: protected String getProblemsMessage() {
175: return IDEWorkbenchMessages.CloseResourceAction_problemMessage;
176: }
177:
178: /* (non-Javadoc)
179: * Method declared on WorkspaceAction.
180: */
181: protected String getProblemsTitle() {
182: return IDEWorkbenchMessages.CloseResourceAction_title;
183: }
184:
185: protected void invokeOperation(IResource resource,
186: IProgressMonitor monitor) throws CoreException {
187: ((IProject) resource).close(monitor);
188: }
189:
190: /**
191: * The implementation of this <code>WorkspaceAction</code> method
192: * method saves and closes the resource's dirty editors before closing
193: * it.
194: */
195: public void run() {
196: if (!saveDirtyEditors()) {
197: return;
198: }
199: if (!validateClose()) {
200: return;
201: }
202: //be conservative and include all projects in the selection - projects
203: //can change state between now and when the job starts
204: ISchedulingRule rule = null;
205: IResourceRuleFactory factory = ResourcesPlugin.getWorkspace()
206: .getRuleFactory();
207: Iterator resources = getSelectedResources().iterator();
208: while (resources.hasNext()) {
209: IProject project = (IProject) resources.next();
210: rule = MultiRule.combine(rule, factory.modifyRule(project));
211: }
212: runInBackground(rule);
213: }
214:
215: /**
216: * Causes all dirty editors associated to the resource(s) to be saved, if so
217: * specified by the user, and closed.
218: */
219: boolean saveDirtyEditors() {
220: // Get the items to close.
221: List projects = getSelectedResources();
222: if (projects == null || projects.isEmpty()) {
223: // no action needs to be taken since no projects are selected
224: return false;
225: }
226:
227: // Collect all the dirty editors that are associated to the projects that are
228: // to be closed.
229: final List dirtyEditors = getDirtyEditors(projects);
230:
231: // See which editors should be saved.
232: final List saveEditors = getEditorsToSave(dirtyEditors);
233: if (saveEditors == null) {
234: // the operation was cancelled
235: return false;
236: }
237:
238: // Save and close the dirty editors.
239: BusyIndicator.showWhile(getShell().getDisplay(),
240: new Runnable() {
241: public void run() {
242: Iterator iter = dirtyEditors.iterator();
243: while (iter.hasNext()) {
244: IEditorPart editor = (IEditorPart) iter
245: .next();
246: IWorkbenchPage page = editor
247: .getEditorSite().getPage();
248: if (saveEditors.contains(editor)) {
249: // do a direct save vs. using page.saveEditor, so that
250: // progress dialogs do not flash up on the screen multiple
251: // times
252: editor
253: .doSave(new NullProgressMonitor());
254: }
255: page.closeEditor(editor, false);
256: }
257: }
258: });
259:
260: return true;
261: }
262:
263: /* (non-Javadoc)
264: * Method declared on WorkspaceAction.
265: */
266: protected boolean shouldPerformResourcePruning() {
267: return false;
268: }
269:
270: /**
271: * The <code>CloseResourceAction</code> implementation of this
272: * <code>SelectionListenerAction</code> method ensures that this action is
273: * enabled only if one of the selections is an open project.
274: */
275: protected boolean updateSelection(IStructuredSelection s) {
276: // don't call super since we want to enable if open project is selected.
277: if (!selectionIsOfType(IResource.PROJECT)) {
278: return false;
279: }
280:
281: Iterator resources = getSelectedResources().iterator();
282: while (resources.hasNext()) {
283: IProject currentResource = (IProject) resources.next();
284: if (currentResource.isOpen()) {
285: return true;
286: }
287: }
288: return false;
289: }
290:
291: /**
292: * Handles a resource changed event by updating the enablement
293: * if one of the selected projects is opened or closed.
294: */
295: public void resourceChanged(IResourceChangeEvent event) {
296: // Warning: code duplicated in OpenResourceAction
297: List sel = getSelectedResources();
298: // don't bother looking at delta if selection not applicable
299: if (selectionIsOfType(IResource.PROJECT)) {
300: IResourceDelta delta = event.getDelta();
301: if (delta != null) {
302: IResourceDelta[] projDeltas = delta
303: .getAffectedChildren(IResourceDelta.CHANGED);
304: for (int i = 0; i < projDeltas.length; ++i) {
305: IResourceDelta projDelta = projDeltas[i];
306: if ((projDelta.getFlags() & IResourceDelta.OPEN) != 0) {
307: if (sel.contains(projDelta.getResource())) {
308: selectionChanged(getStructuredSelection());
309: return;
310: }
311: }
312: }
313: }
314: }
315: }
316:
317: /**
318: * Returns the model provider ids that are known to the client
319: * that instantiated this operation.
320: *
321: * @return the model provider ids that are known to the client
322: * that instantiated this operation.
323: * @since 3.2
324: */
325: public String[] getModelProviderIds() {
326: return modelProviderIds;
327: }
328:
329: /**
330: * Sets the model provider ids that are known to the client
331: * that instantiated this operation. Any potential side effects
332: * reported by these models during validation will be ignored.
333: *
334: * @param modelProviderIds the model providers known to the client
335: * who is using this operation.
336: * @since 3.2
337: */
338: public void setModelProviderIds(String[] modelProviderIds) {
339: this .modelProviderIds = modelProviderIds;
340: }
341:
342: /**
343: * Validates the operation against the model providers.
344: *
345: * @return whether the operation should proceed
346: */
347: private boolean validateClose() {
348: IResourceChangeDescriptionFactory factory = ResourceChangeValidator
349: .getValidator().createDeltaFactory();
350: List resources = getActionResources();
351: for (Iterator iter = resources.iterator(); iter.hasNext();) {
352: IResource resource = (IResource) iter.next();
353: if (resource instanceof IProject) {
354: IProject project = (IProject) resource;
355: factory.close(project);
356: }
357: }
358: String message;
359: if (resources.size() == 1) {
360: message = NLS
361: .bind(
362: IDEWorkbenchMessages.CloseResourceAction_warningForOne,
363: ((IResource) resources.get(0)).getName());
364: } else {
365: message = IDEWorkbenchMessages.CloseResourceAction_warningForMultiple;
366: }
367: return IDE.promptToConfirm(getShell(),
368: IDEWorkbenchMessages.CloseResourceAction_confirm,
369: message, factory.getDelta(), getModelProviderIds(),
370: false /* no need to syncExec */);
371: }
372: }
|