Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 154122 | Differences between
and this patch

Collapse All | Expand All

(-)plugin.xml (+8 lines)
Lines 663-668 Link Here
663
            icon="icons/mockeditorpart1.gif"
663
            icon="icons/mockeditorpart1.gif"
664
            id="org.eclipse.ui.tests.multipageeditor.MultiVariablePageEditor"
664
            id="org.eclipse.ui.tests.multipageeditor.MultiVariablePageEditor"
665
            name="Multi Variable Page Editor"/>
665
            name="Multi Variable Page Editor"/>
666
      <editor
667
            class="org.eclipse.ui.tests.manual.TestBackgroundSaveEditor"
668
            default="false"
669
            extensions="background"
670
            icon="icons/binary_co.gif"
671
            id="org.eclipse.ui.tests.manual.TestBackgroundSaveEditor"
672
            name="TestBackgroundSaveEditor">
673
      </editor>
666
   </extension>
674
   </extension>
667
   <extension
675
   <extension
668
         point="org.eclipse.ui.actionSets">
676
         point="org.eclipse.ui.actionSets">
(-)META-INF/MANIFEST.MF (-1 / +4 lines)
Lines 21-27 Link Here
21
 org.eclipse.test.performance,
21
 org.eclipse.test.performance,
22
 org.eclipse.core.tests.harness,
22
 org.eclipse.core.tests.harness,
23
 org.eclipse.ui.tests.harness,
23
 org.eclipse.ui.tests.harness,
24
 org.eclipse.core.filesystem
24
 org.eclipse.core.filesystem,
25
 org.eclipse.core.databinding,
26
 org.eclipse.core.databinding.beans,
27
 org.eclipse.jface.databinding
