001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 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;
011:
012: import org.eclipse.core.runtime.CoreException;
013: import org.eclipse.core.runtime.IAdaptable;
014: import org.eclipse.core.runtime.IProgressMonitor;
015: import org.eclipse.core.runtime.IStatus;
016: import org.eclipse.jface.resource.ImageDescriptor;
017: import org.eclipse.jface.window.IShellProvider;
018: import org.eclipse.swt.SWT;
019: import org.eclipse.swt.graphics.Cursor;
020: import org.eclipse.swt.widgets.Composite;
021: import org.eclipse.swt.widgets.Control;
022: import org.eclipse.ui.internal.InternalSaveable;
023: import org.eclipse.ui.internal.PartSite;
024: import org.eclipse.ui.progress.IJobRunnable;
025:
026: /**
027: * A <code>Saveable</code> represents a unit of saveability, e.g. an editable
028: * subset of the underlying domain model that may contain unsaved changes.
029: * Different workbench parts (editors and views) may present the same saveables
030: * in different ways. This interface allows the workbench to provide more
031: * appropriate handling of operations such as saving and closing workbench
032: * parts. For example, if two editors sharing the same saveable with unsaved
033: * changes are closed simultaneously, the user is only prompted to save the
034: * changes once for the shared saveable, rather than once for each editor.
035: * <p>
036: * Workbench parts that work in terms of saveables should implement
037: * {@link ISaveablesSource}.
038: * </p>
039: *
040: * @see ISaveablesSource
041: * @since 3.2
042: */
043: public abstract class Saveable extends InternalSaveable implements
044: IAdaptable {
045:
046: private Cursor waitCursor;
047: private Cursor originalCursor;
048:
049: /**
050: * Attempts to show this saveable in the given page and returns
051: * <code>true</code> on success. The default implementation does nothing
052: * and returns <code>false</code>.
053: *
054: * @param page
055: * the workbench page in which to show this saveable
056: * @return <code>true</code> if this saveable is now visible to the user
057: * @since 3.3
058: */
059: public boolean show(IWorkbenchPage page) {
060: if (page == null) {
061: // I wish it was easier to avoid warnings about unused parameters
062: }
063: return false;
064: }
065:
066: /**
067: * Returns the name of this saveable for display purposes.
068: *
069: * @return the model's name; never <code>null</code>.
070: */
071: public abstract String getName();
072:
073: /**
074: * Returns the tool tip text for this saveable. This text is used to
075: * differentiate between two inputs with the same name. For instance,
076: * MyClass.java in folder X and MyClass.java in folder Y. The format of the
077: * text varies between input types.
078: *
079: * @return the tool tip text; never <code>null</code>
080: */
081: public abstract String getToolTipText();
082:
083: /**
084: * Returns the image descriptor for this saveable.
085: *
086: * @return the image descriptor for this model; may be <code>null</code>
087: * if there is no image
088: */
089: public abstract ImageDescriptor getImageDescriptor();
090:
091: /**
092: * Saves the contents of this saveable.
093: * <p>
094: * If the save is cancelled through user action, or for any other reason,
095: * the part should invoke <code>setCancelled</code> on the
096: * <code>IProgressMonitor</code> to inform the caller.
097: * </p>
098: * <p>
099: * This method is long-running; progress and cancellation are provided by
100: * the given progress monitor.
101: * </p>
102: *
103: * @param monitor
104: * the progress monitor
105: * @throws CoreException
106: * if the save fails; it is the caller's responsibility to
107: * report the failure to the user
108: */
109: public abstract void doSave(IProgressMonitor monitor)
110: throws CoreException;
111:
112: /**
113: * Returns whether the contents of this saveable have changed since the last
114: * save operation.
115: * <p>
116: * <b>Note:</b> this method is called frequently, for example by actions to
117: * determine their enabled status.
118: * </p>
119: *
120: * @return <code>true</code> if the contents have been modified and need
121: * saving, and <code>false</code> if they have not changed since
122: * the last save
123: */
124: public abstract boolean isDirty();
125:
126: /**
127: * Clients must implement equals and hashCode as defined in
128: * {@link Object#equals(Object)} and {@link Object#hashCode()}. Two
129: * saveables should be equal if their dirty state is shared, and saving one
130: * will save the other. If two saveables are equal, their names, tooltips,
131: * and images should be the same because only one of them will be shown when
132: * prompting the user to save.
133: *
134: * @param object
135: * @return true if this Saveable is equal to the given object
136: */
137: public abstract boolean equals(Object object);
138:
139: /**
140: * Clients must implement equals and hashCode as defined in
141: * {@link Object#equals(Object)} and {@link Object#hashCode()}. Two
142: * saveables should be equal if their dirty state is shared, and saving one
143: * will save the other. If two saveables are equal, their hash codes MUST be
144: * the same, and their names, tooltips, and images should be the same
145: * because only one of them will be shown when prompting the user to save.
146: * <p>
147: * IMPORTANT: Implementers should ensure that the hashCode returned is
148: * sufficiently unique so as not to collide with hashCodes returned by other
149: * implementations. It is suggested that the defining plug-in's ID be used
150: * as part of the returned hashCode, as in the following example:
151: * </p>
152: *
153: * <pre>
154: * int PRIME = 31;
155: * int hash = ...; // compute the "normal" hash code, e.g. based on some identifier unique within the defining plug-in
156: * return hash * PRIME + MY_PLUGIN_ID.hashCode();
157: * </pre>
158: *
159: * @return a hash code
160: */
161: public abstract int hashCode();
162:
163: /**
164: * Saves this saveable, or prepares this saveable for a background save
165: * operation. Returns null if this saveable has been successfully saved, or
166: * a job runnable that needs to be run to complete the save in the
167: * background. This method is called in the UI thread. If this saveable
168: * supports saving in the background, it should do only minimal work.
169: * However, since the job runnable returned by this method (if any) will not
170: * run on the UI thread, this method should copy any state that can only be
171: * accessed from the UI thread so that the job runnable will be able to
172: * access it.
173: * <p>
174: * The supplied shell provider can be used from within this method and from
175: * within the job runnable for the purpose of parenting dialogs. Care should
176: * be taken not to open dialogs gratuitously and only if user input is
177: * required for cases where the save cannot otherwise proceed - note that in
178: * any given save operation, many saveable objects may be saved at the same
179: * time. In particular, errors should be signaled by throwing an exception,
180: * or if an error occurs while running the job runnable, an error status
181: * should be returned.
182: * </p>
183: * <p>
184: * If the foreground part of the save is cancelled through user action, or
185: * for any other reason, the part should invoke <code>setCancelled</code>
186: * on the <code>IProgressMonitor</code> to inform the caller. If the
187: * background part of the save is cancelled, the job should return a
188: * {@link IStatus#CANCEL} status.
189: * </p>
190: * <p>
191: * This method is long-running; progress and cancellation are provided by
192: * the given progress monitor.
193: * </p>
194: * <p>
195: * The default implementation of this method calls
196: * {@link #doSave(IProgressMonitor)} and returns <code>null</code>.
197: * </p>
198: *
199: * @param monitor
200: * a progress monitor used for reporting progress and
201: * cancellation
202: * @param shellProvider
203: * an object that can provide a shell for parenting dialogs
204: * @return <code>null</code> if this saveable has been saved successfully,
205: * or a job runnable that needs to be run to complete the save in
206: * the background.
207: *
208: * @since 3.3
209: */
210: public IJobRunnable doSave(IProgressMonitor monitor,
211: IShellProvider shellProvider) throws CoreException {
212: doSave(monitor);
213: return null;
214: }
215:
216: /**
217: * Disables the UI of the given parts containing this saveable if necessary.
218: * This method is not intended to be called by clients. A corresponding call
219: * to
220: * <p>
221: * Saveables that can be saved in the background should ensure that the user
222: * cannot make changes to their data from the UI, for example by disabling
223: * controls, unless they are prepared to handle this case. This method is
224: * called on the UI thread after a job runnable has been returned from
225: * {@link #doSave(IProgressMonitor, IShellProvider)} and before
226: * spinning the event loop. The <code>closing</code> flag indicates that
227: * this saveable is currently being saved in response to closing a workbench
228: * part, in which case further changes to this saveable through the UI must
229: * be prevented.
230: * </p>
231: * <p>
232: * The default implementation calls setEnabled(false) on the given parts'
233: * composites.
234: * </p>
235: *
236: * @param parts
237: * the workbench parts containing this saveable
238: * @param closing
239: * a boolean flag indicating whether the save was triggered by a
240: * request to close a workbench part, and all of the given parts
241: * will be closed after the save operation finishes successfully.
242: *
243: * @since 3.3
244: */
245: public void disableUI(IWorkbenchPart[] parts, boolean closing) {
246: for (int i = 0; i < parts.length; i++) {
247: IWorkbenchPart workbenchPart = parts[i];
248: Composite paneComposite = (Composite) ((PartSite) workbenchPart
249: .getSite()).getPane().getControl();
250: Control[] paneChildren = paneComposite.getChildren();
251: Composite toDisable = ((Composite) paneChildren[0]);
252: toDisable.setEnabled(false);
253: if (waitCursor == null) {
254: waitCursor = new Cursor(workbenchPart.getSite()
255: .getWorkbenchWindow().getShell().getDisplay(),
256: SWT.CURSOR_WAIT);
257: }
258: originalCursor = paneComposite.getCursor();
259: paneComposite.setCursor(waitCursor);
260: }
261: }
262:
263: /**
264: * Enables the UI of the given parts containing this saveable after a
265: * background save operation has finished. This method is not intended to be
266: * called by clients.
267: * <p>
268: * The default implementation calls setEnabled(true) on the given parts'
269: * composites.
270: * </p>
271: *
272: * @param parts
273: * the workbench parts containing this saveable
274: *
275: * @since 3.3
276: */
277: public void enableUI(IWorkbenchPart[] parts) {
278: for (int i = 0; i < parts.length; i++) {
279: IWorkbenchPart workbenchPart = parts[i];
280: Composite paneComposite = (Composite) ((PartSite) workbenchPart
281: .getSite()).getPane().getControl();
282: Control[] paneChildren = paneComposite.getChildren();
283: Composite toEnable = ((Composite) paneChildren[0]);
284: paneComposite.setCursor(originalCursor);
285: if (waitCursor != null && !waitCursor.isDisposed()) {
286: waitCursor.dispose();
287: waitCursor = null;
288: }
289: toEnable.setEnabled(true);
290: }
291: }
292:
293: /**
294: * This implementation of {@link IAdaptable#getAdapter(Class)} returns
295: * <code>null</code>. Subclasses may override. This allows two unrelated
296: * subclasses of Saveable to implement {@link #equals(Object)} and
297: * {@link #hashCode()} based on an underlying implementation class that is
298: * shared by both Saveable subclasses.
299: *
300: * @since 3.3
301: */
302: public Object getAdapter(Class adapter) {
303: return null;
304: }
305: }
|