/******************************************************************************* * Copyright (c) 2000, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.program.Program; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.IHandler; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.internal.provisional.action.ICoolBarManager2; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.util.SafeRunnable; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.window.IShellProvider; import org.eclipse.ui.ActiveShellExpression; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorDescriptor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorLauncher; import org.eclipse.ui.IEditorMatchingStrategy; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorRegistry; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPathEditorInput; import org.eclipse.ui.IPersistableEditor; import org.eclipse.ui.IPersistableElement; import org.eclipse.ui.ISaveablePart; import org.eclipse.ui.ISaveablePart2; import org.eclipse.ui.ISaveablesLifecycleListener; import org.eclipse.ui.ISaveablesSource; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchPart3; import org.eclipse.ui.IWorkbenchPartReference; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.Saveable; import org.eclipse.ui.dialogs.ListSelectionDialog; import org.eclipse.ui.handlers.IHandlerActivation; import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.internal.StartupThreading.StartupRunnable; import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor; import org.eclipse.ui.internal.editorsupport.ComponentSupport; import org.eclipse.ui.internal.misc.ExternalEditor; import org.eclipse.ui.internal.misc.StatusUtil; import org.eclipse.ui.internal.misc.UIStats; import org.eclipse.ui.internal.part.NullEditorInput; import org.eclipse.ui.internal.registry.EditorDescriptor; import org.eclipse.ui.internal.registry.EditorRegistry; import org.eclipse.ui.internal.tweaklets.TabBehaviour; import org.eclipse.ui.internal.tweaklets.Tweaklets; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.model.WorkbenchPartLabelProvider; import org.eclipse.ui.part.MultiEditor; import org.eclipse.ui.part.MultiEditorInput; import org.eclipse.ui.statushandlers.StatusManager; /** * Manage a group of element editors. Prevent the creation of two editors on the * same element. * * 06/12/00 - DS - Given the ambiguous editor input type, the manager delegates * a number of responsibilities to the editor itself. * * <ol> * <li>The editor should determine its own title.</li> * <li>The editor should listen to resource deltas and close itself if the * input is deleted. It may also choose to stay open if the editor has dirty * state.</li> * <li>The editor should persist its own state plus editor input.</li> * </ol> */ public class EditorManager implements IExtensionChangeHandler { EditorAreaHelper editorPresentation; WorkbenchWindow window; WorkbenchPage page; private Map actionCache = new HashMap(); private static final String PIN_EDITOR_KEY = "PIN_EDITOR"; //$NON-NLS-1$ private static final String PIN_EDITOR = "ovr16/pinned_ovr.gif"; //$NON-NLS-1$ // When the user removes or adds the close editors automatically preference // the icon should be removed or added accordingly private IPropertyChangeListener editorPropChangeListnener = null; // Handler for the pin editor keyboard shortcut private IHandlerActivation pinEditorHandlerActivation = null; static final String RESOURCES_TO_SAVE_MESSAGE = WorkbenchMessages.EditorManager_saveResourcesMessage; static final String SAVE_RESOURCES_TITLE = WorkbenchMessages.EditorManager_saveResourcesTitle; /** * EditorManager constructor comment. */ public EditorManager(WorkbenchWindow window, WorkbenchPage workbenchPage, EditorAreaHelper pres) { Assert.isNotNull(window); Assert.isNotNull(workbenchPage); Assert.isNotNull(pres); this.window = window; this.page = workbenchPage; this.editorPresentation = pres; page.getExtensionTracker().registerHandler(this, null); } /** * Check to determine if the editor resources are no longer needed removes * property change listener for editors removes pin editor keyboard shortcut * handler disposes cached images and clears the cached images hash table */ void checkDeleteEditorResources() { // get the current number of editors IEditorReference[] editors = page.getEditorReferences(); // If there are no editors if (editors.length == 0) { if (editorPropChangeListnener != null) { // remove property change listener for editors IPreferenceStore prefStore = WorkbenchPlugin.getDefault() .getPreferenceStore(); prefStore .removePropertyChangeListener(editorPropChangeListnener); editorPropChangeListnener = null; } if (pinEditorHandlerActivation != null) { // remove pin editor keyboard shortcut handler final IHandlerService handlerService = (IHandlerService) window.getWorkbench().getService(IHandlerService.class); handlerService.deactivateHandler(pinEditorHandlerActivation); pinEditorHandlerActivation = null; } } } /** * Check to determine if the property change listener for editors should be * created */ void checkCreateEditorPropListener() { if (editorPropChangeListnener == null) { // Add a property change listener for closing editors automatically // preference // Add or remove the pin icon accordingly editorPropChangeListnener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals( IPreferenceConstants.REUSE_EDITORS_BOOLEAN)) { IEditorReference[] editors = getEditors(); for (int i = 0; i < editors.length; i++) { ((EditorReference) editors[i]).pinStatusUpdated(); } } } }; WorkbenchPlugin.getDefault().getPreferenceStore() .addPropertyChangeListener(editorPropChangeListnener); } } /** * Check to determine if the handler for the pin editor keyboard shortcut * should be created. */ void checkCreatePinEditorShortcutKeyHandler() { if (pinEditorHandlerActivation == null) { final Shell shell = window.getShell(); final IHandler pinEditorHandler = new AbstractHandler() { public final Object execute(final ExecutionEvent event) { // check if the "Close editors automatically" preference is // set IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore(); if (store .getBoolean(IPreferenceConstants.REUSE_EDITORS_BOOLEAN) || ((TabBehaviour)Tweaklets.get(TabBehaviour.KEY)).alwaysShowPinAction()) { IWorkbenchPartReference ref = editorPresentation .getVisibleEditor(); if (ref instanceof WorkbenchPartReference) { WorkbenchPartReference concreteRef = (WorkbenchPartReference) ref; concreteRef.setPinned(concreteRef.isPinned()); } } return null; } }; // Assign the handler for the pin editor keyboard shortcut. final IHandlerService handlerService = (IHandlerService) window.getWorkbench().getService(IHandlerService.class); pinEditorHandlerActivation = handlerService.activateHandler( "org.eclipse.ui.window.pinEditor", pinEditorHandler, //$NON-NLS-1$ new ActiveShellExpression(shell)); } } /** * Method to create the editor's pin ImageDescriptor * * @return the single image descriptor for the editor's pin icon */ ImageDescriptor getEditorPinImageDesc() { ImageRegistry registry = JFaceResources.getImageRegistry(); ImageDescriptor pinDesc = registry.getDescriptor(PIN_EDITOR_KEY); // Avoid registering twice if (pinDesc == null) { pinDesc = WorkbenchImages.getWorkbenchImageDescriptor(PIN_EDITOR); registry.put(PIN_EDITOR_KEY, pinDesc); } return pinDesc; } /** * Answer a list of dirty editors. */ private List collectDirtyEditors() { List result = new ArrayList(3); IEditorReference[] editors = page.getEditorReferences(); for (int i = 0; i < editors.length; i++) { IEditorPart part = (IEditorPart) editors[i].getPart(false); if (part != null && part.isDirty()) { result.add(part); } } return result; } /** * Returns whether the manager contains an editor. */ public boolean containsEditor(IEditorReference ref) { IEditorReference[] editors = page.getEditorReferences(); for (int i = 0; i < editors.length; i++) { if (ref == editors[i]) { return true; } } return false; } /* * Creates the action bars for an editor. Editors of the same type should * share a single editor action bar, so this implementation may return an * existing action bar vector. */ private EditorActionBars createEditorActionBars(EditorDescriptor desc, final IEditorSite site) { // Get the editor type. String type = desc.getId(); // If an action bar already exists for this editor type return it. EditorActionBars actionBars = (EditorActionBars) actionCache.get(type); if (actionBars != null) { actionBars.addRef(); return actionBars; } // Create a new action bar set. actionBars = new EditorActionBars(page, site.getWorkbenchWindow(), type); actionBars.addRef(); actionCache.put(type, actionBars); // Read base contributor. IEditorActionBarContributor contr = desc.createActionBarContributor(); if (contr != null) { actionBars.setEditorContributor(contr); contr.init(actionBars, page); } // Read action extensions. EditorActionBuilder builder = new EditorActionBuilder(); contr = builder.readActionExtensions(desc); if (contr != null) { actionBars.setExtensionContributor(contr); contr.init(actionBars, page); } // Return action bars. return actionBars; } /* * Creates the action bars for an editor. */ private EditorActionBars createEmptyEditorActionBars(final IEditorSite site) { // Get the editor type. String type = String.valueOf(System.currentTimeMillis()); // Create a new action bar set. // Note: It is an empty set. EditorActionBars actionBars = new EditorActionBars(page, site.getWorkbenchWindow(), type); actionBars.addRef(); actionCache.put(type, actionBars); // Return action bars. return actionBars; } /* * Dispose */ void disposeEditorActionBars(EditorActionBars actionBars) { actionBars.removeRef(); if (actionBars.getRef() <= 0) { String type = actionBars.getEditorType(); actionCache.remove(type); // refresh the cool bar manager before disposing of a cool item ICoolBarManager2 coolBar = (ICoolBarManager2) window.getCoolBarManager2(); if (coolBar != null) { coolBar.refresh(); } actionBars.dispose(); } } /** * Returns an open editor matching the given editor input. If none match, * returns <code>null</code>. * * @param input * the editor input * @return the matching editor, or <code>null</code> if no match fond */ public IEditorPart findEditor(IEditorInput input) { return findEditor(null, input, IWorkbenchPage.MATCH_INPUT); } /** * Returns an open editor matching the given editor input and/or editor id, * as specified by matchFlags. If none match, returns <code>null</code>. * * @param editorId * the editor id * @param input * the editor input * @param matchFlags * flags specifying which aspects to match * @return the matching editor, or <code>null</code> if no match fond * @since 3.1 */ public IEditorPart findEditor(String editorId, IEditorInput input, int matchFlags) { IEditorReference[] refs = findEditors(input, editorId, matchFlags); if (refs.length == 0) { return null; } return refs[0].getEditor(true); } /** * Returns the open editor references matching the given editor input and/or * editor id, as specified by matchFlags. If none match, returns an empty * array. * * @param editorId * the editor id * @param input * the editor input * @param matchFlags * flags specifying which aspects to match * @return the matching editor, or <code>null</code> if no match fond * @since 3.1 */ public IEditorReference[] findEditors(IEditorInput input, String editorId, int matchFlags) { if (matchFlags == IWorkbenchPage.MATCH_NONE) { return new IEditorReference[0]; } List result = new ArrayList(); ArrayList othersList = new ArrayList(Arrays.asList(page .getEditorReferences())); if (!othersList.isEmpty()) { IEditorReference active = page.getActiveEditorReference(); if (active != null) { othersList.remove(active); ArrayList activeList = new ArrayList(1); activeList.add(active); findEditors(activeList, input, editorId, matchFlags, result); } findEditors(othersList, input, editorId, matchFlags, result); } return (IEditorReference[]) result.toArray(new IEditorReference[result .size()]); } /** * Returns an open editor matching the given editor id and/or editor input. * Returns <code>null</code> if none match. * * @param editorId * the editor id * @param input * the editor input * @param editorList * a mutable list containing the references for the editors to * check (warning: items may be removed) * @param result * the list to which matching editor references should be added * @since 3.1 */ private void findEditors(List editorList, IEditorInput input, String editorId, int matchFlags, List result) { if (matchFlags == IWorkbenchPage.MATCH_NONE) { return; } // Phase 0: Remove editors whose ids don't match (if matching by id) if (((matchFlags & IWorkbenchPage.MATCH_ID) != 0) && editorId != null) { for (Iterator i = editorList.iterator(); i.hasNext();) { EditorReference editor = (EditorReference) i.next(); if (!editorId.equals(editor.getId())) { i.remove(); } } } // If not matching on editor input, just return the remaining editors. // In practice, this case is never used. if ((matchFlags & IWorkbenchPage.MATCH_INPUT) == 0) { result.addAll(editorList); return; } // Phase 1: check editors that have their own matching strategy for (Iterator i = editorList.iterator(); i.hasNext();) { EditorReference editor = (EditorReference) i.next(); IEditorDescriptor desc = editor.getDescriptor(); if (desc != null) { IEditorMatchingStrategy matchingStrategy = desc .getEditorMatchingStrategy(); if (matchingStrategy != null) { i.remove(); // We're handling this one here, so remove it // from the list. if (matchingStrategy.matches(editor, input)) { result.add(editor); } } } } // Phase 2: check materialized editors (without their own matching // strategy) for (Iterator i = editorList.iterator(); i.hasNext();) { IEditorReference editor = (IEditorReference) i.next(); IEditorPart part = (IEditorPart) editor.getPart(false); if (part != null) { i.remove(); // We're handling this one here, so remove it from // the list. if (part.getEditorInput() != null && part.getEditorInput().equals(input)) { result.add(editor); } } } // Phase 3: check unmaterialized editors for input equality, // delaying plug-in activation further by only restoring the editor // input // if the editor reference's factory id and name match. String name = input.getName(); IPersistableElement persistable = input.getPersistable(); if (name == null || persistable == null) { return; } String id = persistable.getFactoryId(); if (id == null) { return; } for (Iterator i = editorList.iterator(); i.hasNext();) { EditorReference editor = (EditorReference) i.next(); if (name.equals(editor.getName()) && id.equals(editor.getFactoryId())) { IEditorInput restoredInput; try { restoredInput = editor.getEditorInput(); if (Util.equals(restoredInput, input)) { result.add(editor); } } catch (PartInitException e1) { WorkbenchPlugin.log(e1); } } } } /** * Returns the SWT Display. */ private Display getDisplay() { return window.getShell().getDisplay(); } /** * Answer the number of editors. */ public int getEditorCount() { return page.getEditorReferences().length; } /* * Answer the editor registry. */ private IEditorRegistry getEditorRegistry() { return WorkbenchPlugin.getDefault().getEditorRegistry(); } /* * See IWorkbenchPage. */ public IEditorPart[] getDirtyEditors() { List dirtyEditors = collectDirtyEditors(); return (IEditorPart[]) dirtyEditors .toArray(new IEditorPart[dirtyEditors.size()]); } /* * See IWorkbenchPage. */ public IEditorReference[] getEditors() { return page.getEditorReferences(); } /* * See IWorkbenchPage#getFocusEditor */ public IEditorPart getVisibleEditor() { IEditorReference ref = editorPresentation.getVisibleEditor(); if (ref == null) { return null; } return (IEditorPart) ref.getPart(true); } /** * Answer true if save is needed in any one of the editors. */ public boolean isSaveAllNeeded() { IEditorReference[] editors = page.getEditorReferences(); for (int i = 0; i < editors.length; i++) { IEditorReference ed = editors[i]; if (ed.isDirty()) { return true; } } return false; } /* * Prompt the user to save the reusable editor. Return false if a new editor * should be opened. */ private IEditorReference findReusableEditor(EditorDescriptor desc) { return ((TabBehaviour)Tweaklets.get(TabBehaviour.KEY)).findReusableEditor(page); } /** * @param editorId * the editor part id * @param input * the input * @param setVisible * if this is to be created visible ... not used * @param editorState * an {@link IMemento} <editorState> for persistable * editors. Can be <code>null</code>. * @return a created editor reference * @throws PartInitException */ public IEditorReference openEditor(String editorId, IEditorInput input, boolean setVisible, IMemento editorState) throws PartInitException { if (editorId == null || input == null) { throw new IllegalArgumentException(); } IEditorRegistry reg = getEditorRegistry(); EditorDescriptor desc = (EditorDescriptor) reg.findEditor(editorId); if (desc == null) { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_unknownEditorIDMessage, editorId)); } IEditorReference result = openEditorFromDescriptor(desc, input, editorState); return result; } /* * Open a new editor */ public IEditorReference openEditorFromDescriptor(EditorDescriptor desc, IEditorInput input, IMemento editorState) throws PartInitException { IEditorReference result = null; if (desc.isInternal()) { result = reuseInternalEditor(desc, input); if (result == null) { result = new EditorReference(this, input, desc, editorState); } } else if (desc.getId() .equals(IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID)) { if (ComponentSupport.inPlaceEditorSupported()) { result = new EditorReference(this, input, desc); } } else if (desc.getId().equals( IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID)) { IPathEditorInput pathInput = getPathEditorInput(input); if (pathInput != null) { result = openSystemExternalEditor(pathInput.getPath()); } else { throw new PartInitException( WorkbenchMessages.EditorManager_systemEditorError); } } else if (desc.isOpenExternal()) { result = openExternalEditor(desc, input); } else { // this should never happen throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_invalidDescriptor, desc .getId())); } if (result != null) { createEditorTab((EditorReference) result, ""); //$NON-NLS-1$ } Workbench wb = (Workbench) window.getWorkbench(); wb.getEditorHistory().add(input, desc); return result; } /** * Open a specific external editor on an file based on the descriptor. */ private IEditorReference openExternalEditor(final EditorDescriptor desc, IEditorInput input) throws PartInitException { final CoreException ex[] = new CoreException[1]; final IPathEditorInput pathInput = getPathEditorInput(input); if (pathInput != null && pathInput.getPath() != null) { BusyIndicator.showWhile(getDisplay(), new Runnable() { public void run() { try { if (desc.getLauncher() != null) { // open using launcher Object launcher = WorkbenchPlugin.createExtension( desc.getConfigurationElement(), "launcher"); //$NON-NLS-1$ ((IEditorLauncher) launcher).open(pathInput .getPath()); } else { // open using command ExternalEditor oEditor = new ExternalEditor( pathInput.getPath(), desc); oEditor.open(); } } catch (CoreException e) { ex[0] = e; } } }); } else { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_errorOpeningExternalEditor, desc.getFileName(), desc.getId())); } if (ex[0] != null) { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_errorOpeningExternalEditor, desc.getFileName(), desc.getId()), ex[0]); } // we do not have an editor part for external editors return null; } /** * Create the part and reference for each inner editor. * * @param ref * the MultiEditor reference * @param part * the part * @param input * the MultiEditor input * @return the array of inner references to store in the MultiEditor reference */ IEditorReference[] openMultiEditor(final IEditorReference ref, final MultiEditor part, final MultiEditorInput input) throws PartInitException { String[] editorArray = input.getEditors(); IEditorInput[] inputArray = input.getInput(); // find all descriptors EditorDescriptor[] descArray = new EditorDescriptor[editorArray.length]; IEditorReference refArray[] = new IEditorReference[editorArray.length]; IEditorPart partArray[] = new IEditorPart[editorArray.length]; IEditorRegistry reg = getEditorRegistry(); for (int i = 0; i < editorArray.length; i++) { EditorDescriptor innerDesc = (EditorDescriptor) reg .findEditor(editorArray[i]); if (innerDesc == null) { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_unknownEditorIDMessage, editorArray[i])); } descArray[i] = innerDesc; InnerEditor innerRef = new InnerEditor(ref, inputArray[i], descArray[i]); refArray[i] = innerRef; partArray[i] = innerRef.getEditor(true); } part.setChildren(partArray); return refArray; } /* * Opens an editor part. */ private void createEditorTab(final EditorReference ref, final String workbookId) throws PartInitException { editorPresentation.addEditor(ref, workbookId); } /* * Create the site and initialize it with its action bars. */ EditorSite createSite(final IEditorReference ref, final IEditorPart part, final EditorDescriptor desc, final IEditorInput input) throws PartInitException { EditorSite site = new EditorSite(ref, part, page, desc); if (desc != null) { site.setActionBars(createEditorActionBars(desc, site)); } else { site.setActionBars(createEmptyEditorActionBars(site)); } final String label = part.getTitle(); // debugging only try { try { UIStats.start(UIStats.INIT_PART, label); part.init(site, input); } finally { UIStats.end(UIStats.INIT_PART, part, label); } // Sanity-check the site if (part.getSite() != site || part.getEditorSite() != site) { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_siteIncorrect, desc .getId())); } } catch (Exception e) { disposeEditorActionBars((EditorActionBars) site.getActionBars()); site.dispose(); if (e instanceof PartInitException) { throw (PartInitException) e; } throw new PartInitException( WorkbenchMessages.EditorManager_errorInInit, e); } return site; } /* * See IWorkbenchPage. */ private IEditorReference reuseInternalEditor(EditorDescriptor desc, IEditorInput input) throws PartInitException { Assert.isNotNull(desc, "descriptor must not be null"); //$NON-NLS-1$ Assert.isNotNull(input, "input must not be null"); //$NON-NLS-1$ IEditorReference reusableEditorRef = findReusableEditor(desc); if (reusableEditorRef != null) { return ((TabBehaviour) Tweaklets.get(TabBehaviour.KEY)) .reuseInternalEditor(page, this, editorPresentation, desc, input, reusableEditorRef); } return null; } IEditorPart createPart(final EditorDescriptor desc) throws PartInitException { try { IEditorPart result = desc.createEditor(); IConfigurationElement element = desc.getConfigurationElement(); if (element != null) { page.getExtensionTracker().registerObject( element.getDeclaringExtension(), result, IExtensionTracker.REF_WEAK); } return result; } catch (CoreException e) { throw new PartInitException(StatusUtil.newStatus( desc.getPluginID(), WorkbenchMessages.EditorManager_instantiationError, e)); } } /** * Open a system external editor on the input path. */ private IEditorReference openSystemExternalEditor(final IPath location) throws PartInitException { if (location == null) { throw new IllegalArgumentException(); } final boolean result[] = { false }; BusyIndicator.showWhile(getDisplay(), new Runnable() { public void run() { if (location != null) { result[0] = Program.launch(location.toOSString()); } } }); if (!result[0]) { throw new PartInitException(NLS.bind( WorkbenchMessages.EditorManager_unableToOpenExternalEditor, location)); } // We do not have an editor part for external editors return null; } ImageDescriptor findImage(EditorDescriptor desc, IPath path) { if (desc == null) { // @issue what should be the default image? return ImageDescriptor.getMissingImageDescriptor(); } if (desc.isOpenExternal() && path != null) { return PlatformUI.getWorkbench().getEditorRegistry() .getImageDescriptor(path.toOSString()); } return desc.getImageDescriptor(); } /** * @see org.eclipse.ui.IPersistable */ public IStatus restoreState(IMemento memento) { // Restore the editor area workbooks layout/relationship final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK, WorkbenchMessages.EditorManager_problemsRestoringEditors, null); final String activeWorkbookID[] = new String[1]; final ArrayList visibleEditors = new ArrayList(5); final IEditorReference activeEditor[] = new IEditorReference[1]; IMemento areaMem = memento.getChild(IWorkbenchConstants.TAG_AREA); if (areaMem != null) { result.add(editorPresentation.restoreState(areaMem)); activeWorkbookID[0] = areaMem .getString(IWorkbenchConstants.TAG_ACTIVE_WORKBOOK); } // Loop through the editors. IMemento[] editorMems = memento .getChildren(IWorkbenchConstants.TAG_EDITOR); for (int x = 0; x < editorMems.length; x++) { // for dynamic UI - call restoreEditorState to replace code which is // commented out restoreEditorState(editorMems[x], visibleEditors, activeEditor, result); } // restore the presentation if (areaMem != null) { result.add(editorPresentation.restorePresentationState(areaMem)); } try { StartupThreading.runWithThrowable(new StartupRunnable(){ public void runWithException() throws Throwable { // Update each workbook with its visible editor. for (int i = 0; i < visibleEditors.size(); i++) { setVisibleEditor((IEditorReference) visibleEditors.get(i), false); } // Update the active workbook if (activeWorkbookID[0] != null) { editorPresentation .setActiveEditorWorkbookFromID(activeWorkbookID[0]); } if (activeEditor[0] != null) { IWorkbenchPart editor = activeEditor[0].getPart(true); if (editor != null) { page.activate(editor); } } }}); } catch (Throwable t) { // The exception is already logged. result .add(new Status( IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, WorkbenchMessages.EditorManager_exceptionRestoringEditor, t)); } return result; } /** * Save all of the editors in the workbench. Return true if successful. * Return false if the user has canceled the command. * @param confirm true if the user should be prompted before the save * @param closing true if the page is being closed * @param addNonPartSources true if saveables from non-part sources should be saved too. * @return false if the user canceled or an error occurred while saving */ public boolean saveAll(boolean confirm, boolean closing, boolean addNonPartSources) { // Get the list of dirty editors and views. If it is // empty just return. ISaveablePart[] parts = page.getDirtyParts(); if (parts.length == 0) { return true; } // saveAll below expects a mutable list List dirtyParts = new ArrayList(parts.length); for (int i = 0; i < parts.length; i++) { dirtyParts.add(parts[i]); } // If confirmation is required .. return saveAll(dirtyParts, confirm, closing, addNonPartSources, window); } /** * Saves the given dirty editors and views, optionally prompting the user. * * @param dirtyParts * the dirty views and editors * @param confirm * <code>true</code> to prompt whether to save, <code>false</code> * to save without prompting * @param closing * <code>true</code> if the parts are being closed, * <code>false</code> if just being saved without closing * @param addNonPartSources true if non-part sources should be saved too * @param window * the window to use as the parent for the dialog that prompts to * save multiple dirty editors and views * @return <code>true</code> on success, <code>false</code> if the user * canceled the save or an error occurred while saving */ public static boolean saveAll(List dirtyParts, boolean confirm, boolean closing, boolean addNonPartSources, final IWorkbenchWindow window) { return saveAll(dirtyParts, confirm, closing, addNonPartSources, window, window); } /** * Saves the given dirty editors and views, optionally prompting the user. * * @param dirtyParts * the dirty views and editors * @param confirm * <code>true</code> to prompt whether to save, * <code>false</code> to save without prompting * @param closing * <code>true</code> if the parts are being closed, * <code>false</code> if just being saved without closing * @param addNonPartSources * true if non-part sources should be saved too * @param runnableContext * the context in which to run long-running operations * @param shellProvider * providing the shell to use as the parent for the dialog that * prompts to save multiple dirty editors and views * @return <code>true</code> on success, <code>false</code> if the user * canceled the save */ public static boolean saveAll(List dirtyParts, final boolean confirm, final boolean closing, boolean addNonPartSources, final IRunnableContext runnableContext, final IShellProvider shellProvider) { // clone the input list dirtyParts = new ArrayList(dirtyParts); List modelsToSave; if (confirm) { boolean saveable2Processed = false; // Process all parts that implement ISaveablePart2. // These parts are removed from the list after saving // them. We then need to restore the workbench to // its previous state, for now this is just last // active perspective. // Note that the given parts may come from multiple // windows, pages and perspectives. ListIterator listIterator = dirtyParts.listIterator(); WorkbenchPage currentPage = null; Perspective currentPageOriginalPerspective = null; while (listIterator.hasNext()) { IWorkbenchPart part = (IWorkbenchPart) listIterator.next(); if (part instanceof ISaveablePart2) { WorkbenchPage page = (WorkbenchPage) part.getSite() .getPage(); if (!Util.equals(currentPage, page)) { if (currentPage != null && currentPageOriginalPerspective != null) { if (!currentPageOriginalPerspective .equals(currentPage.getActivePerspective())) { currentPage .setPerspective(currentPageOriginalPerspective .getDesc()); } } currentPage = page; currentPageOriginalPerspective = page .getActivePerspective(); } if (confirm) { if (part instanceof IViewPart) { Perspective perspective = page .getFirstPerspectiveWithView((IViewPart) part); if (perspective != null) { page.setPerspective(perspective.getDesc()); } } // show the window containing the page? IWorkbenchWindow partsWindow = page .getWorkbenchWindow(); if (partsWindow != partsWindow.getWorkbench() .getActiveWorkbenchWindow()) { Shell shell = partsWindow.getShell(); if (shell.getMinimized()) { shell.setMinimized(false); } shell.setActive(); } page.bringToTop(part); } // try to save the part int choice = SaveableHelper.savePart((ISaveablePart2) part, page.getWorkbenchWindow(), confirm); if (choice == ISaveablePart2.CANCEL) { // If the user cancels, don't restore the previous // workbench state, as that will // be an unexpected switch from the current state. return false; } else if (choice != ISaveablePart2.DEFAULT) { saveable2Processed = true; listIterator.remove(); } } } // try to restore the workbench to its previous state if (currentPage != null && currentPageOriginalPerspective != null) { if (!currentPageOriginalPerspective.equals(currentPage .getActivePerspective())) { currentPage.setPerspective(currentPageOriginalPerspective .getDesc()); } } // if processing a ISaveablePart2 caused other parts to be // saved, remove them from the list presented to the user. if (saveable2Processed) { listIterator = dirtyParts.listIterator(); while (listIterator.hasNext()) { ISaveablePart part = (ISaveablePart) listIterator.next(); if (!part.isDirty()) { listIterator.remove(); } } } modelsToSave = convertToSaveables(dirtyParts, closing, addNonPartSources); // If nothing to save, return. if (modelsToSave.isEmpty()) { return true; } boolean canceled = SaveableHelper.waitForBackgroundSaveJobs(modelsToSave); if (canceled) { return false; } // Use a simpler dialog if there's only one if (modelsToSave.size() == 1) { Saveable model = (Saveable) modelsToSave.get(0); String message = NLS.bind(WorkbenchMessages.EditorManager_saveChangesQuestion, model.getName()); // Show a dialog. String[] buttons = new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }; MessageDialog d = new MessageDialog( shellProvider.getShell(), WorkbenchMessages.Save_Resource, null, message, MessageDialog.QUESTION, buttons, 0); int choice = SaveableHelper.testGetAutomatedResponse(); if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) { choice = d.open(); } // Branch on the user choice. // The choice id is based on the order of button labels // above. switch (choice) { case ISaveablePart2.YES: // yes break; case ISaveablePart2.NO: // no return true; default: case ISaveablePart2.CANCEL: // cancel return false; } } else { ListSelectionDialog dlg = new ListSelectionDialog( shellProvider.getShell(), modelsToSave, new ArrayContentProvider(), new WorkbenchPartLabelProvider(), RESOURCES_TO_SAVE_MESSAGE); dlg.setInitialSelections(modelsToSave.toArray()); dlg.setTitle(SAVE_RESOURCES_TITLE); // this "if" statement aids in testing. if (SaveableHelper.testGetAutomatedResponse()==SaveableHelper.USER_RESPONSE) { int result = dlg.open(); //Just return false to prevent the operation continuing if (result == IDialogConstants.CANCEL_ID) { return false; } modelsToSave = Arrays.asList(dlg.getResult()); } } } else { modelsToSave = convertToSaveables(dirtyParts, closing, addNonPartSources); } // If the editor list is empty return. if (modelsToSave.isEmpty()) { return true; } // Create save block. final List finalModels = modelsToSave; IRunnableWithProgress progressOp = new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { IProgressMonitor monitorWrap = new EventLoopProgressMonitor( monitor); monitorWrap.beginTask("", finalModels.size()); //$NON-NLS-1$ for (Iterator i = finalModels.iterator(); i.hasNext();) { Saveable model = (Saveable) i.next(); // handle case where this model got saved as a result of saving another if (!model.isDirty()) { monitor.worked(1); continue; } SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), shellProvider, closing || confirm); if (monitorWrap.isCanceled()) { break; } } monitorWrap.done(); } }; // Do the save. return SaveableHelper.runProgressMonitorOperation( WorkbenchMessages.Save_All, progressOp, runnableContext, shellProvider); } /** * For each part (view or editor) in the given list, attempts to convert it * to one or more saveable models. Duplicate models are removed. If closing * is true, then models that will remain open in parts other than the given * parts are removed. * * @param parts * the parts (list of IViewPart or IEditorPart) * @param closing * whether the parts are being closed * @param addNonPartSources * whether non-part sources should be added (true for the Save * All action, see bug 139004) * @return the dirty models */ private static List convertToSaveables(List parts, boolean closing, boolean addNonPartSources) { ArrayList result = new ArrayList(); HashSet seen = new HashSet(); for (Iterator i = parts.iterator(); i.hasNext();) { IWorkbenchPart part = (IWorkbenchPart) i.next(); Saveable[] saveables = getSaveables(part); for (int j = 0; j < saveables.length; j++) { Saveable saveable = saveables[j]; if (saveable.isDirty() && !seen.contains(saveable)) { seen.add(saveable); if (!closing || closingLastPartShowingModel(saveable, parts, part .getSite().getPage())) { result.add(saveable); } } } } if (addNonPartSources) { SaveablesList saveablesList = (SaveablesList) PlatformUI .getWorkbench().getService( ISaveablesLifecycleListener.class); ISaveablesSource[] nonPartSources = saveablesList .getNonPartSources(); for (int i = 0; i < nonPartSources.length; i++) { Saveable[] saveables = nonPartSources[i].getSaveables(); for (int j = 0; j < saveables.length; j++) { Saveable saveable = saveables[j]; if (saveable.isDirty() && !seen.contains(saveable)) { seen.add(saveable); result.add(saveable); } } } } return result; } /** * Returns the saveable models provided by the given part. * If the part does not provide any models, a default model * is returned representing the part. * * @param part the workbench part * @return the saveable models */ private static Saveable[] getSaveables(IWorkbenchPart part) { if (part instanceof ISaveablesSource) { ISaveablesSource source = (ISaveablesSource) part; return source.getSaveables(); } return new Saveable[] { new DefaultSaveable(part) }; } /** * Returns true if, in the given page, no more parts will reference the * given model if the given parts are closed. * * @param model * the model * @param closingParts * the parts being closed (list of IViewPart or IEditorPart) * @param page * the page * @return <code>true</code> if no more parts in the page will reference * the given model, <code>false</code> otherwise */ private static boolean closingLastPartShowingModel(Saveable model, List closingParts, IWorkbenchPage page) { HashSet closingPartsWithSameModel = new HashSet(); for (Iterator i = closingParts.iterator(); i.hasNext();) { IWorkbenchPart part = (IWorkbenchPart) i.next(); Saveable[] models = getSaveables(part); if (Arrays.asList(models).contains(model)) { closingPartsWithSameModel.add(part); } } IWorkbenchPartReference[] pagePartRefs = ((WorkbenchPage) page).getAllParts(); HashSet pagePartsWithSameModels = new HashSet(); for (int i = 0; i < pagePartRefs.length; i++) { IWorkbenchPartReference partRef = pagePartRefs[i]; IWorkbenchPart part = partRef.getPart(false); if (part != null) { Saveable[] models = getSaveables(part); if (Arrays.asList(models).contains(model)) { pagePartsWithSameModels.add(part); } } } for (Iterator i = closingPartsWithSameModel.iterator(); i.hasNext();) { IWorkbenchPart part = (IWorkbenchPart) i.next(); pagePartsWithSameModels.remove(part); } return pagePartsWithSameModels.isEmpty(); } /* * Saves the workbench part. */ public boolean savePart(final ISaveablePart saveable, IWorkbenchPart part, boolean confirm) { return SaveableHelper.savePart(saveable, part, window, confirm); } /** * @see IPersistablePart */ public IStatus saveState(final IMemento memento) { final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK, WorkbenchMessages.EditorManager_problemsSavingEditors, null); // Save the editor area workbooks layout/relationship IMemento editorAreaMem = memento .createChild(IWorkbenchConstants.TAG_AREA); result.add(editorPresentation.saveState(editorAreaMem)); // Save the active workbook id editorAreaMem.putString(IWorkbenchConstants.TAG_ACTIVE_WORKBOOK, editorPresentation.getActiveEditorWorkbookID()); // Get each workbook ArrayList workbooks = editorPresentation.getWorkbooks(); for (Iterator iter = workbooks.iterator(); iter.hasNext();) { EditorStack workbook = (EditorStack) iter.next(); // Use the list of editors found in EditorStack; fix for 24091 EditorPane editorPanes[] = workbook.getEditors(); for (int i = 0; i < editorPanes.length; i++) { // Save each open editor. IEditorReference editorReference = editorPanes[i] .getEditorReference(); EditorReference e = (EditorReference) editorReference; final IEditorPart editor = editorReference.getEditor(false); if (editor == null) { if (e.getMemento() != null) { IMemento editorMem = memento .createChild(IWorkbenchConstants.TAG_EDITOR); editorMem.putMemento(e.getMemento()); } continue; } // for dynamic UI - add the next line to replace the subsequent // code which is commented out saveEditorState(memento, e, result); } } return result; } /** * Shows an editor. If <code>setFocus == true</code> then give it focus, * too. * * @return true if the active editor was changed, false if not. */ public boolean setVisibleEditor(IEditorReference newEd, boolean setFocus) { return editorPresentation.setVisibleEditor(newEd, setFocus); } private IPathEditorInput getPathEditorInput(IEditorInput input) { if (input instanceof IPathEditorInput) { return (IPathEditorInput) input; } return (IPathEditorInput) Util.getAdapter(input, IPathEditorInput.class); } private class InnerEditor extends EditorReference { private IEditorReference outerEditor; public InnerEditor(IEditorReference outerEditor, IEditorInput input, EditorDescriptor desc) { super(EditorManager.this, input, desc); this.outerEditor = outerEditor; } protected PartPane createPane() { return new MultiEditorInnerPane( (EditorPane) ((EditorReference) outerEditor).getPane(), this, page, editorPresentation.getActiveWorkbook()); } } /* * Made public for Mylar in 3.3 - see bug 138666. Can be made private once * we have real API for this. */ public void restoreEditorState(IMemento editorMem, ArrayList visibleEditors, IEditorReference[] activeEditor, MultiStatus result) { // String strFocus = editorMem.getString(IWorkbenchConstants.TAG_FOCUS); // boolean visibleEditor = "true".equals(strFocus); //$NON-NLS-1$ final EditorReference e = new EditorReference(this, editorMem); // if the editor is not visible, ensure it is put in the correct // workbook. PR 24091 final String workbookID = editorMem .getString(IWorkbenchConstants.TAG_WORKBOOK); try { StartupThreading.runWithPartInitExceptions(new StartupRunnable () { public void runWithException() throws Throwable { createEditorTab(e, workbookID); }}); } catch (PartInitException ex) { result.add(ex.getStatus()); } String strActivePart = editorMem .getString(IWorkbenchConstants.TAG_ACTIVE_PART); if ("true".equals(strActivePart)) { //$NON-NLS-1$ activeEditor[0] = e; } String strFocus = editorMem.getString(IWorkbenchConstants.TAG_FOCUS); boolean visibleEditor = "true".equals(strFocus); //$NON-NLS-1$ if (visibleEditor) { visibleEditors.add(e); } } // for dynamic UI protected void saveEditorState(IMemento mem, IEditorReference ed, MultiStatus res) { final EditorReference editorRef = (EditorReference) ed; final IEditorPart editor = ed.getEditor(false); final IMemento memento = mem; final MultiStatus result = res; if (!(editor.getEditorSite() instanceof EditorSite)) { return; } final EditorSite site = (EditorSite) editor.getEditorSite(); if (site.getPane() instanceof MultiEditorInnerPane) { return; } SafeRunner.run(new SafeRunnable() { public void run() { // Get the input. IEditorInput input = editor.getEditorInput(); if (!input.exists()) { return; } IPersistableElement persistable = input.getPersistable(); if (persistable == null) { return; } // Save editor. IMemento editorMem = memento .createChild(IWorkbenchConstants.TAG_EDITOR); editorMem.putString(IWorkbenchConstants.TAG_TITLE, editorRef .getTitle()); editorMem.putString(IWorkbenchConstants.TAG_NAME, editorRef .getName()); editorMem.putString(IWorkbenchConstants.TAG_ID, editorRef .getId()); editorMem.putString(IWorkbenchConstants.TAG_TOOLTIP, editorRef .getTitleToolTip()); editorMem.putString(IWorkbenchConstants.TAG_PART_NAME, editorRef.getPartName()); if (editor instanceof IWorkbenchPart3) { Map properties = ((IWorkbenchPart3) editor) .getPartProperties(); if (!properties.isEmpty()) { IMemento propBag = editorMem .createChild(IWorkbenchConstants.TAG_PROPERTIES); Iterator i = properties.entrySet().iterator(); while (i.hasNext()) { Map.Entry entry = (Map.Entry) i.next(); IMemento p = propBag.createChild( IWorkbenchConstants.TAG_PROPERTY, (String) entry.getKey()); p.putTextData((String) entry.getValue()); } } } if (editorRef.isPinned()) { editorMem.putString(IWorkbenchConstants.TAG_PINNED, "true"); //$NON-NLS-1$ } EditorPane editorPane = (EditorPane) ((EditorSite) editor .getEditorSite()).getPane(); editorMem.putString(IWorkbenchConstants.TAG_WORKBOOK, editorPane.getWorkbook().getID()); if (editor == page.getActivePart()) { editorMem.putString(IWorkbenchConstants.TAG_ACTIVE_PART, "true"); //$NON-NLS-1$ } if (editorPane == editorPane.getWorkbook().getSelection()) { editorMem.putString(IWorkbenchConstants.TAG_FOCUS, "true"); //$NON-NLS-1$ } if (input instanceof IPathEditorInput) { IPath path = ((IPathEditorInput) input).getPath(); if (path != null) { editorMem.putString(IWorkbenchConstants.TAG_PATH, path .toString()); } } // Save input. IMemento inputMem = editorMem .createChild(IWorkbenchConstants.TAG_INPUT); inputMem.putString(IWorkbenchConstants.TAG_FACTORY_ID, persistable.getFactoryId()); persistable.saveState(inputMem); // any editors that want to persist state if (editor instanceof IPersistableEditor) { IMemento editorState = editorMem .createChild(IWorkbenchConstants.TAG_EDITOR_STATE); ((IPersistableEditor) editor).saveState(editorState); } } public void handleException(Throwable e) { result .add(new Status( IStatus.ERROR, PlatformUI.PLUGIN_ID, 0, NLS .bind( WorkbenchMessages.EditorManager_unableToSaveEditor, editorRef.getTitle()), e)); } }); } // for dynamic UI public IMemento getMemento(IEditorReference e) { if (e instanceof EditorReference) { return ((EditorReference) e).getMemento(); } return null; } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension, * java.lang.Object[]) */ public void removeExtension(IExtension source, Object[] objects) { for (int i = 0; i < objects.length; i++) { if (objects[i] instanceof IEditorPart) { // close the editor and clean up the editor history IEditorPart editor = (IEditorPart) objects[i]; IEditorInput input = editor.getEditorInput(); page.closeEditor(editor, true); ((Workbench) window.getWorkbench()).getEditorHistory().remove( input); } } } /* * (non-Javadoc) * * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#addExtension(org.eclipse.core.runtime.dynamicHelpers.IExtensionTracker, * org.eclipse.core.runtime.IExtension) */ public void addExtension(IExtensionTracker tracker, IExtension extension) { // Nothing to do } /** * @return */ /*package*/ IEditorReference openEmptyTab() { IEditorInput input = new NullEditorInput(); EditorDescriptor desc = (EditorDescriptor) ((EditorRegistry) getEditorRegistry()) .findEditor(EditorRegistry.EMPTY_EDITOR_ID); EditorReference result = new EditorReference(this, input, desc); try { createEditorTab(result, ""); //$NON-NLS-1$ return result; } catch (PartInitException e) { StatusManager.getManager().handle( StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH, e)); } return null; } public static boolean useIPersistableEditor() { IPreferenceStore store = WorkbenchPlugin.getDefault() .getPreferenceStore(); return store.getBoolean(IPreferenceConstants.USE_IPERSISTABLE_EDITORS); } }