25
Eclipse-AutoStart: true
28
Eclipse-AutoStart: true
26
Plugin-Class: org.eclipse.ui.tests.TestPlugin
29
Plugin-Class: org.eclipse.ui.tests.TestPlugin
27
Export-Package: org.eclipse.ui.tests.menus
30
Export-Package: org.eclipse.ui.tests.menus
(-)Eclipse (+496 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.tests.manual;
13
14
import java.beans.PropertyChangeListener;
15
import java.beans.PropertyChangeSupport;
16
17
import org.eclipse.core.databinding.DataBindingContext;
18
import org.eclipse.core.databinding.beans.BeansObservables;
19
import org.eclipse.core.databinding.observable.Realm;
20
import org.eclipse.core.databinding.observable.value.IObservableValue;
21
import org.eclipse.core.runtime.Assert;
22
import org.eclipse.core.runtime.CoreException;
23
import org.eclipse.core.runtime.IProgressMonitor;
24
import org.eclipse.core.runtime.IStatus;
25
import org.eclipse.core.runtime.Status;
26
import org.eclipse.core.runtime.SubMonitor;
27
import org.eclipse.jface.databinding.swt.SWTObservables;
28
import org.eclipse.jface.dialogs.MessageDialog;
29
import org.eclipse.jface.layout.GridDataFactory;
30
import org.eclipse.jface.layout.GridLayoutFactory;
31
import org.eclipse.jface.resource.ImageDescriptor;
32
import org.eclipse.jface.window.IShellProvider;
33
import org.eclipse.swt.SWT;
34
import org.eclipse.swt.events.DisposeEvent;
35
import org.eclipse.swt.events.DisposeListener;
36
import org.eclipse.swt.widgets.Button;
37
import org.eclipse.swt.widgets.Composite;
38
import org.eclipse.swt.widgets.Group;
39
import org.eclipse.swt.widgets.Label;
40
import org.eclipse.swt.widgets.Text;
41
import org.eclipse.ui.IEditorInput;
42
import org.eclipse.ui.IEditorSite;
43
import org.eclipse.ui.IFileEditorInput;
44
import org.eclipse.ui.ISaveablePart;
45
import org.eclipse.ui.ISaveablesSource;
46
import org.eclipse.ui.PartInitException;
47
import org.eclipse.ui.PlatformUI;
48
import org.eclipse.ui.Saveable;
49
import org.eclipse.ui.internal.WorkbenchMessages;
50
import org.eclipse.ui.internal.WorkbenchPlugin;
51
import org.eclipse.ui.internal.progress.WorkbenchSiteProgressService;
52
import org.eclipse.ui.part.EditorPart;
53
import org.eclipse.ui.progress.IJobRunnable;
54
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
55
56
/**
57
 * @since 3.3
58
 * 
59
 */
60
public class TestBackgroundSaveEditor extends EditorPart implements
61
		ISaveablesSource {
62
63
	public class MySaveable extends Saveable {
64
65
		PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
66
67
		private boolean dirty;
68
69
		public boolean supportsBackgroundSave() {
70
			return true;
71
		}
72
		
73
		public void doSave(IProgressMonitor monitor) throws CoreException {
74
			SubMonitor subMonitor = SubMonitor.convert(monitor, 2);
75
			IJobRunnable runnable = doSave(subMonitor.newChild(1), getSite());
76
			if (runnable!=null) {
77
				runnable.run(subMonitor.newChild(1));
78
			}
79
			monitor.done();
80
		}
81
82
		public IJobRunnable doSave(IProgressMonitor monitor,
83
				IShellProvider shellProvider) throws CoreException {
84
			monitor.beginTask("Saving in the foreground",
85
					data.foregroundSaveTime);
86
			data.setOutput("");
87
			for (int i = 0; i < data.foregroundSaveTime; i++) {
88
				try {
89
					Thread.sleep(1000);
90
				} catch (InterruptedException e) {
91
					Thread.currentThread().interrupt();
92
				}
93
				data.setOutput(data.getInput().substring(0,
94
						Math.min(i, data.getInput().length())));
95
				monitor.worked(1);
96
			}
97
			if (data.throwExceptionInForeground) {
98
				throw new CoreException(new Status(IStatus.ERROR,
99
						"org.eclipse.ui.tests",
100
						"Saving in the foreground failed"));
101
			}
102
			monitor.done();
103
			if (!data.saveInBackground) {
104
				data.setOutput(data.getInput());
105
				setDirty(false);
106
				return null;
107
			}
108
			WorkbenchSiteProgressService progressService = (WorkbenchSiteProgressService) getSite()
109
					.getAdapter(IWorkbenchSiteProgressService.class);
110
			IJobRunnable result = new IJobRunnable() {
111
				public IStatus run(IProgressMonitor monitor) {
112
					monitor.beginTask("Saving in the background",
113
							data.backgroundSaveTime);
114
					for (int i = 0; i < data.backgroundSaveTime; i++) {
115
						try {
116
							Thread.sleep(1000);
117
						} catch (InterruptedException e) {
118
							Thread.currentThread().interrupt();
119
						}
120
						data.setOutput(data.getInput().substring(
121
								0,
122
								Math.min(i + data.foregroundSaveTime, data
123
										.getInput().length())));
124
						monitor.worked(1);
125
					}
126
					if (data.throwExceptionInBackground) {
127
						return new Status(IStatus.ERROR,
128
								"org.eclipse.ui.tests",
129
								"Saving in the background failed");
130
					}
131
					data.setOutput(data.getInput());
132
					setDirty(false);
133
					monitor.done();
134
					return Status.OK_STATUS;
135
				}
136
			};
137
			return result;
138
		}
139
140
		public boolean equals(Object object) {
141
			return this == object;
142
		}
143
144
		public ImageDescriptor getImageDescriptor() {
145
			return input.getImageDescriptor();
146
		}
147
148
		public String getName() {
149
			return input.getName();
150
		}
151
152
		public String getToolTipText() {
153
			return input.getToolTipText();
154
		}
155
156
		public int hashCode() {
157
			return System.identityHashCode(this);
158
		}
159
160
		public boolean isDirty() {
161
			return dirty;
162
		}
163
164
		public void setDirty(boolean dirty) {
165
			firePropertyChange("dirty", new Boolean(this.dirty), new Boolean(
166
					this.dirty = dirty));
167
			getSite().getShell().getDisplay().syncExec(new Runnable(){
168
				public void run() {
169
					TestBackgroundSaveEditor.this
170
					.firePropertyChange(ISaveablePart.PROP_DIRTY);
171
				}});
172
		}
173
174
		public void addPropertyChangeListener(String propertyName,
175
				PropertyChangeListener listener) {
176
			changeSupport.addPropertyChangeListener(propertyName, listener);
177
		}
178
179
		void firePropertyChange(String propertyName, Object oldValue,
180
				Object newValue) {
181
			changeSupport.firePropertyChange(propertyName, oldValue, newValue);
182
		}
183
184
		public void removePropertyChangeListener(String propertyName,
185
				PropertyChangeListener listener) {
186
			changeSupport.removePropertyChangeListener(propertyName, listener);
187
		}
188
	}
189
190
	private MySaveable mySaveable;
191
	private Text inputText;
192
	private IEditorInput input;
193
	private Composite myComposite;
194
195
	public TestBackgroundSaveEditor() {
196
		mySaveable = new MySaveable();
197
	}
198
199
	public void createPartControl(Composite parent) {
200
		myComposite = parent;
201
		Realm realm = SWTObservables.getRealm(parent.getDisplay());
202
		final DataBindingContext dbc = new DataBindingContext(realm);
203
		parent.addDisposeListener(new DisposeListener() {
204
			public void widgetDisposed(DisposeEvent e) {
205
				dbc.dispose();
206
			}
207
		});
208
209
		final IObservableValue inputObservable = BeansObservables.observeValue(
210
				realm, data, "input");
211
		final IObservableValue outputObservable = BeansObservables
212
				.observeValue(realm, data, "output");
213
214
		createInputGroup(parent, dbc, inputObservable);
215
		createOptionsGroup(parent, realm, dbc, inputObservable,
216
				outputObservable);
217
		createOutputGroup(parent, dbc, outputObservable);
218
219
		GridLayoutFactory.swtDefaults().numColumns(3).equalWidth(true)
220
				.generateLayout(parent);
221
	}
222
223
	private void createOutputGroup(Composite parent,
224
			final DataBindingContext dbc,
225
			final IObservableValue outputObservable) {
226
		Group outputGroup = new Group(parent, SWT.NONE);
227
		outputGroup.setText("Output");
228
		Text outputText = new Text(outputGroup, SWT.BORDER | SWT.READ_ONLY
229
				| SWT.MULTI);
230
		GridDataFactory.fillDefaults().grab(true, true).applyTo(outputText);
231
		dbc.bindValue(SWTObservables.observeText(outputText, SWT.NONE),
232
				outputObservable, null);
233
		GridLayoutFactory.swtDefaults().generateLayout(outputGroup);
234
	}
235
236
	private void createOptionsGroup(Composite parent, Realm realm,
237
			final DataBindingContext dbc,
238
			final IObservableValue inputObservable,
239
			final IObservableValue outputObservable) {
240
		Group optionsGroup = new Group(parent, SWT.NONE);
241
		optionsGroup.setText("Options");
242
243
		Button dirtyButton = new Button(optionsGroup, SWT.CHECK);
244
		new Label(optionsGroup, SWT.NONE).setText("Editor is dirty");
245
		IObservableValue dirtyObservable = BeansObservables.observeValue(realm,
246
				mySaveable, "dirty");
247
		dbc.bindValue(SWTObservables.observeSelection(dirtyButton),
248
				dirtyObservable, null);
249
		// IObservableValue inputAndOutputDiffer = new ComputedValue(realm) {
250
		// protected Object calculate() {
251
		// return new Boolean(!Util.equals(inputObservable.getValue(),
252
		// outputObservable.getValue()));
253
		// }
254
		// };
255
		// dbc.bindValue(dirtyObservable, inputAndOutputDiffer, null);
256
257
		Button saveInBackgroundButton = new Button(optionsGroup, SWT.CHECK);
258
		new Label(optionsGroup, SWT.NONE)
259
				.setText("Do part of the save in the background");
260
		dbc.bindValue(SWTObservables.observeSelection(saveInBackgroundButton),
261
				BeansObservables.observeValue(realm, data, "saveInBackground"),
262
				null);
263
264
		Button foregroundExceptionButton = new Button(optionsGroup, SWT.CHECK);
265
		new Label(optionsGroup, SWT.NONE)
266
				.setText("Throw exception while saving in the foreground");
267
		dbc.bindValue(SWTObservables
268
				.observeSelection(foregroundExceptionButton), BeansObservables
269
				.observeValue(realm, data, "throwExceptionInForeground"), null);
270
271
		Button backgroundExceptionButton = new Button(optionsGroup, SWT.CHECK);
272
		new Label(optionsGroup, SWT.NONE)
273
				.setText("Throw exception while saving in the background");
274
		dbc.bindValue(SWTObservables
275
				.observeSelection(backgroundExceptionButton), BeansObservables
276
				.observeValue(realm, data, "throwExceptionInBackground"), null);
277
278
		new Label(optionsGroup, SWT.NONE).setText("Foreground save time:");
279
		Text optionsForegroundTime = new Text(optionsGroup, SWT.BORDER);
280
		dbc.bindValue(SWTObservables.observeText(optionsForegroundTime,
281
				SWT.Modify), BeansObservables.observeValue(realm, data,
282
				"foregroundSaveTime"), null);
283
284
		new Label(optionsGroup, SWT.NONE).setText("Background save time:");
285
		Text optionsBackgroundTime = new Text(optionsGroup, SWT.BORDER);
286
		dbc.bindValue(SWTObservables.observeText(optionsBackgroundTime,
287
				SWT.Modify), BeansObservables.observeValue(realm, data,
288
				"backgroundSaveTime"), null);
289
290
		GridLayoutFactory.swtDefaults().numColumns(2).generateLayout(
291
				optionsGroup);
292
	}
293
294
	private void createInputGroup(Composite parent,
295
			final DataBindingContext dbc, final IObservableValue inputObservable) {
296
		Group inputGroup = new Group(parent, SWT.NONE);
297
		inputGroup.setText("Input");
298
299
		inputText = new Text(inputGroup, SWT.BORDER | SWT.MULTI);
300
		dbc.bindValue(SWTObservables.observeText(inputText, SWT.Modify),
301
				inputObservable, null);
302
303
		GridLayoutFactory.swtDefaults().generateLayout(inputGroup);
304
	}
305
306
	public void doSave(IProgressMonitor monitor) {
307
		try {
308
			mySaveable.doSave(monitor);
309
		} catch (CoreException e) {
310
			String title = "Save failed";
311
			WorkbenchPlugin.log(title, new Status(IStatus.WARNING,
312
					PlatformUI.PLUGIN_ID, 0, title, e));
313
			MessageDialog.openError(getSite().getShell(),
314
					WorkbenchMessages.Error, title + ':' + e.getMessage());
315
		}
316
	}
317
318
	public void doSaveAs() {
319
		Assert.isTrue(false, "Should not be called");
320
	}
321
322
	public void init(IEditorSite site, IEditorInput input)
323
			throws PartInitException {
324
		if (!(input instanceof IFileEditorInput))
325
			throw new PartInitException(
326
					"Invalid Input: Must be IFileEditorInput");
327
		setSite(site);
328
		setInput(input);
329
		this.input = input;
330
	}
331
332
	public boolean isDirty() {
333
		return mySaveable.isDirty();
334
	}
335
336
	public boolean isSaveAsAllowed() {
337
		return false;
338
	}
339
340
	public void setFocus() {
341
		inputText.setFocus();
342
	}
343
344
	public static class Data {
345
		PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
346
		public String input;
347
		public String output;
348
		public String buffer;
349
		public boolean saveInBackground;
350
		public boolean throwExceptionInForeground;
351
		public boolean throwExceptionInBackground;
352
		public int foregroundSaveTime;
353
		public int backgroundSaveTime;
354
355
		public String getOutput() {
356
			return output;
357
		}
358
359
		public void setOutput(String output) {
360
			firePropertyChange("output", this.output, this.output = output);
361
		}
362
363
		public void addPropertyChangeListener(String propertyName,
364
				PropertyChangeListener listener) {
365
			changeSupport.addPropertyChangeListener(propertyName, listener);
366
		}
367
368
		void firePropertyChange(String propertyName, Object oldValue,
369
				Object newValue) {
370
			changeSupport.firePropertyChange(propertyName, oldValue, newValue);
371
		}
372
373
		public void removePropertyChangeListener(String propertyName,
374
				PropertyChangeListener listener) {
375
			changeSupport.removePropertyChangeListener(propertyName, listener);
376
		}
377
378
		/**
379
		 * @return Returns the input.
380
		 */
381
		public String getInput() {
382
			return input;
383
		}
384
385
		/**
386
		 * @param input
387
		 *            The input to set.
388
		 */
389
		public void setInput(String input) {
390
			this.input = input;
391
		}
392
393
		/**
394
		 * @return Returns the buffer.
395
		 */
396
		public String getBuffer() {
397
			return buffer;
398
		}
399
400
		/**
401
		 * @param buffer
402
		 *            The buffer to set.
403
		 */
404
		public void setBuffer(String buffer) {
405
			this.buffer = buffer;
406
		}
407
408
		/**
409
		 * @return Returns the saveInBackground.
410
		 */
411
		public boolean isSaveInBackground() {
412
			return saveInBackground;
413
		}
414
415
		/**
416
		 * @param saveInBackground
417
		 *            The saveInBackground to set.
418
		 */
419
		public void setSaveInBackground(boolean saveInBackground) {
420
			this.saveInBackground = saveInBackground;
421
		}
422
423
		/**
424
		 * @return Returns the throwExceptionInForeground.
425
		 */
426
		public boolean isThrowExceptionInForeground() {
427
			return throwExceptionInForeground;
428
		}
429
430
		/**
431
		 * @param throwExceptionInForeground
432
		 *            The throwExceptionInForeground to set.
433
		 */
434
		public void setThrowExceptionInForeground(
435
				boolean throwExceptionInForeground) {
436
			this.throwExceptionInForeground = throwExceptionInForeground;
437
		}
438
439
		/**
440
		 * @return Returns the throwExceptionInBackground.
441
		 */
442
		public boolean isThrowExceptionInBackground() {
443
			return throwExceptionInBackground;
444
		}
445
446
		/**
447
		 * @param throwExceptionInBackground
448
		 *            The throwExceptionInBackground to set.
449
		 */
450
		public void setThrowExceptionInBackground(
451
				boolean throwExceptionInBackground) {
452
			this.throwExceptionInBackground = throwExceptionInBackground;
453
		}
454
455
		/**
456
		 * @return Returns the foregroundSaveTime.
457
		 */
458
		public int getForegroundSaveTime() {
459
			return foregroundSaveTime;
460
		}
461
462
		/**
463
		 * @param foregroundSaveTime
464
		 *            The foregroundSaveTime to set.
465
		 */
466
		public void setForegroundSaveTime(int foregroundSaveTime) {
467
			this.foregroundSaveTime = foregroundSaveTime;
468
		}
469
470
		/**
471
		 * @return Returns the backgroundSaveTime.
472
		 */
473
		public int getBackgroundSaveTime() {
474
			return backgroundSaveTime;
475
		}
476
477
		/**
478
		 * @param backgroundSaveTime
479
		 *            The backgroundSaveTime to set.
480
		 */
481
		public void setBackgroundSaveTime(int backgroundSaveTime) {
482
			this.backgroundSaveTime = backgroundSaveTime;
483
		}
484
	}
485
486
	private Data data = new Data();
487
488
	public Saveable[] getActiveSaveables() {
489
		return new Saveable[] { mySaveable };
490
	}
491
492
	public Saveable[] getSaveables() {
493
		return new Saveable[] { mySaveable };
494
	}
495
	
496
}
(-)Eclipse UI/org/eclipse/ui/internal/WorkbenchWindow.java (+18 lines)
Lines 2859-2864 Link Here
2859
2859
2860
	private ListenerList actionSetListeners = null;
2860
	private ListenerList actionSetListeners = null;
2861
2861
2862
	private ListenerList backgroundSaveListeners = new ListenerList(ListenerList.IDENTITY);
2863
2862
	private final void fireActionSetsChanged() {
2864
	private final void fireActionSetsChanged() {
2863
		if (actionSetListeners != null) {
2865
		if (actionSetListeners != null) {
2864
			final Object[] listeners = actionSetListeners.getListeners();
2866
			final Object[] listeners = actionSetListeners.getListeners();
Lines 3676-3679 Link Here
3676
			setPerspectiveBarVisible(!perspectivebarVisible);
3678
			setPerspectiveBarVisible(!perspectivebarVisible);
3677
		getShell().layout();
3679
		getShell().layout();
3678
	}
3680
	}
3681
3682
	/*package*/ void addBackgroundSaveListener(IBackgroundSaveListener listener) {
3683
		backgroundSaveListeners.add(listener);
3684
	}
3685
	
3686
	/*package*/ void fireBackgroundSaveStarted() {
3687
		Object[] listeners = backgroundSaveListeners.getListeners();
3688
		for (int i = 0; i < listeners.length; i++) {
3689
			IBackgroundSaveListener listener = (IBackgroundSaveListener) listeners[i];
3690
			listener.handleBackgroundSaveStarted();
3691
		}
3692
	}
3693
3694
	/*package*/ void removeBackgroundSaveListener(IBackgroundSaveListener listener) {
3695
		backgroundSaveListeners.remove(listener);
3696
	}
3679
}
3697
}
(-)Eclipse UI/org/eclipse/ui/internal/SaveAction.java (-1 / +11 lines)
Lines 19-25 Link Here
19
/**
19
/**
20
 * Workbench common <code>Save</code> action.
20
 * Workbench common <code>Save</code> action.
21
 */
21
 */
22
public class SaveAction extends BaseSaveAction {
22
public class SaveAction extends BaseSaveAction implements IBackgroundSaveListener {
23
23
24
    /**
24
    /**
25
     * Create an instance of this class
25
     * Create an instance of this class
Lines 38-43 Link Here
38
        setDisabledImageDescriptor(WorkbenchImages
38
        setDisabledImageDescriptor(WorkbenchImages
39
                .getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_SAVE_EDIT_DISABLED));
39
                .getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_SAVE_EDIT_DISABLED));
40
        setActionDefinitionId("org.eclipse.ui.file.save"); //$NON-NLS-1$
40
        setActionDefinitionId("org.eclipse.ui.file.save"); //$NON-NLS-1$
41
        ((WorkbenchWindow)window).addBackgroundSaveListener(this);
42
    }
43
    
44
    public void dispose() {
45
    	super.dispose();
46
    	((WorkbenchWindow)getWorkbenchWindow()).removeBackgroundSaveListener(this);
41
    }
47
    }
42
48
43
    /* (non-Javadoc)
49
    /* (non-Javadoc)
Lines 88-91 Link Here
88
        }
94
        }
89
        setEnabled(saveable != null && saveable.isDirty());
95
        setEnabled(saveable != null && saveable.isDirty());
90
    }
96
    }
97
98
	public void handleBackgroundSaveStarted() {
99
		updateState();
100
	}
91
}
101
}
(-)Eclipse UI/org/eclipse/ui/internal/WorkbenchMessages.java (+1 lines)
Lines 521-526 Link Here
521
    public static String EditorManager_bad_element_factory;
521
    public static String EditorManager_bad_element_factory;
522
    public static String EditorManager_create_element_returned_null;
522
    public static String EditorManager_create_element_returned_null;
523
    public static String EditorManager_wrong_createElement_result;
523
    public static String EditorManager_wrong_createElement_result;
524
    public static String EditorManager_backgroundSaveJobName;
524
    
525
    
525
	public static String EditorPane_pinEditor;
526
	public static String EditorPane_pinEditor;
526
527
(-)Eclipse UI/org/eclipse/ui/internal/messages.properties (+1 lines)
Lines 517-522 Link Here
517
EditorManager_invalid_editor_descriptor=Invalid editor descriptor for id {0}
517
EditorManager_invalid_editor_descriptor=Invalid editor descriptor for id {0}
518
EditorManager_problemsSavingEditors=Problems occurred saving editors.
518
EditorManager_problemsSavingEditors=Problems occurred saving editors.
519
EditorManager_unableToSaveEditor=Unable to save editor: {0}.
519
EditorManager_unableToSaveEditor=Unable to save editor: {0}.
520
EditorManager_backgroundSaveJobName=Saving {0}
520
521
521
EditorPane_pinEditor=&Pin Editor
522
EditorPane_pinEditor=&Pin Editor
522
523
(-)Eclipse UI/org/eclipse/ui/internal/PartPane.java (-2 / +8 lines)
Lines 37-42 Link Here
37
import org.eclipse.ui.IWorkbenchPartReference;
37
import org.eclipse.ui.IWorkbenchPartReference;
38
import org.eclipse.ui.internal.dnd.SwtUtil;
38
import org.eclipse.ui.internal.dnd.SwtUtil;
39
import org.eclipse.ui.part.MultiEditor;
39
import org.eclipse.ui.part.MultiEditor;
40
import org.eclipse.ui.presentations.IPresentablePart;
40
41
41
/**
42
/**
42
 * Provides the common behavior for both views
43
 * Provides the common behavior for both views
Lines 94-99 Link Here
94
95
95
    };
96
    };
96
97
98
	private boolean busy;
99
97
    public static class Sashes {
100
    public static class Sashes {
98
        public Sash left;
101
        public Sash left;
99
102
Lines 491-497 Link Here
491
     * Set the busy state of the pane.
494
     * Set the busy state of the pane.
492
     */
495
     */
493
    public void setBusy(boolean isBusy) {
496
    public void setBusy(boolean isBusy) {
494
        //Do nothing by default
497
        if (isBusy != busy) {
498
            busy = isBusy;
499
            firePropertyChange(IPresentablePart.PROP_BUSY);
500
        }
495
    }
501
    }
496
502
497
    /**
503
    /**
Lines 524-530 Link Here
524
    }
530
    }
525
    
531
    
526
    public boolean isBusy() {
532
    public boolean isBusy() {
527
        return false;
533
        return busy;
528
    }
534
    }
529
535
530
    /**
536
    /**
(-)Eclipse UI/org/eclipse/ui/internal/ViewPane.java (-20 lines)
Lines 40-46 Link Here
40
 * TODO: Delete ViewPane and EditorPane, and make PartPane non-abstract.
40
 * TODO: Delete ViewPane and EditorPane, and make PartPane non-abstract.
41
 */
41
 */
42
public class ViewPane extends PartPane {
42
public class ViewPane extends PartPane {
43
    private boolean busy = false;
44
43
45
    // create initially toolbarless bar manager so that actions may be added in the 
44
    // create initially toolbarless bar manager so that actions may be added in the 
46
    // init method of the view.
45
    // init method of the view.
Lines 548-572 Link Here
548
        return toolbarManager.getItemCount() > 0;
547
        return toolbarManager.getItemCount() > 0;
549
    }
548
    }
550
549
551
    /*
552
     *  (non-Javadoc)
553
     * @see org.eclipse.ui.internal.PartPane#setBusy(boolean)
554
     */
555
    public void setBusy(boolean isBusy) {
556
        if (isBusy != busy) {
557
            busy = isBusy;
558
            firePropertyChange(IPresentablePart.PROP_BUSY);
559
        }
560
    }
561
562
    /**
563
     * Return the busy state of the receiver.
564
     * @return boolean
565
     */
566
    public boolean isBusy() {
567
        return busy;
568
    }
569
570
    /* (non-Javadoc)
550
    /* (non-Javadoc)
571
     * @see org.eclipse.ui.internal.PartPane#showHighlight()
551
     * @see org.eclipse.ui.internal.PartPane#showHighlight()
572
     */
552
     */
(-)Eclipse UI/org/eclipse/ui/internal/SaveableHelper.java (-8 / +201 lines)
Lines 12-24 Link Here
12
12
13
import java.lang.reflect.InvocationTargetException;
13
import java.lang.reflect.InvocationTargetException;
14
import java.util.ArrayList;
14
import java.util.ArrayList;
15
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.Iterator;
17
import java.util.List;
18
import java.util.Set;
16
19
17
import org.eclipse.core.runtime.CoreException;
20
import org.eclipse.core.runtime.CoreException;
18
import org.eclipse.core.runtime.IProgressMonitor;
21
import org.eclipse.core.runtime.IProgressMonitor;
19
import org.eclipse.core.runtime.IStatus;
22
import org.eclipse.core.runtime.IStatus;
20
import org.eclipse.core.runtime.Status;
23
import org.eclipse.core.runtime.Status;
24
import org.eclipse.core.runtime.SubMonitor;
21
import org.eclipse.core.runtime.SubProgressMonitor;
25
import org.eclipse.core.runtime.SubProgressMonitor;
26
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
27
import org.eclipse.core.runtime.jobs.ISchedulingRule;
28
import org.eclipse.core.runtime.jobs.Job;
29
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
22
import org.eclipse.jface.dialogs.ErrorDialog;
30
import org.eclipse.jface.dialogs.ErrorDialog;
23
import org.eclipse.jface.dialogs.IDialogConstants;
31
import org.eclipse.jface.dialogs.IDialogConstants;
24
import org.eclipse.jface.dialogs.MessageDialog;
32
import org.eclipse.jface.dialogs.MessageDialog;
Lines 28-39 Link Here
28
import org.eclipse.osgi.util.NLS;
36
import org.eclipse.osgi.util.NLS;
29
import org.eclipse.ui.ISaveablePart;
37
import org.eclipse.ui.ISaveablePart;
30
import org.eclipse.ui.ISaveablePart2;
38
import org.eclipse.ui.ISaveablePart2;
39
import org.eclipse.ui.ISaveablesLifecycleListener;
31
import org.eclipse.ui.ISaveablesSource;
40
import org.eclipse.ui.ISaveablesSource;
32
import org.eclipse.ui.IWorkbenchPart;
41
import org.eclipse.ui.IWorkbenchPart;
33
import org.eclipse.ui.IWorkbenchWindow;
42
import org.eclipse.ui.IWorkbenchWindow;
34
import org.eclipse.ui.PlatformUI;
43
import org.eclipse.ui.PlatformUI;
35
import org.eclipse.ui.Saveable;
44
import org.eclipse.ui.Saveable;
36
import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
45
import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
46
import org.eclipse.ui.progress.IJobRunnable;
47
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
37
48
38
/**
49
/**
39
 * Helper class for prompting to save dirty views or editors.
50
 * Helper class for prompting to save dirty views or editors.
Lines 168-179 Link Here
168
						monitor.worked(1);
179
						monitor.worked(1);
169
						continue;
180
						continue;
170
					}
181
					}
171
					try {
182
					doSaveModel(model, new SubProgressMonitor(monitorWrap, 1),
172
						model.doSave(new SubProgressMonitor(monitorWrap, 1));
183
							(WorkbenchWindow) window, false);
173
					}
174
					catch (CoreException e) {
175
						ErrorDialog.openError(window.getShell(), WorkbenchMessages.Error, e.getMessage(), e.getStatus());
176
					}
177
					if (monitor.isCanceled()) {
184
					if (monitor.isCanceled()) {
178
						break;
185
						break;
179
					}
186
					}
Lines 288-298 Link Here
288
		Saveable[] selectedModels = modelSource.getActiveSaveables();
295
		Saveable[] selectedModels = modelSource.getActiveSaveables();
289
		for (int i = 0; i < selectedModels.length; i++) {
296
		for (int i = 0; i < selectedModels.length; i++) {
290
			Saveable model = selectedModels[i];
297
			Saveable model = selectedModels[i];
291
			if (model.isDirty()) {
298
			if (model.isDirty() && !((InternalSaveable)model).isSavingInBackground()) {
292
				return true;
299
				return true;
293
			}
300
			}
294
		}
301
		}
295
		return false;
302
		return false;
296
	}
303
	}
297
	
304
305
	/**
306
	 * @param model
307
	 * @param progressMonitor
308
	 * @param shellProvider
309
	 * @param closing
310
	 */
311
	public static void doSaveModel(final Saveable model,
312
			IProgressMonitor progressMonitor,
313
			final IShellProvider shellProvider, boolean closing) {
314
		final ISchedulingRule schedulingRule = ((InternalSaveable) model)
315
				.getSchedulingRule();
316
		boolean ruleTransferred = false;
317
		try {
318
			if (model.supportsBackgroundSave()) {
319
				Job.getJobManager().beginRule(schedulingRule, null);
320
			} else {
321
				// To be safe for 3.3M4, don't lock if the saveable does not support background saves
322
				ruleTransferred = true;
323
			}
324
			final IJobRunnable[] backgroundSaveRunnable = new IJobRunnable[1];
325
			try {
326
				SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 3);
327
				backgroundSaveRunnable[0] = model.doSave(
328
						subMonitor.newChild(2), shellProvider);
329
				if (backgroundSaveRunnable[0] == null) {
330
					return;
331
				}
332
				if (closing || !model.supportsBackgroundSave()) {
333
					// for now, block on close by running the runnable in the UI
334
					// thread
335
					IStatus result = backgroundSaveRunnable[0].run(subMonitor
336
							.newChild(1));
337
					if (!result.isOK()) {
338
						ErrorDialog.openError(shellProvider.getShell(),
339
								WorkbenchMessages.Error, result.getMessage(),
340
								result);
341
						progressMonitor.setCanceled(true);
342
					}
343
					return;
344
				}
345
				// we will need the associated parts (for disabling their UI)
346
				((InternalSaveable) model).setSavingInBackground(true);
347
				SaveablesList saveablesList = (SaveablesList) PlatformUI
348
						.getWorkbench().getService(
349
								ISaveablesLifecycleListener.class);
350
				final IWorkbenchPart[] parts = saveablesList
351
						.getPartsForSaveable(model);
352
				{
353
					// about to start the job - notify the save actions,
354
					// this is done through the workbench windows, which
355
					// we can get from the parts...
356
					Set wwindows = new HashSet();
357
					for (int i = 0; i < parts.length; i++) {
358
						wwindows.add(parts[i].getSite().getWorkbenchWindow());
359
					}
360
					for (Iterator it = wwindows.iterator(); it.hasNext();) {
361
						WorkbenchWindow wwin = (WorkbenchWindow) it.next();
362
						wwin.fireBackgroundSaveStarted();
363
					}
364
				}
365
				// for the job family, we use the model object because based on
366
				// the family we can display the busy state with an animated tab
367
				// (see the calls to showBusyForFamily() below).
368
				// As a "lock", we use a scheduling rule obtained from the model.
369
				// This lock is already held by the UI thread at this time. We
370
				// will transfer it to the job. This lock is necessary so that
371
				// saves or close-on-dirty that happen later, but still concurrent
372
				// with the background save, are blocked until the background save
373
				// is finished.
374
				Job saveJob = new Job(NLS.bind(
375
						WorkbenchMessages.EditorManager_backgroundSaveJobName,
376
						model.getName())) {
377
					public boolean belongsTo(Object family) {
378
						return family.equals(model);
379
					}
380
381
					protected IStatus run(IProgressMonitor monitor) {
382
						try {
383
							Job.getJobManager().beginRule(schedulingRule, null);
384
							return backgroundSaveRunnable[0].run(monitor);
385
						} finally {
386
							Job.getJobManager().endRule(schedulingRule);
387
							Job.getJobManager().endRule(schedulingRule);
388
						}
389
					}
390
				};
391
				// this will cause the parts tabs to show the ongoing background operation
392
				for (int i = 0; i < parts.length; i++) {
393
					IWorkbenchPart workbenchPart = parts[i];
394
					IWorkbenchSiteProgressService progressService = (IWorkbenchSiteProgressService) workbenchPart
395
							.getSite().getAdapter(
396
									IWorkbenchSiteProgressService.class);
397
					progressService.showBusyForFamily(model);
398
				}
399
				model.disableUI(parts, closing);
400
				// Add a listener for enabling the UI after the save job has
401
				// finished, and for displaying an error dialog if
402
				// necessary. We also use this to notify the UI thread that
403
				// the job has started. This complicated dance is necessary
404
				// to transfer the schedulingRule to the new job because we
405
				// can only do that once we know the thread to transfer the
406
				// rule to.
407
				final Boolean[] latch = { Boolean.FALSE };
408
				saveJob.addJobChangeListener(new JobChangeAdapter() {
409
					public void done(final IJobChangeEvent event) {
410
						shellProvider.getShell().getDisplay().asyncExec(
411
								new Runnable() {
412
									public void run() {
413
										((InternalSaveable) model)
414
												.setSavingInBackground(false);
415
										model.enableUI(parts);
416
										IStatus result = event.getResult();
417
										if (!result.isOK()) {
418
											ErrorDialog
419
													.openError(
420
															shellProvider
421
																	.getShell(),
422
															WorkbenchMessages.Error,
423
															result.getMessage(),
424
															result);
425
										}
426
									}
427
								});
428
					}
429
430
					public void running(IJobChangeEvent event) {
431
						synchronized (latch) {
432
							latch[0] = Boolean.TRUE;
433
							latch.notifyAll();
434
						}
435
					}
436
				});
437
				// Finally, we are ready to schedule the job.
438
				// The job will block on the "beginRule" call since
439
				// we haven't transferred the rule to it yet. We can
440
				// only do that 
441
				saveJob.schedule();
442
				// wait until the job has a thread...
443
				synchronized (latch) {
444
					while (!latch[0].booleanValue()) {
445
						try {
446
							latch.wait();
447
						} catch (InterruptedException e) {
448
							Thread.currentThread().interrupt();
449
						}
450
					}
451
				}
452
				Job.getJobManager().transferRule(schedulingRule,
453
						saveJob.getThread());
454
				ruleTransferred = true;
455
				return;
456
			} catch (CoreException e) {
457
				ErrorDialog.openError(shellProvider.getShell(),
458
						WorkbenchMessages.Error, e.getMessage(), e.getStatus());
459
				progressMonitor.setCanceled(true);
460
			}
461
		} finally {
462
			if (!ruleTransferred) {
463
				Job.getJobManager().endRule(schedulingRule);
464
			}
465
			progressMonitor.done();
466
		}
467
	}
468
469
	public static void waitForBackgroundSaveJobs(List modelsToSave) {
470
		// block if any of the saveables is still saving in the background
471
		for (Iterator it = modelsToSave.iterator(); it.hasNext();) {
472
			Saveable model = (Saveable) it.next();
473
			// Temporary safety check for 3.3M4: Only block if the saveable
474
			// supports background save.
475
			if (model.supportsBackgroundSave()) {
476
				ISchedulingRule schedulingRule = ((InternalSaveable) model)
477
						.getSchedulingRule();
478
				try {
479
					Job.getJobManager().beginRule(schedulingRule, null);
480
					// if the saveable is no longer dirty, remove it from the list
481
					if (!model.isDirty()) {
482
						it.remove();
483
					}
484
				} finally {
485
					Job.getJobManager().endRule(schedulingRule);
486
				}
487
			}
488
		}
489
	}
490
298
}
491
}
(-)Eclipse UI/org/eclipse/ui/internal/EditorManager.java (-8 / +4 lines)
Lines 35-41 Link Here
35
import org.eclipse.core.runtime.SubProgressMonitor;
35
import org.eclipse.core.runtime.SubProgressMonitor;
36
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
36
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
37
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
37
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
38
import org.eclipse.jface.dialogs.ErrorDialog;
39
import org.eclipse.jface.dialogs.IDialogConstants;
38
import org.eclipse.jface.dialogs.IDialogConstants;
40
import org.eclipse.jface.dialogs.MessageDialog;
39
import org.eclipse.jface.dialogs.MessageDialog;
41
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
40
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
Lines 1105-1111 Link Here
1105
	 * @return <code>true</code> on success, <code>false</code> if the user
