001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.model;
011:
012: import java.util.ArrayList;
013: import java.util.Collection;
014: import java.util.Iterator;
015:
016: import org.eclipse.core.resources.IContainer;
017: import org.eclipse.core.resources.IResource;
018: import org.eclipse.core.resources.IResourceChangeEvent;
019: import org.eclipse.core.resources.IResourceChangeListener;
020: import org.eclipse.core.resources.IResourceDelta;
021: import org.eclipse.core.resources.IWorkspace;
022: import org.eclipse.jface.viewers.AbstractTreeViewer;
023: import org.eclipse.jface.viewers.StructuredViewer;
024: import org.eclipse.jface.viewers.Viewer;
025: import org.eclipse.swt.widgets.Control;
026:
027: /**
028: * Tree content provider for resource objects that can be adapted to the
029: * interface {@link org.eclipse.ui.model.IWorkbenchAdapter IWorkbenchAdapter}.
030: * This provider will listen for resource changes within the workspace and
031: * update the viewer as necessary.
032: * <p>
033: * This class may be instantiated, or subclassed by clients.
034: * </p>
035: */
036: public class WorkbenchContentProvider extends
037: BaseWorkbenchContentProvider implements IResourceChangeListener {
038: private Viewer viewer;
039:
040: /**
041: * Creates the resource content provider.
042: */
043: public WorkbenchContentProvider() {
044: super ();
045: }
046:
047: /*
048: * (non-Javadoc) Method declared on IContentProvider.
049: */
050: public void dispose() {
051: if (viewer != null) {
052: IWorkspace workspace = null;
053: Object obj = viewer.getInput();
054: if (obj instanceof IWorkspace) {
055: workspace = (IWorkspace) obj;
056: } else if (obj instanceof IContainer) {
057: workspace = ((IContainer) obj).getWorkspace();
058: }
059: if (workspace != null) {
060: workspace.removeResourceChangeListener(this );
061: }
062: }
063:
064: super .dispose();
065: }
066:
067: /*
068: * (non-Javadoc) Method declared on IContentProvider.
069: */
070: public void inputChanged(Viewer viewer, Object oldInput,
071: Object newInput) {
072: super .inputChanged(viewer, oldInput, newInput);
073:
074: this .viewer = viewer;
075: IWorkspace oldWorkspace = null;
076: IWorkspace newWorkspace = null;
077:
078: if (oldInput instanceof IWorkspace) {
079: oldWorkspace = (IWorkspace) oldInput;
080: } else if (oldInput instanceof IContainer) {
081: oldWorkspace = ((IContainer) oldInput).getWorkspace();
082: }
083:
084: if (newInput instanceof IWorkspace) {
085: newWorkspace = (IWorkspace) newInput;
086: } else if (newInput instanceof IContainer) {
087: newWorkspace = ((IContainer) newInput).getWorkspace();
088: }
089:
090: if (oldWorkspace != newWorkspace) {
091: if (oldWorkspace != null) {
092: oldWorkspace.removeResourceChangeListener(this );
093: }
094: if (newWorkspace != null) {
095: newWorkspace.addResourceChangeListener(this ,
096: IResourceChangeEvent.POST_CHANGE);
097: }
098: }
099: }
100:
101: /*
102: * (non-Javadoc) Method declared on IResourceChangeListener.
103: */
104: public final void resourceChanged(final IResourceChangeEvent event) {
105:
106: processDelta(event.getDelta());
107:
108: }
109:
110: /**
111: * Process the resource delta.
112: *
113: * @param delta
114: */
115: protected void processDelta(IResourceDelta delta) {
116:
117: Control ctrl = viewer.getControl();
118: if (ctrl == null || ctrl.isDisposed()) {
119: return;
120: }
121:
122: final Collection runnables = new ArrayList();
123: processDelta(delta, runnables);
124:
125: if (runnables.isEmpty()) {
126: return;
127: }
128:
129: //Are we in the UIThread? If so spin it until we are done
130: if (ctrl.getDisplay().getThread() == Thread.currentThread()) {
131: runUpdates(runnables);
132: } else {
133: ctrl.getDisplay().asyncExec(new Runnable() {
134: /* (non-Javadoc)
135: * @see java.lang.Runnable#run()
136: */
137: public void run() {
138: //Abort if this happens after disposes
139: Control ctrl = viewer.getControl();
140: if (ctrl == null || ctrl.isDisposed()) {
141: return;
142: }
143:
144: runUpdates(runnables);
145: }
146: });
147: }
148:
149: }
150:
151: /**
152: * Run all of the runnables that are the widget updates
153: * @param runnables
154: */
155: private void runUpdates(Collection runnables) {
156: Iterator runnableIterator = runnables.iterator();
157: while (runnableIterator.hasNext()) {
158: ((Runnable) runnableIterator.next()).run();
159: }
160:
161: }
162:
163: /**
164: * Process a resource delta. Add any runnables
165: */
166: private void processDelta(IResourceDelta delta, Collection runnables) {
167: //he widget may have been destroyed
168: // by the time this is run. Check for this and do nothing if so.
169: Control ctrl = viewer.getControl();
170: if (ctrl == null || ctrl.isDisposed()) {
171: return;
172: }
173:
174: // Get the affected resource
175: final IResource resource = delta.getResource();
176:
177: // If any children have changed type, just do a full refresh of this
178: // parent,
179: // since a simple update on such children won't work,
180: // and trying to map the change to a remove and add is too dicey.
181: // The case is: folder A renamed to existing file B, answering yes to
182: // overwrite B.
183: IResourceDelta[] affectedChildren = delta
184: .getAffectedChildren(IResourceDelta.CHANGED);
185: for (int i = 0; i < affectedChildren.length; i++) {
186: if ((affectedChildren[i].getFlags() & IResourceDelta.TYPE) != 0) {
187: runnables.add(getRefreshRunnable(resource));
188: return;
189: }
190: }
191:
192: // Opening a project just affects icon, but we need to refresh when
193: // a project is closed because if child items have not yet been created
194: // in the tree we still need to update the item's children
195: int changeFlags = delta.getFlags();
196: if ((changeFlags & IResourceDelta.OPEN) != 0) {
197: if (resource.isAccessible()) {
198: runnables.add(getUpdateRunnable(resource));
199: } else {
200: runnables.add(getRefreshRunnable(resource));
201: return;
202: }
203: }
204: // Check the flags for changes the Navigator cares about.
205: // See ResourceLabelProvider for the aspects it cares about.
206: // Notice we don't care about F_CONTENT or F_MARKERS currently.
207: if ((changeFlags & (IResourceDelta.SYNC | IResourceDelta.TYPE | IResourceDelta.DESCRIPTION)) != 0) {
208: runnables.add(getUpdateRunnable(resource));
209: }
210: // Replacing a resource may affect its label and its children
211: if ((changeFlags & IResourceDelta.REPLACED) != 0) {
212: runnables.add(getRefreshRunnable(resource));
213: return;
214: }
215:
216: // Handle changed children .
217: for (int i = 0; i < affectedChildren.length; i++) {
218: processDelta(affectedChildren[i], runnables);
219: }
220:
221: // @issue several problems here:
222: // - should process removals before additions, to avoid multiple equal
223: // elements in viewer
224: // - Kim: processing removals before additions was the indirect cause of
225: // 44081 and its varients
226: // - Nick: no delta should have an add and a remove on the same element,
227: // so processing adds first is probably OK
228: // - using setRedraw will cause extra flashiness
229: // - setRedraw is used even for simple changes
230: // - to avoid seeing a rename in two stages, should turn redraw on/off
231: // around combined removal and addition
232: // - Kim: done, and only in the case of a rename (both remove and add
233: // changes in one delta).
234:
235: IResourceDelta[] addedChildren = delta
236: .getAffectedChildren(IResourceDelta.ADDED);
237: IResourceDelta[] removedChildren = delta
238: .getAffectedChildren(IResourceDelta.REMOVED);
239:
240: if (addedChildren.length == 0 && removedChildren.length == 0) {
241: return;
242: }
243:
244: final Object[] addedObjects;
245: final Object[] removedObjects;
246:
247: // Process additions before removals as to not cause selection
248: // preservation prior to new objects being added
249: // Handle added children. Issue one update for all insertions.
250: int numMovedFrom = 0;
251: int numMovedTo = 0;
252: if (addedChildren.length > 0) {
253: addedObjects = new Object[addedChildren.length];
254: for (int i = 0; i < addedChildren.length; i++) {
255: addedObjects[i] = addedChildren[i].getResource();
256: if ((addedChildren[i].getFlags() & IResourceDelta.MOVED_FROM) != 0) {
257: ++numMovedFrom;
258: }
259: }
260: } else {
261: addedObjects = new Object[0];
262: }
263:
264: // Handle removed children. Issue one update for all removals.
265: if (removedChildren.length > 0) {
266: removedObjects = new Object[removedChildren.length];
267: for (int i = 0; i < removedChildren.length; i++) {
268: removedObjects[i] = removedChildren[i].getResource();
269: if ((removedChildren[i].getFlags() & IResourceDelta.MOVED_TO) != 0) {
270: ++numMovedTo;
271: }
272: }
273: } else {
274: removedObjects = new Object[0];
275: }
276: // heuristic test for items moving within same folder (i.e. renames)
277: final boolean hasRename = numMovedFrom > 0 && numMovedTo > 0;
278:
279: Runnable addAndRemove = new Runnable() {
280: public void run() {
281: if (viewer instanceof AbstractTreeViewer) {
282: AbstractTreeViewer treeViewer = (AbstractTreeViewer) viewer;
283: // Disable redraw until the operation is finished so we don't
284: // get a flash of both the new and old item (in the case of
285: // rename)
286: // Only do this if we're both adding and removing files (the
287: // rename case)
288: if (hasRename) {
289: treeViewer.getControl().setRedraw(false);
290: }
291: try {
292: if (addedObjects.length > 0) {
293: treeViewer.add(resource, addedObjects);
294: }
295: if (removedObjects.length > 0) {
296: treeViewer.remove(removedObjects);
297: }
298: } finally {
299: if (hasRename) {
300: treeViewer.getControl().setRedraw(true);
301: }
302: }
303: } else {
304: ((StructuredViewer) viewer).refresh(resource);
305: }
306: }
307: };
308: runnables.add(addAndRemove);
309: }
310:
311: /**
312: * Return a runnable for refreshing a resource.
313: * @param resource
314: * @return Runnable
315: */
316: private Runnable getRefreshRunnable(final IResource resource) {
317: return new Runnable() {
318: public void run() {
319: ((StructuredViewer) viewer).refresh(resource);
320: }
321: };
322: }
323:
324: /**
325: * Return a runnable for refreshing a resource.
326: * @param resource
327: * @return Runnable
328: */
329: private Runnable getUpdateRunnable(final IResource resource) {
330: return new Runnable() {
331: public void run() {
332: ((StructuredViewer) viewer).update(resource, null);
333: }
334: };
335: }
336: }
|