blob: f489aa710f018a6d2802c04df82a8737c2b32ad2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.internal.InternalSaveable;
import org.eclipse.ui.internal.PartSite;
import org.eclipse.ui.progress.IJobRunnable;
/**
* A <code>Saveable</code> represents a unit of saveability, e.g. an editable
* subset of the underlying domain model that may contain unsaved changes.
* Different workbench parts (editors and views) may present the same saveables
* in different ways. This interface allows the workbench to provide more
* appropriate handling of operations such as saving and closing workbench
* parts. For example, if two editors sharing the same saveable with unsaved
* changes are closed simultaneously, the user is only prompted to save the
* changes once for the shared saveable, rather than once for each editor.
* <p>
* Workbench parts that work in terms of saveables should implement
* {@link ISaveablesSource}.
* </p>
*
* @see ISaveablesSource
* @since 3.2
*/
public abstract class Saveable extends InternalSaveable implements IAdaptable {
/**
* Either {@code null} or the System's {@link SWT#CURSOR_WAIT} cursor instance.
* Should never be disposed.
*/
private Cursor waitCursor;
private Cursor originalCursor;
/**
* Attempts to show this saveable in the given page and returns
* <code>true</code> on success. The default implementation does nothing and
* returns <code>false</code>.
*
* @param page the workbench page in which to show this saveable
* @return <code>true</code> if this saveable is now visible to the user
* @since 3.3
*/
public boolean show(IWorkbenchPage page) {
if (page == null) {
// I wish it was easier to avoid warnings about unused parameters
}
return false;
}
/**
* Returns the name of this saveable for display purposes.
*
* @return the model's name; never <code>null</code>.
*/
public abstract String getName();
/**
* Returns the tool tip text for this saveable. This text is used to
* differentiate between two inputs with the same name. For instance,
* MyClass.java in folder X and MyClass.java in folder Y. The format of the text
* varies between input types.
*
* @return the tool tip text; never <code>null</code>
*/
public abstract String getToolTipText();
/**
* Returns the image descriptor for this saveable.
*
* @return the image descriptor for this model; may be <code>null</code> if
* there is no image
*/
public abstract ImageDescriptor getImageDescriptor();
/**
* Saves the contents of this saveable.
* <p>
* If the save is cancelled through user action, or for any other reason, the
* part should invoke <code>setCancelled</code> on the
* <code>IProgressMonitor</code> to inform the caller.
* </p>
* <p>
* This method is long-running; progress and cancellation are provided by the
* given progress monitor.
* </p>
*
* @param monitor the progress monitor
* @throws CoreException if the save fails; it is the caller's responsibility to
* report the failure to the user
*/
public abstract void doSave(IProgressMonitor monitor) throws CoreException;
/**
* Returns whether the contents of this saveable have changed since the last
* save operation.
* <p>
* <b>Note:</b> this method is called frequently, for example by actions to
* determine their enabled status.
* </p>
*
* @return <code>true</code> if the contents have been modified and need saving,
* and <code>false</code> if they have not changed since the last save
*/
public abstract boolean isDirty();
/**
* Clients must implement equals and hashCode as defined in
* {@link Object#equals(Object)} and {@link Object#hashCode()}. Two saveables
* should be equal if their dirty state is shared, and saving one will save the
* other. If two saveables are equal, their names, tooltips, and images should
* be the same because only one of them will be shown when prompting the user to
* save.
*
* @param object the object to compare
* @return true if this Saveable is equal to the given object
*/
@Override
public abstract boolean equals(Object object);
/**
* Clients must implement equals and hashCode as defined in
* {@link Object#equals(Object)} and {@link Object#hashCode()}. Two saveables
* should be equal if their dirty state is shared, and saving one will save the
* other. If two saveables are equal, their hash codes MUST be the same, and
* their names, tooltips, and images should be the same because only one of them
* will be shown when prompting the user to save.
* <p>
* IMPORTANT: Implementers should ensure that the hashCode returned is
* sufficiently unique so as not to collide with hashCodes returned by other
* implementations. It is suggested that the defining plug-in's ID be used as
* part of the returned hashCode, as in the following example:
* </p>
*
* <pre>
* int PRIME = 31;
* int hash = ...; // compute the &quot;normal&quot; hash code, e.g. based on some identifier unique within the defining plug-in
* return hash * PRIME + MY_PLUGIN_ID.hashCode();
* </pre>
*
* @return a hash code
*/
@Override
public abstract int hashCode();
/**
* Saves this saveable, or prepares this saveable for a background save
* operation. Returns null if this saveable has been successfully saved, or a
* job runnable that needs to be run to complete the save in the background.
* This method is called in the UI thread. If this saveable supports saving in
* the background, it should do only minimal work. However, since the job
* runnable returned by this method (if any) will not run on the UI thread, this
* method should copy any state that can only be accessed from the UI thread so
* that the job runnable will be able to access it.
* <p>
* The supplied shell provider can be used from within this method and from
* within the job runnable for the purpose of parenting dialogs. Care should be
* taken not to open dialogs gratuitously and only if user input is required for
* cases where the save cannot otherwise proceed - note that in any given save
* operation, many saveable objects may be saved at the same time. In
* particular, errors should be signaled by throwing an exception, or if an
* error occurs while running the job runnable, an error status should be
* returned.
* </p>
* <p>
* If the foreground part of the save is cancelled through user action, or for
* any other reason, the part should invoke <code>setCancelled</code> on the
* <code>IProgressMonitor</code> to inform the caller. If the background part of
* the save is cancelled, the job should return a {@link IStatus#CANCEL} status.
* </p>
* <p>
* This method is long-running; progress and cancellation are provided by the
* given progress monitor.
* </p>
* <p>
* The default implementation of this method calls
* {@link #doSave(IProgressMonitor)} and returns <code>null</code>.
* </p>
*
* @param monitor a progress monitor used for reporting progress and
* cancellation
* @param shellProvider an object that can provide a shell for parenting dialogs
* @return <code>null</code> if this saveable has been saved successfully, or a
* job runnable that needs to be run to complete the save in the
* background.
* @throws CoreException if the save fails; it is the caller's responsibility to
* report the failure to the user
* @since 3.3
*/
public IJobRunnable doSave(IProgressMonitor monitor, IShellProvider shellProvider) throws CoreException {
doSave(monitor);
return null;
}
/**
* Disables the UI of the given parts containing this saveable if necessary.
* This method is not intended to be called by clients. A corresponding call to
* <p>
* Saveables that can be saved in the background should ensure that the user
* cannot make changes to their data from the UI, for example by disabling
* controls, unless they are prepared to handle this case. This method is called
* on the UI thread after a job runnable has been returned from
* {@link #doSave(IProgressMonitor, IShellProvider)} and before spinning the
* event loop. The <code>closing</code> flag indicates that this saveable is
* currently being saved in response to closing a workbench part, in which case
* further changes to this saveable through the UI must be prevented.
* </p>
* <p>
* The default implementation calls setEnabled(false) on the given parts'
* composites.
* </p>
*
* @param parts the workbench parts containing this saveable
* @param closing a boolean flag indicating whether the save was triggered by a
* request to close a workbench part, and all of the given parts
* will be closed after the save operation finishes successfully.
*
* @since 3.3
*/
public void disableUI(IWorkbenchPart[] parts, boolean closing) {
for (IWorkbenchPart workbenchPart : parts) {
Composite paneComposite = (Composite) ((PartSite) workbenchPart.getSite()).getModel().getWidget();
Control[] paneChildren = paneComposite.getChildren();
Composite toDisable = ((Composite) paneChildren[0]);
toDisable.setEnabled(false);
if (waitCursor == null) {
waitCursor = workbenchPart.getSite().getWorkbenchWindow().getShell().getDisplay()
.getSystemCursor(SWT.CURSOR_WAIT);
}
if (waitCursor.equals(paneComposite.getCursor())) {
originalCursor = paneComposite.getCursor();
paneComposite.setCursor(waitCursor);
}
}
}
/**
* Enables the UI of the given parts containing this saveable after a background
* save operation has finished. This method is not intended to be called by
* clients.
* <p>
* The default implementation calls setEnabled(true) on the given parts'
* composites.
* </p>
*
* @param parts the workbench parts containing this saveable
*
* @since 3.3
*/
public void enableUI(IWorkbenchPart[] parts) {
for (IWorkbenchPart workbenchPart : parts) {
Composite paneComposite = (Composite) ((PartSite) workbenchPart.getSite()).getModel().getWidget();
Control[] paneChildren = paneComposite.getChildren();
Composite toEnable = ((Composite) paneChildren[0]);
paneComposite.setCursor(originalCursor);
if (waitCursor != null) {
/*
* waitCursor is always the System SWT.CURSOR_WAIT instance and should never be
* disposed
*/
waitCursor = null;
}
toEnable.setEnabled(true);
}
}
/**
* This implementation of {@link IAdaptable#getAdapter(Class)} returns
* <code>null</code>. Subclasses may override. This allows two unrelated
* subclasses of Saveable to implement {@link #equals(Object)} and
* {@link #hashCode()} based on an underlying implementation class that is
* shared by both Saveable subclasses.
*
* @since 3.3
*/
@Override
public <T> T getAdapter(Class<T> adapter) {
return null;
}
}