1104
	 * @return <code>true</code> on success, <code>false</code> if the user
1106
	 *         canceled the save
1105
	 *         canceled the save
1107
	 */
1106
	 */
1108
	public static boolean saveAll(List dirtyParts, boolean confirm, boolean closing,
1107
	public static boolean saveAll(List dirtyParts, boolean confirm, final boolean closing,
1109
				boolean addNonPartSources, final IRunnableContext runnableContext, final IShellProvider shellProvider) {
1108
				boolean addNonPartSources, final IRunnableContext runnableContext, final IShellProvider shellProvider) {
1110
		// clone the input list
1109
		// clone the input list
1111
		dirtyParts = new ArrayList(dirtyParts);
1110
		dirtyParts = new ArrayList(dirtyParts);
Lines 1205-1210 Link Here
1205
            if (modelsToSave.isEmpty()) {
1204
            if (modelsToSave.isEmpty()) {
1206
				return true;
1205
				return true;
1207
			}
1206
			}
1207
			SaveableHelper.waitForBackgroundSaveJobs(modelsToSave);
1208
            // Use a simpler dialog if there's only one
1208
            // Use a simpler dialog if there's only one
1209
            if (modelsToSave.size() == 1) {
1209
            if (modelsToSave.size() == 1) {
1210
            	Saveable model = (Saveable) modelsToSave.get(0);
1210
            	Saveable model = (Saveable) modelsToSave.get(0);
Lines 1276-1286 Link Here
1276
						monitor.worked(1);
1276
						monitor.worked(1);
1277
						continue;
1277
						continue;
1278
					}
1278
					}
1279
					try {
1279
					SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), shellProvider, closing);
1280
						model.doSave(new SubProgressMonitor(monitorWrap, 1));
1281
					} catch (CoreException e) {
1282
						ErrorDialog.openError(shellProvider.getShell(), WorkbenchMessages.Error, e.getMessage(), e.getStatus());
1283
					}
1284
					if (monitorWrap.isCanceled()) {
1280
					if (monitorWrap.isCanceled()) {
1285
						break;
1281
						break;
1286
					}
1282
					}
Lines 1294-1300 Link Here
1294
				WorkbenchMessages.Save_All, progressOp, runnableContext, shellProvider);
1290
				WorkbenchMessages.Save_All, progressOp, runnableContext, shellProvider);
1295
	}
1291
	}
1296
1292
1297
    /**
1293
	/**
1298
	 * For each part (view or editor) in the given list, attempts to convert it
1294
	 * For each part (view or editor) in the given list, attempts to convert it
1299
	 * to one or more saveable models. Duplicate models are removed. If closing
1295
	 * to one or more saveable models. Duplicate models are removed. If closing
1300
	 * is true, then models that will remain open in parts other than the given
1296
	 * is true, then models that will remain open in parts other than the given
(-)Eclipse UI/org/eclipse/ui/internal/SaveablesList.java (-9 / +17 lines)
Lines 21-31 Link Here
21
import java.util.Set;
21
import java.util.Set;
22
22
23
import org.eclipse.core.runtime.Assert;
23
import org.eclipse.core.runtime.Assert;
24
import org.eclipse.core.runtime.CoreException;
25
import org.eclipse.core.runtime.IProgressMonitor;
24
import org.eclipse.core.runtime.IProgressMonitor;
26
import org.eclipse.core.runtime.ListenerList;
25
import org.eclipse.core.runtime.ListenerList;
27
import org.eclipse.core.runtime.SubProgressMonitor;
26
import org.eclipse.core.runtime.SubProgressMonitor;
28
import org.eclipse.jface.dialogs.ErrorDialog;
29
import org.eclipse.jface.dialogs.IDialogConstants;
27
import org.eclipse.jface.dialogs.IDialogConstants;
30
import org.eclipse.jface.dialogs.MessageDialog;
28
import org.eclipse.jface.dialogs.MessageDialog;
31
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
29
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
Lines 417-422 Link Here
417
			final IWorkbenchWindow window, final boolean canCancel, boolean stillOpenElsewhere) {
415
			final IWorkbenchWindow window, final boolean canCancel, boolean stillOpenElsewhere) {
418
		// Save parts, exit the method if cancel is pressed.
416
		// Save parts, exit the method if cancel is pressed.
419
		if (modelsToSave.size() > 0) {
417
		if (modelsToSave.size() > 0) {
418
			SaveableHelper.waitForBackgroundSaveJobs(modelsToSave);
420
419
421
			IPreferenceStore apiPreferenceStore = PrefUtil.getAPIPreferenceStore();
420
			IPreferenceStore apiPreferenceStore = PrefUtil.getAPIPreferenceStore();
422
			boolean dontPrompt = stillOpenElsewhere && !apiPreferenceStore.getBoolean(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN);
421
			boolean dontPrompt = stillOpenElsewhere && !apiPreferenceStore.getBoolean(IWorkbenchPreferenceConstants.PROMPT_WHEN_SAVEABLE_STILL_OPEN);
Lines 555-567 Link Here
555
						monitor.worked(1);
554
						monitor.worked(1);
556
						continue;
555
						continue;
557
					}
556
					}
558
					try {
557
					SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), (WorkbenchWindow)window, true);
559
						model.doSave(new SubProgressMonitor(monitorWrap, 1));
560
					} catch (CoreException e) {
561
						ErrorDialog.openError(window.getShell(),
562
								WorkbenchMessages.Error, e.getMessage(), e
563
										.getStatus());
564
					}
565
					if (monitorWrap.isCanceled())
558
					if (monitorWrap.isCanceled())
566
						break;
559
						break;
567
				}
560
				}
Lines 743-746 Link Here
743
				.toArray(new ISaveablesSource[nonPartSources.size()]);
736
				.toArray(new ISaveablesSource[nonPartSources.size()]);
744
	}
737
	}
745
738
739
	/**
740
	 * @param model
741
	 */
742
	public IWorkbenchPart[] getPartsForSaveable(Saveable model) {
743
		List result = new ArrayList();
744
		for (Iterator it = modelMap.entrySet().iterator(); it.hasNext();) {
745
			Map.Entry entry = (Map.Entry) it.next();
746
			Set values = (Set) entry.getValue();
747
			if (values.contains(model) && entry.getKey() instanceof IWorkbenchPart) {
748
				result.add(entry.getKey());
749
			}
750
		}
751
		return (IWorkbenchPart[]) result.toArray(new IWorkbenchPart[result.size()]);
752
	}
753
746
}
754
}
(-)Eclipse UI/org/eclipse/ui/Saveable.java (-8 / +168 lines)
Lines 13-19 Link Here
13
13
14
import org.eclipse.core.runtime.CoreException;
14
import org.eclipse.core.runtime.CoreException;
15
import org.eclipse.core.runtime.IProgressMonitor;
15
import org.eclipse.core.runtime.IProgressMonitor;
16
import org.eclipse.core.runtime.IStatus;
16
import org.eclipse.jface.resource.ImageDescriptor;
17
import org.eclipse.jface.resource.ImageDescriptor;
18
import org.eclipse.jface.window.IShellProvider;
19
import org.eclipse.swt.SWT;
20
import org.eclipse.swt.graphics.Cursor;
21
import org.eclipse.swt.widgets.Composite;
22
import org.eclipse.swt.widgets.Control;
23
import org.eclipse.ui.internal.InternalSaveable;
24
import org.eclipse.ui.internal.PartSite;
25
import org.eclipse.ui.progress.IJobRunnable;
17
26
18
/**
27
/**
19
 * A <code>Saveable</code> represents a unit of saveability, e.g. an editable
28
 * A <code>Saveable</code> represents a unit of saveability, e.g. an editable
Lines 32-48 Link Here
32
 * @see ISaveablesSource
41
 * @see ISaveablesSource
33
 * @since 3.2
42
 * @since 3.2
34
 */
43
 */
35
public abstract class Saveable {
44
public abstract class Saveable extends InternalSaveable {
36
	
45
46
	private Cursor waitCursor;
47
	private Cursor originalCursor;
48
37
	/**
49
	/**
38
	 * Attempts to show this saveable in the given page and returns <code>true</code>
50
	 * Attempts to show this saveable in the given page and returns
39
	 * on success. The default implementation does nothing and returns <code>false</code>.
51
	 * <code>true</code> on success. The default implementation does nothing
52
	 * and returns <code>false</code>.
40
	 * 
53
	 * 
41
	 * @param page the workbench page in which to show this saveable  
54
	 * @param page
55
	 *            the workbench page in which to show this saveable
42
	 * @return <code>true</code> if this saveable is now visible to the user
56
	 * @return <code>true</code> if this saveable is now visible to the user
43
	 * @since 3.3
57
	 * @since 3.3
44
	 */
58
	 */
45
	public boolean show(IWorkbenchPage page) {
59
	public boolean show(IWorkbenchPage page) {
60
		if (page == null) {
61
			// I wish it was easier to avoid warnings about unused parameters
62
		}
46
		return false;
63
		return false;
47
	}
64
	}
48
65
Lines 133-145 Link Here
133
	 * </p>
150
	 * </p>
134
	 * 
151
	 * 
135
	 * <pre>
152
	 * <pre>
136
	 *   int PRIME = 31;
153
	 *     int PRIME = 31;
137
	 *   int hash = ...; // compute the &quot;normal&quot; hash code, e.g. based on some identifier unique within the defining plug-in
154
	 *     int hash = ...; // compute the &quot;normal&quot; hash code, e.g. based on some identifier unique within the defining plug-in
138
	 *   return hash * PRIME + MY_PLUGIN_ID.hashCode();
155
	 *     return hash * PRIME + MY_PLUGIN_ID.hashCode();
139
	 * </pre>
156
	 * </pre>
140
	 * 
157
	 * 
141
	 * @return a hash code
158
	 * @return a hash code
142
	 */
159
	 */
143
	public abstract int hashCode();
160
	public abstract int hashCode();
144
161
162
	/**
163
	 * Saves this saveable, or prepares this saveable for a background save
164
	 * operation. Returns null if this saveable has been successfully saved, or
165
	 * a job runnable that needs to be run to complete the save in the
166
	 * background. This method is called in the UI thread. If this saveable
167
	 * supports saving in the background, it should do only minimal work.
168
	 * However, since the job runnable returned by this method (if any) will not
169
	 * run on the UI thread, this method should copy any state that can only be
170
	 * accessed from the UI thread so that the job runnable will be able to
171
	 * access it.
172
	 * <p>
173
	 * The supplied shell provider can be used from within this method and from
174
	 * within the job runnable for the purpose of parenting dialogs. Care should
175
	 * be taken not to open dialogs gratuitously and only if user input is
176
	 * required for cases where the save cannot otherwise proceed - note that in
177
	 * any given save operation, many saveable objects may be saved at the same
178
	 * time. In particular, errors should be signaled by throwing an exception,
179
	 * or if an error occurs while running the job runnable, an error status
180
	 * should be returned.
181
	 * </p>
182
	 * <p>
183
	 * If the foreground part of the save is cancelled through user action, or
184
	 * for any other reason, the part should invoke <code>setCancelled</code>
185
	 * on the <code>IProgressMonitor</code> to inform the caller. If the
186
	 * background part of the save is cancelled, the job should return a
187
	 * {@link IStatus#CANCEL} status.
188
	 * </p>
189
	 * <p>
190
	 * This method is long-running; progress and cancellation are provided by
191
	 * the given progress monitor.
192
	 * </p>
193
	 * <p>
194
	 * The default implementation of this method calls
195
	 * {@link #doSave(IProgressMonitor)} and returns <code>null</code>.
196
	 * </p>
197
	 * 
198
	 * @param monitor
199
	 *            a progress monitor used for reporting progress and
200
	 *            cancellation
201
	 * @param shellProvider
202
	 *            an object that can provide a shell for parenting dialogs
203
	 * @return <code>null</code> if this saveable has been saved successfully,
204
	 *         or a job runnable that needs to be run to complete the save in
205
	 *         the background.
206
	 * 
207
	 * @since 3.3
208
	 */
209
	public IJobRunnable doSave(IProgressMonitor monitor,
210
			IShellProvider shellProvider) throws CoreException {
211
		doSave(monitor);
212
		return null;
213
	}
214
215
	/**
216
	 * Disables the UI of the given parts containing this saveable if necessary.
217
	 * This method is not intended to be called by clients. A corresponding call
218
	 * to
219
	 * <p>
220
	 * Saveables that can be saved in the background should ensure that the user
221
	 * cannot make changes to their data from the UI, for example by disabling
222
	 * controls, unless they are prepared to handle this case. This method is
223
	 * called on the UI thread after a job runnable has been returned from
224
	 * {@link #doSave(IProgressMonitor, IShellProvider, boolean)} and before
225
	 * spinning the event loop. The <code>closing</code> flag indicates that
226
	 * this saveable is currently being saved in response to closing a workbench
227
	 * part, in which case further changes to this saveable through the UI must
228
	 * be prevented.
229
	 * </p>
230
	 * <p>
231
	 * The default implementation calls setEnabled(false) on the given parts'
232
	 * composites.
233
	 * </p>
234
	 * 
235
	 * @param parts
236
	 *            the workbench parts containing this saveable
237
	 * @param closing
238
	 *            a boolean flag indicating whether the save was triggered by a
239
	 *            request to close a workbench part, and all of the given parts
240
	 *            will be closed after the save operation finishes successfully.
241
	 * 
242
	 * @since 3.3
243
	 */
244
	public void disableUI(IWorkbenchPart[] parts, boolean closing) {
245
		for (int i = 0; i < parts.length; i++) {
246
			IWorkbenchPart workbenchPart = parts[i];
247
			Composite paneComposite = (Composite) ((PartSite) workbenchPart
248
					.getSite()).getPane().getControl();
249
			Control[] paneChildren = paneComposite.getChildren();
250
			Composite toDisable = ((Composite) paneChildren[0]);
251
			toDisable.setEnabled(false);
252
			if (waitCursor == null) {
253
				waitCursor = new Cursor(workbenchPart.getSite().getWorkbenchWindow().getShell().getDisplay(), SWT.CURSOR_WAIT);
254
			}
255
			originalCursor = paneComposite.getCursor();
256
			paneComposite.setCursor(waitCursor);
257
		}
258
	}
259
260
	/**
261
	 * Enables the UI of the given parts containing this saveable after a
262
	 * background save operation has finished. This method is not intended to be
263
	 * called by clients.
264
	 * <p>
265
	 * The default implementation calls setEnabled(true) on the given parts'
266
	 * composites.
267
	 * </p>
268
	 * 
269
	 * @param parts
270
	 *            the workbench parts containing this saveable
271
	 * 
272
	 * @since 3.3
273
	 */
274
	public void enableUI(IWorkbenchPart[] parts) {
275
		for (int i = 0; i < parts.length; i++) {
276
			IWorkbenchPart workbenchPart = parts[i];
277
			Composite paneComposite = (Composite) ((PartSite) workbenchPart
278
					.getSite()).getPane().getControl();
279
			Control[] paneChildren = paneComposite.getChildren();
280
			Composite toEnable = ((Composite) paneChildren[0]);
281
			paneComposite.setCursor(originalCursor);
282
			if (waitCursor!=null && !waitCursor.isDisposed()) {
283
				waitCursor.dispose();
284
				waitCursor = null;
285
			}
286
			toEnable.setEnabled(true);
287
		}
288
	}
289
290
	/**
291
	 * EXPERIMENTAL / TEMPORARY: This method will very likely be removed before
292
	 * 3.3 M5. As of 3.3 M4, clients must override this method and return true
293
	 * if their implementation of
294
	 * {@link #doSave(IProgressMonitor, IShellProvider)} may return a non-null
295
	 * result.  The default implementation returns false.
296
	 * 
297
	 * @return <code>true</code> if this saveable may return a non-null value
298
	 *         from {@link #doSave(IProgressMonitor, IShellProvider)}.
299
	 * 
300
	 * @since 3.3
301
	 */
302
	public boolean supportsBackgroundSave() {
303
		return false;
304
	}
145
}
305
}
(-)Eclipse (+42 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.progress;
13
14
import org.eclipse.core.runtime.IProgressMonitor;
15
import org.eclipse.core.runtime.IStatus;
16
17
/**
18
 * Interface for runnables that can be run as jobs.
19
 * 
20
 * @since 3.3
21
 */
22
public interface IJobRunnable {
23
24
	/**
25
	 * Executes this runnable. Returns the result of the execution.
26
	 * <p>
27
	 * The provided monitor can be used to report progress and respond to
28
	 * cancellation. If the progress monitor has been canceled, the runnable should
29
	 * finish its execution at the earliest convenience and return a result
30
	 * status of severity <code>IStatus.CANCEL</code>. The singleton cancel
31
	 * status <code>Status.CANCEL_STATUS</code> can be used for this purpose.
32
	 * <p>
33
	 * 
34
	 * @param monitor
35
	 *            the monitor to be used for reporting progress and responding
36
	 *            to cancelation. The monitor is never <code>null</code>
37
	 * @return resulting status of the run. The result must not be
38
	 *         <code>null</code>
39
	 */
40
	public IStatus run(IProgressMonitor monitor);
41
42
}
(-)Eclipse (+22 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
/**
15
 * @since 3.3
16
 *
17
 */
18
interface IBackgroundSaveListener {
19
20
	public void handleBackgroundSaveStarted();
21
	
22
}
(-)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.internal;
13
14
import org.eclipse.core.runtime.jobs.ISchedulingRule;
15
16
/**
17
 * @since 3.3
18
 * 
19
 */
20
public class InternalSaveable {
21
22
	static class SerialPerObjectRule implements ISchedulingRule {
23
24
		private InternalSaveable lockObject = null;
25
26
		public SerialPerObjectRule(InternalSaveable lock) {
27
			lockObject = lock;
28
		}
29
30
		/*
31
		 * (non-Javadoc)
32
		 * 
33
		 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
34
		 */
35
		public boolean contains(ISchedulingRule rule) {
36
			return rule == this;
37
		}
38
39
		/*
40
		 * (non-Javadoc)
41
		 * 
42
		 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
43
		 */
44
		public boolean isConflicting(ISchedulingRule rule) {
45
			if (rule instanceof SerialPerObjectRule) {
46
				SerialPerObjectRule otherRule = (SerialPerObjectRule) rule;
47
				return lockObject.equals(otherRule.lockObject);
48
			}
49
			return false;
50
		}
51
52
	}
53
54
	private boolean savingInBackground;
55
56
	/**
57
	 * @return Returns the lock.
58
	 */
59
	/* package */ISchedulingRule getSchedulingRule() {
60
		return new SerialPerObjectRule(this);
61
	}
62
63
	/**
64
	 * @return
65
	 */
66
	/* package */ boolean isSavingInBackground() {
67
		return savingInBackground;
68
	}
69
70
	/**
71
	 * @param savingInBackground The savingInBackground to set.
72
	 */
73
	/* package */ void setSavingInBackground(boolean savingInBackground) {
74
		this.savingInBackground = savingInBackground;
75
	}
76
77
}

Return to bug 154122