KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > internal > ui > viewers > AsynchronousViewer


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 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 package org.eclipse.debug.internal.ui.viewers;
12
13 import java.util.HashMap JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.core.runtime.IAdaptable;
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.core.runtime.Platform;
23 import org.eclipse.core.runtime.Status;
24 import org.eclipse.debug.internal.ui.DebugUIPlugin;
25 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
26 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy;
27 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
28 import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicyFactory;
29 import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
30 import org.eclipse.debug.internal.ui.viewers.model.provisional.IStatusMonitor;
31 import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
32 import org.eclipse.jface.resource.ImageDescriptor;
33 import org.eclipse.jface.viewers.ISelection;
34 import org.eclipse.jface.viewers.IStructuredContentProvider;
35 import org.eclipse.jface.viewers.IStructuredSelection;
36 import org.eclipse.jface.viewers.SelectionChangedEvent;
37 import org.eclipse.jface.viewers.StructuredSelection;
38 import org.eclipse.jface.viewers.StructuredViewer;
39 import org.eclipse.jface.viewers.Viewer;
40 import org.eclipse.swt.SWT;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.graphics.Color;
43 import org.eclipse.swt.graphics.Font;
44 import org.eclipse.swt.graphics.FontData;
45 import org.eclipse.swt.graphics.Image;
46 import org.eclipse.swt.graphics.RGB;
47 import org.eclipse.swt.widgets.Control;
48 import org.eclipse.swt.widgets.Event;
49 import org.eclipse.swt.widgets.Item;
50 import org.eclipse.swt.widgets.Listener;
51 import org.eclipse.swt.widgets.Widget;
52 import org.eclipse.ui.progress.WorkbenchJob;
53
54 /**
55  * A viewer that retrieves labels and content asynchronously via adapters and supports
56  * duplicate elements in the viewer. Retrieving content and labels asynchronously allows
57  * for arbitrary latency without blocking the UI thread.
58  * <p>
59  * This viewer uses adapters to retrieve labels and content rather than
60  * a label provider and content provider. As such, the label provider for this viewer
61  * is <code>null</code> by default. The content provider returned by this viewer is
62  * non-<code>null</code> to conform to the viewer specification, but performs no
63  * useful function.
64  * </p>
65  * <p>
66  * The selection in this viewer is also set asynchronously. When the selection is set,
67  * the viewer attempts to perform the selection. If the elements in the specified selection
68  * are not yet in the viewer, the portion of the selection that could not be honored
69  * becomes a pending selection. As more elements are added to viewer, the pending selection
70  * is attempted to be set.
71  * </p>
72  * @since 3.2
73  */

74 public abstract class AsynchronousViewer extends StructuredViewer implements Listener {
75     
76     /**
77      * Model of elements for this viewer
78      */

79     private AsynchronousModel fModel;
80
81     /**
82      * Cache of images used for elements in this tree viewer. Label updates
83      * use the method <code>getImage(...)</code> to cache images for
84      * image descriptors. The images are disposed when this viewer is disposed.
85      */

86     private Map JavaDoc fImageCache = new HashMap JavaDoc();
87
88     /**
89      * Cache of the fonts used for elements in this tree viewer. Label updates
90      * use the method <code>getFont(...)</code> to cache fonts for
91      * FontData objects. The fonts are disposed with the viewer.
92      */

93     private Map JavaDoc fFontCache = new HashMap JavaDoc();
94
95     /**
96      * Cache of the colors used for elements in this tree viewer. Label updates
97      * use the method <code>getColor(...)</code> to cache colors for
98      * RGB values. The colors are disposed with the viewer.
99      */

100     private Map JavaDoc fColorCache = new HashMap JavaDoc();
101
102     /**
103      * The context in which this viewer is being used - i.e. what part it is contained
104      * in any any preference settings associated with it.
105      */

106     private IPresentationContext fContext;
107
108     private ISelection fPendingSelection;
109
110     private ISelection fCurrentSelection;
111     
112     /**
113      * Array used to store indices of the path to an item in the viewer being mapped
114      * by a 'set data' callback. Indices are bottom up. For example when 'set data' for
115      * the 3rd child of the 4th child of the 2nd root element were being asked for,
116      * the first 3 indices would look like: [3, 4, 2, ....]. We re-use an array to avoid
117      * creating a new one all the time. The array grows as needed to accommodate deep
118      * elements.
119      */

120     private int[] fSetDataIndicies = new int[5];
121     
122     /**
123      * The update policy for this viewer.
124      */

125     private AbstractUpdatePolicy fUpdatePolicy;
126
127     protected static final String JavaDoc OLD_LABEL = "old_label"; //$NON-NLS-1$
128
protected static final String JavaDoc OLD_IMAGE = "old_image"; //$NON-NLS-1$
129

130        // debug flags
131
public static boolean DEBUG_VIEWER = false;
132     
133     static {
134         DEBUG_VIEWER = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
135
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/viewer")); //$NON-NLS-1$
136
}
137     
138     /**
139      * Creates a new viewer
140      */

141     protected AsynchronousViewer() {
142         setContentProvider(new NullContentProvider());
143         setUseHashlookup(true);
144     }
145
146     /**
147      * Hash lookup is required, don't let subclasses change behavior.
148      */

149     public final void setUseHashlookup(boolean enable) {
150         Assert.isTrue(enable);
151         super.setUseHashlookup(enable);
152     }
153
154     /* (non-Javadoc)
155      * @see org.eclipse.jface.viewers.StructuredViewer#hookControl(org.eclipse.swt.widgets.Control)
156      */

157     protected void hookControl(Control control) {
158         super.hookControl(control);
159         control.addListener(SWT.SetData, this);
160     }
161
162     /**
163      * Clients must call this methods when this viewer is no longer needed
164      * so it can perform cleanup.
165      */

166     public synchronized void dispose() {
167         Iterator JavaDoc images = fImageCache.values().iterator();
168         while (images.hasNext()) {
169             Image image = (Image) images.next();
170             image.dispose();
171         }
172         fImageCache.clear();
173         
174         Iterator JavaDoc fonts = fFontCache.values().iterator();
175         while (fonts.hasNext()) {
176             Font font = (Font) fonts.next();
177             font.dispose();
178         }
179         fFontCache.clear();
180         
181         Iterator JavaDoc colors = fColorCache.values().iterator();
182         while (colors.hasNext()) {
183             Color color = (Color) colors.next();
184             color.dispose();
185         }
186         fColorCache.clear();
187         
188         if (fModel != null) {
189             fModel.dispose();
190         }
191         if (fUpdatePolicy != null) {
192             fUpdatePolicy.dispose();
193         }
194         if (fContext != null) {
195             ((PresentationContext)fContext).dispose();
196         }
197     }
198
199     /**
200      * Updates all occurrences of the given element in this viewer.
201      *
202      * @param element element to update
203      */

204     public void update(Object JavaDoc element) {
205         ModelNode[] nodes = getModel().getNodes(element);
206         if (nodes != null) {
207             for (int i = 0; i < nodes.length; i++) {
208                 updateLabel(nodes[i]);
209             }
210         }
211     }
212     
213     /**
214      * Updates the label for a specific element (node) in the model.
215      *
216      * @param node node to update
217      * @param item its associated item
218      */

219     protected void updateLabel(ModelNode node) {
220         // the input is not displayed
221
if (!node.getElement().equals(getInput())) {
222             getModel().updateLabel(node);
223         }
224     }
225         
226     /**
227      * Returns the presentation context to be used in update requests.
228      * Clients may override this method if required to provide special
229      * implementations of contexts.
230      *
231      * @return presentation context
232      */

233     public IPresentationContext getPresentationContext() {
234         return fContext;
235     }
236
237     /* (non-Javadoc)
238      * @see org.eclipse.jface.viewers.StructuredViewer#unmapAllElements()
239      */

240     protected synchronized void unmapAllElements() {
241         super.unmapAllElements();
242         AsynchronousModel model = getModel();
243         if (model != null) {
244             model.dispose();
245         }
246     }
247
248     /* (non-Javadoc)
249      * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
250      */

251     protected synchronized void inputChanged(Object JavaDoc input, Object JavaDoc oldInput) {
252         fPendingSelection = null;
253         if (fCurrentSelection != null) {
254             updateSelection(new StructuredSelection());
255             fCurrentSelection = null;
256         }
257         if (fUpdatePolicy == null) {
258             fUpdatePolicy = createUpdatePolicy();
259             fUpdatePolicy.init(this);
260         }
261         if (fModel != null) {
262             fModel.dispose();
263         }
264         fModel = createModel();
265         fModel.init(input);
266         if (input != null) {
267             mapElement(fModel.getRootNode(), getControl());
268             getControl().setData(fModel.getRootNode().getElement());
269         } else {
270             unmapAllElements();
271             getControl().setData(null);
272         }
273         refresh();
274     }
275     
276     /**
277      * Creates a new empty model for this viewer that
278      * is *not* initialized.
279      *
280      * @return a new model
281      */

282     protected abstract AsynchronousModel createModel();
283
284     /**
285      * Creates and returns this viewers update policy.
286      * @return update policy
287      */

288     public abstract AbstractUpdatePolicy createUpdatePolicy();
289
290     Image[] getImages(ImageDescriptor[] descriptors) {
291         if (descriptors == null || descriptors.length == 0) {
292             String JavaDoc[] columns = getPresentationContext().getColumns();
293             if (columns == null) {
294                 return new Image[1];
295             } else {
296                 return new Image[columns.length];
297             }
298         }
299         Image[] images = new Image[descriptors.length];
300         for (int i = 0; i < images.length; i++) {
301             images[i] = getImage(descriptors[i]);
302         }
303         return images;
304     }
305     
306     /**
307      * Returns an image for the given image descriptor or <code>null</code>. Adds the image
308      * to a cache of images if it does not already exist. The cache is cleared when this viewer
309      * is disposed.
310      *
311      * @param descriptor image descriptor or <code>null</code>
312      * @return image or <code>null</code>
313      */

314     protected Image getImage(ImageDescriptor descriptor) {
315         if (descriptor == null) {
316             return null;
317         }
318         Image image = (Image) fImageCache.get(descriptor);
319         if (image == null) {
320             image = new Image(getControl().getDisplay(), descriptor.getImageData());
321             fImageCache.put(descriptor, image);
322         }
323         return image;
324     }
325
326     protected Font[] getFonts(FontData[] fontDatas) {
327         if (fontDatas == null || fontDatas.length == 0) {
328             String JavaDoc[] columns = getPresentationContext().getColumns();
329             if (columns == null) {
330                 return new Font[1];
331             } else {
332                 return new Font[columns.length];
333             }
334         }
335         
336         Font[] fonts = new Font[fontDatas.length];
337         for (int i = 0; i < fonts.length; i++) {
338             fonts[i] = getFont(fontDatas[i]);
339         }
340         return fonts;
341     }
342     
343     /**
344      * Returns a font for the given font data or <code>null</code>. Adds the font to this viewer's font
345      * cache which is disposed when this viewer is disposed.
346      *
347      * @param fontData font data or <code>null</code>
348      * @return font font or <code>null</code>
349      */

350     protected Font getFont(FontData fontData) {
351         if (fontData == null) {
352             return null;
353         }
354         Font font = (Font) fFontCache.get(fontData);
355         if (font == null) {
356             font = new Font(getControl().getDisplay(), fontData);
357             fFontCache.put(fontData, font);
358         }
359         return font;
360     }
361     
362     protected Color[] getColors(RGB[] rgb) {
363         if (rgb == null || rgb.length == 0) {
364             String JavaDoc[] columns = getPresentationContext().getColumns();
365             if (columns == null) {
366                 return new Color[1];
367             } else {
368                 return new Color[columns.length];
369             }
370         }
371         Color[] colors = new Color[rgb.length];
372         for (int i = 0; i < colors.length; i++) {
373             colors[i] = getColor(rgb[i]);
374         }
375         return colors;
376     }
377     /**
378      * Returns a color for the given RGB or <code>null</code>. Adds the color to this viewer's color
379      * cache which is disposed when this viewer is disposed.
380      *
381      * @param rgb RGB or <code>null</code>
382      * @return color or <code>null</code>
383      */

384     protected Color getColor(RGB rgb) {
385         if (rgb == null) {
386             return null;
387         }
388         Color color = (Color) fColorCache.get(rgb);
389         if (color == null) {
390             color = new Color(getControl().getDisplay(), rgb);
391             fColorCache.put(rgb, color);
392         }
393         return color;
394     }
395     
396     /**
397      * Sets the context for this viewer.
398      *
399      * @param context
400      */

401     public void setContext(IPresentationContext context) {
402         fContext = context;
403     }
404
405     /* (non-Javadoc)
406      * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
407      */

408     protected Widget doFindItem(Object JavaDoc element) {
409         // this viewer maps model nodes to widgets, so the element is a ModelNode
410
AsynchronousModel model = getModel();
411         if (model != null) {
412             if (element.equals(model.getRootNode())) {
413                 return doFindInputItem(element);
414             }
415             Widget[] widgets = findItems(element);
416             if (widgets.length > 0) {
417                 return widgets[0];
418             }
419         }
420         return null;
421     }
422     
423     /*
424      * (non-Javadoc)
425      *
426      * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
427      */

428     protected Widget doFindInputItem(Object JavaDoc element) {
429         if (element instanceof ModelNode) {
430             ModelNode node = (ModelNode) element;
431             if (node.getElement().equals(getInput())) {
432                 return getControl();
433             }
434         }
435         return null;
436     }
437
438     /* (non-Javadoc)
439      * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget, java.lang.Object, boolean)
440      */

441     protected void doUpdateItem(Widget item, Object JavaDoc element, boolean fullMap) {
442     }
443
444     /* (non-Javadoc)
445      * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
446      */

447     protected void internalRefresh(Object JavaDoc element) {
448         // get the nodes in the model
449
AsynchronousModel model = getModel();
450         if (model != null) {
451             ModelNode[] nodes = model.getNodes(element);
452             if (nodes != null) {
453                 for (int i = 0; i < nodes.length; i++) {
454                     ModelNode node = nodes[i];
455                     // get the widget for the node
456
Widget item = findItem(node);
457                     if (item != null) {
458                         internalRefresh(node);
459                     }
460                 }
461             }
462         }
463     }
464     
465     /**
466      * Refreshes a specific occurrence of an element (a node).
467      *
468      * @param node node to update
469      *
470      * Subclasses should override and call super
471      */

472     protected void internalRefresh(ModelNode node) {
473         updateLabel(node);
474     }
475
476     /* (non-Javadoc)
477      * @see org.eclipse.jface.viewers.Viewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean)
478      */

479     public synchronized void setSelection(ISelection selection, boolean reveal) {
480         setSelection(selection, reveal, false);
481     }
482     
483     /**
484      * Sets the selection in this viewer.
485      *
486      * @param selection new selection
487      * @param reveal whether to reveal the selection
488      * @param force whether to force the selection change without consulting the model
489      * selection policy
490      */

491     public synchronized void setSelection(ISelection selection, final boolean reveal, boolean force) {
492         Control control = getControl();
493         if (control == null || control.isDisposed()) {
494             return;
495         }
496         if (!acceptsSelection(selection)) {
497             selection = getEmptySelection();
498         }
499         if (!force && !overrideSelection(fCurrentSelection, selection)) {
500             return;
501         }
502         
503         fPendingSelection = selection;
504         
505         if (getControl().getDisplay().getThread() == Thread.currentThread()) {
506             attemptSelection(reveal);
507         } else {
508             WorkbenchJob job = new WorkbenchJob("attemptSelection") { //$NON-NLS-1$
509
public IStatus runInUIThread(IProgressMonitor monitor) {
510                     attemptSelection(reveal);
511                     return Status.OK_STATUS;
512                 }
513                 
514             };
515             job.setSystem(true);
516             job.schedule();
517         }
518     }
519     
520     
521     /**
522      * Returns whether the candidate selection should override the current
523      * selection.
524      *
525      * @param current
526      * @param curr
527      * @return
528      */

529     protected boolean overrideSelection(ISelection current, ISelection candidate) {
530         IModelSelectionPolicy selectionPolicy = getSelectionPolicy(current);
531         if (selectionPolicy == null) {
532             return true;
533         }
534         if (selectionPolicy.contains(candidate, getPresentationContext())) {
535             return selectionPolicy.overrides(current, candidate, getPresentationContext());
536         }
537         return !selectionPolicy.isSticky(current, getPresentationContext());
538     }
539     
540     /* (non-Javadoc)
541      * @see org.eclipse.jface.viewers.StructuredViewer#getSelection()
542      */

543     public ISelection getSelection() {
544         Control control = getControl();
545         if (control == null || control.isDisposed() || fCurrentSelection == null) {
546             return StructuredSelection.EMPTY;
547         }
548         return fCurrentSelection;
549     }
550     
551     /* (non-Javadoc)
552      * @see org.eclipse.jface.viewers.StructuredViewer#handleSelect(org.eclipse.swt.events.SelectionEvent)
553      */

554     protected void handleSelect(SelectionEvent event) {
555         // handle case where an earlier selection listener disposed the control.
556
Control control = getControl();
557         if (control != null && !control.isDisposed()) {
558             updateSelection(newSelectionFromWidget());
559         }
560     }
561     
562     /* (non-Javadoc)
563      * @see org.eclipse.jface.viewers.StructuredViewer#handlePostSelect(org.eclipse.swt.events.SelectionEvent)
564      */

565     protected void handlePostSelect(SelectionEvent e) {
566         SelectionChangedEvent event = new SelectionChangedEvent(this, newSelectionFromWidget());
567         firePostSelectionChanged(event);
568     }
569     
570     /**
571      * Creates and returns a new selection from this viewer, based on the selected
572      * elements in the widget.
573      *
574      * @return a new selection
575      */

576     protected abstract ISelection newSelectionFromWidget();
577     
578     /**
579      * Returns the selection policy associated with the given selection
580      * or <code>null</code> if none.
581      *
582      * @param selection or <code>null</code>
583      * @return selection policy or <code>null</code>
584      */

585     protected IModelSelectionPolicy getSelectionPolicy(ISelection selection) {
586         if (selection instanceof IStructuredSelection) {
587             IStructuredSelection ss = (IStructuredSelection) selection;
588             Object JavaDoc element = ss.getFirstElement();
589             if (element instanceof IAdaptable) {
590                 IAdaptable adaptable = (IAdaptable) element;
591                 IModelSelectionPolicyFactory factory = (IModelSelectionPolicyFactory) adaptable.getAdapter(IModelSelectionPolicyFactory.class);
592                 if (factory != null) {
593                     return factory.createModelSelectionPolicyAdapter(adaptable, getPresentationContext());
594                 }
595             }
596         }
597         return null;
598     }
599
600     /* (non-Javadoc)
601      * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(org.eclipse.jface.viewers.ISelection, boolean)
602      */

603     final protected void setSelectionToWidget(ISelection selection, final boolean reveal) {
604         // NOT USED
605
throw new IllegalArgumentException JavaDoc("This method should not be called"); //$NON-NLS-1$
606
}
607     
608     /* (non-Javadoc)
609      * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, boolean)
610      */

611     final protected void setSelectionToWidget(List JavaDoc l, boolean reveal) {
612         // NOT USED
613
throw new IllegalArgumentException JavaDoc("This method should not be called"); //$NON-NLS-1$
614
}
615         
616     /**
617      * Attempts to update any pending selection.
618      *
619      * @param reveal whether to reveal the selection
620      */

621     protected void attemptSelection(boolean reveal) {
622         ISelection currentSelection = null;
623         synchronized (this) {
624             if (fPendingSelection != null) {
625                 ISelection remaining = doAttemptSelectionToWidget(fPendingSelection, reveal);
626                 if (remaining.isEmpty()) {
627                     remaining = null;
628                 }
629                 if (!fPendingSelection.equals(remaining)) {
630                     fPendingSelection = remaining;
631                     currentSelection = newSelectionFromWidget();
632                     if (isSuppressEqualSelections() && currentSelection.equals(fCurrentSelection)) {
633                         return;
634                     }
635                 }
636             }
637         }
638         if (currentSelection != null) {
639             updateSelection(currentSelection);
640             firePostSelectionChanged(new SelectionChangedEvent(this, currentSelection));
641         }
642     }
643     
644     /**
645      * Controls whether selection change notification is sent even when
646      * successive selections are equal.
647      *
648      * TODO: what we really want is to fire selection change on ACTIVATE model
649      * change, even when selection is the same.
650      *
651      * @return whether to suppress change notification for equal successive
652      * selections
653      */

654     protected boolean isSuppressEqualSelections() {
655         return true;
656     }
657     
658     /**
659      * Attempts to selection the specified selection and returns a selection
660      * representing the portion of the selection that could not be honored
661      * and still needs to be selected.
662      *
663      * @param selection selection to attempt
664      * @param reveal whether to reveal the selection
665      * @return remaining selection
666      */

667     protected abstract ISelection doAttemptSelectionToWidget(ISelection selection, boolean reveal);
668     
669     /**
670      * Returns whether this viewer supports the given selection.
671      *
672      * @param selection a selection
673      * @return whether this viewer supports the given selection
674      */

675     protected abstract boolean acceptsSelection(ISelection selection);
676     
677     /**
678      * Returns an empty selection supported by this viewer.
679      *
680      * @return an empty selection supported by this viewer
681      */

682     protected abstract ISelection getEmptySelection();
683     
684     /**
685      * A content provider that does nothing.
686      */

687     private class NullContentProvider implements IStructuredContentProvider {
688         public void dispose() {
689         }
690
691         public void inputChanged(Viewer viewer, Object JavaDoc oldInput, Object JavaDoc newInput) {
692         }
693
694         public Object JavaDoc[] getElements(Object JavaDoc inputElement) {
695             return null;
696         }
697     }
698
699     /**
700      * Notification that a presentation update has failed.
701      * Subclasses may override as required. The default implementation
702      * does nothing.
703      *
704      * @param monitor monitor for the presentation request that failed
705      * @param status status of update
706      */

707     protected void handlePresentationFailure(IStatusMonitor monitor, IStatus status) {
708     }
709
710     /* (non-Javadoc)
711      * @see org.eclipse.jface.viewers.StructuredViewer#preservingSelection(java.lang.Runnable)
712      */

713     protected synchronized void preservingSelection(Runnable JavaDoc updateCode) {
714         if (fPendingSelection == null || fPendingSelection.isEmpty()) {
715             ISelection oldSelection = null;
716             try {
717                 // preserve selection
718
oldSelection = fCurrentSelection;
719                 // perform the update
720
updateCode.run();
721             } finally {
722                 // restore selection
723
if (oldSelection == null) {
724                     oldSelection = new StructuredSelection();
725                 }
726                 if (getControl().getDisplay().getThread() == Thread.currentThread()) {
727                     if (!oldSelection.equals(newSelectionFromWidget())) {
728                         restoreSelection(oldSelection);
729                     }
730                 } else {
731                     WorkbenchJob job = new WorkbenchJob("attemptSelection") { //$NON-NLS-1$
732
public IStatus runInUIThread(IProgressMonitor monitor) {
733                             synchronized (AsynchronousViewer.this) {
734                                 if (!getControl().isDisposed()) {
735                                     if (fPendingSelection == null || fPendingSelection.isEmpty()) {
736                                         ISelection tempSelection = fCurrentSelection;
737                                         if (tempSelection == null) {
738                                             tempSelection = new StructuredSelection();
739                                         }
740                                         if (!tempSelection.equals(newSelectionFromWidget())) {
741                                             restoreSelection(tempSelection);
742                                         }
743                                     }
744                                 }
745                             }
746                             return Status.OK_STATUS;
747                         }
748                         
749                     };
750                     job.setSystem(true);
751                     job.schedule();
752                 }
753             }
754         } else {
755             updateCode.run();
756         }
757     }
758     
759     protected synchronized void restoreSelection(ISelection oldSelection) {
760         ISelection remaining = doAttemptSelectionToWidget(oldSelection, false);
761         // send out notification if old and new differ
762
fCurrentSelection = newSelectionFromWidget();
763         if (!selectionExists(fCurrentSelection)) {
764             if (selectionExists(oldSelection)) {
765                 // old selection exists in the model, but not widget
766
fCurrentSelection = oldSelection;
767             } else {
768                 fCurrentSelection = getEmptySelection();
769             }
770         }
771         if (!fCurrentSelection.equals(oldSelection)) {
772             handleInvalidSelection(oldSelection, fCurrentSelection);
773             // if the remaining selection still exists in the model, make it pending
774
if (selectionExists(remaining)) {
775                 setSelection(remaining);
776             }
777         }
778     }
779     
780     /**
781      * Returns whether the selection exists in the model
782      */

783     protected boolean selectionExists(ISelection selection) {
784         if (selection.isEmpty()) {
785             return false;
786         }
787         if (selection instanceof IStructuredSelection) {
788             IStructuredSelection ss = (IStructuredSelection) selection;
789             Iterator JavaDoc iterator = ss.iterator();
790             while (iterator.hasNext()) {
791                 Object JavaDoc element = iterator.next();
792                 if (getModel().getNodes(element) == null) {
793                     return false;
794                 }
795             }
796         }
797         return true;
798     }
799     
800     /**
801      * Sets the color attributes of the given widget.
802      *
803      * @param widget the widget to update
804      * @param foreground foreground color of the widget or <code>null</code> if default
805      * @param background background color of the widget or <code>null</code> if default
806      */

807     protected abstract void setColors(Widget widget, RGB foreground[], RGB background[]);
808     
809     /**
810      * Sets the label attributes of the given widget.
811      *
812      * @param widget the widget to update
813      * @param text label text
814      * @param image label image or <code>null</code>
815      */

816     protected abstract void setLabels(Widget widget, String JavaDoc[] text, ImageDescriptor[] image);
817     
818     /**
819      * Sets the font attributes of the given widget.
820      *
821      * @param widget widget to update
822      * @param font font of the widget or <code>null</code> if default.
823      */

824     protected abstract void setFonts(Widget widget, FontData[] font);
825
826     /* (non-Javadoc)
827      * @see org.eclipse.jface.viewers.StructuredViewer#updateSelection(org.eclipse.jface.viewers.ISelection)
828      */

829     protected synchronized void updateSelection(ISelection selection) {
830         fCurrentSelection = selection;
831         super.updateSelection(selection);
832     }
833     
834
835     
836     /**
837      * Notification the given model proxy has been added to this viewer's model.
838      *
839      * @param proxy
840      */

841     protected void modelProxyAdded(IModelProxy proxy) {
842         if (fUpdatePolicy instanceof IModelChangedListener) {
843             proxy.addModelChangedListener((IModelChangedListener)fUpdatePolicy);
844         }
845     }
846     
847     /**
848      * Notification the given model proxy has been removed from this viewer's model.
849      *
850      * @param proxy
851      */

852     protected void modelProxyRemoved(IModelProxy proxy) {
853         if (fUpdatePolicy instanceof IModelChangedListener) {
854             proxy.removeModelChangedListener((IModelChangedListener)fUpdatePolicy);
855         }
856     }
857     
858     /**
859      * Returns this viewer's model
860      *
861      * @return model
862      */

863     protected AsynchronousModel getModel() {
864         return fModel;
865     }
866
867     /**
868      * A node in the model has been updated
869      *
870      * @param node
871      */

872     protected void nodeChanged(ModelNode node) {
873         Widget widget = findItem(node);
874         if (widget != null) {
875             clear(widget);
876             attemptPendingUpdates();
877         }
878     }
879
880     /**
881      * @return if there are any more pending updates in the viewer
882      */

883     public synchronized boolean hasPendingUpdates() {
884         return getModel().hasPendingUpdates();
885     }
886
887     /**
888      * Notification from the model that the update for the given request
889      * has completed.
890      *
891      * @param monitor
892      */

893     protected void updateComplete(IStatusMonitor monitor) {
894     }
895
896     /**
897      * Clears the given widget
898      *
899      * @param item
900      */

901     protected abstract void clear(Widget item);
902     
903     /**
904      * Clears the children of the widget.
905      *
906      * @param item
907      */

908     protected abstract void clearChildren(Widget item);
909     
910     /**
911      * Clears the child at the given index.
912      *
913      * @param parent
914      * @param childIndex
915      */

916     protected abstract void clearChild(Widget parent, int childIndex);
917
918     /**
919      * Returns the child widget at the given index for the given parent or
920      * <code>null</code>
921      *
922      * @param parent
923      * @param index
924      * @return
925      */

926     protected abstract Widget getChildWidget(Widget parent, int index);
927
928     /**
929      * Sets the item count for a parent widget
930      *
931      * @param parent
932      * @param itemCount
933      */

934     protected abstract void setItemCount(Widget parent, int itemCount);
935
936     /**
937      * Attempt pending updates. Subclasses may override but should call super.
938      */

939     protected void attemptPendingUpdates() {
940         attemptSelection(false);
941     }
942     
943     /**
944      * Notification a node's children have changed.
945      * Updates the child count for the parent's widget
946      * and clears children to be updated.
947      *
948      * @param parentNode
949      */

950     protected void nodeChildrenChanged(ModelNode parentNode) {
951         Widget widget = findItem(parentNode);
952         if (widget != null && !widget.isDisposed()) {
953             int childCount = parentNode.getChildCount();
954             setItemCount(widget, childCount);
955             clearChildren(widget);
956             attemptPendingUpdates();
957         }
958     }
959     
960     /**
961      * Notification children have been added to the end
962      * of the given parent.
963      *
964      * @param parentNode
965      */

966     protected void nodeChildrenAdded(ModelNode parentNode) {
967         Widget widget = findItem(parentNode);
968         if (widget != null && !widget.isDisposed()) {
969             int childCount = parentNode.getChildCount();
970             setItemCount(widget, childCount);
971             attemptPendingUpdates();
972         }
973     }
974     
975     /**
976      * Notification children have been added to the end
977      * of the given parent.
978      *
979      * @param parentNode
980      */

981     protected void nodeChildRemoved(ModelNode parentNode, int index) {
982         Widget widget = findItem(parentNode);
983         if (widget != null && !widget.isDisposed()) {
984             Widget childWidget = getChildWidget(widget, index);
985             int childCount = parentNode.getChildCount();
986             // if the child widget exists, dispose it so item state remains, otherwise update child count
987
if (childWidget == null) {
988                 setItemCount(widget, childCount);
989             } else {
990                 childWidget.dispose();
991             }
992             for (int i = index; i < childCount; i ++) {
993                 clearChild(widget, i);
994             }
995             attemptPendingUpdates();
996         }
997     }
998     
999     /**
1000     * Unmaps the node from its widget and all of its children nodes from
1001     * their widgets.
1002     *
1003     * @param node
1004     */

1005    protected void unmapNode(ModelNode node) {
1006        unmapElement(node);
1007        ModelNode[] childrenNodes = node.getChildrenNodes();
1008        if (childrenNodes != null) {
1009            for (int i = 0; i < childrenNodes.length; i++) {
1010                unmapNode(childrenNodes[i]);
1011            }
1012        }
1013    }
1014
1015    /**
1016     * Returns the node corresponding to the given widget or <code>null</code>
1017     * @param widget widget for which a node is requested
1018     * @return node or <code>null</code>
1019     */

1020    protected ModelNode findNode(Widget widget) {
1021        ModelNode[] nodes = getModel().getNodes(widget.getData());
1022        if (nodes != null) {
1023            for (int i = 0; i < nodes.length; i++) {
1024                ModelNode node = nodes[i];
1025                Widget item = findItem(node);
1026                if (widget == item) {
1027                    return node;
1028                }
1029            }
1030        }
1031        return null;
1032    }
1033    
1034    /**
1035     * Returns the item for the node or <code>null</code>
1036     * @param node
1037     * @return
1038     */

1039    protected Widget findItem(ModelNode node) {
1040        return findItem((Object JavaDoc)node);
1041    }
1042
1043    /*
1044     * (non-Javadoc)
1045     *
1046     * A virtual item has been exposed in the control, map its data.
1047     *
1048     * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
1049     */

1050    public void handleEvent(final Event event) {
1051        update((Item)event.item, event.index);
1052    }
1053    
1054    /**
1055     * Update the given item.
1056     *
1057     * @param item item to update
1058     * @param index index of item in parent's children
1059     */

1060    protected void update(Item item, int index) {
1061        restoreLabels(item);
1062        int level = 0;
1063        
1064        Widget parentItem = getParentWidget(item);
1065        if (DEBUG_VIEWER) {
1066            DebugUIPlugin.debug("SET DATA [" + index + "]: " + parentItem); //$NON-NLS-1$//$NON-NLS-2$
1067
}
1068        ModelNode node = null;
1069        // first, see if the parent element is in the model
1070
// and look directly for the child
1071
if (parentItem != null) {
1072            ModelNode[] nodes = getModel().getNodes(parentItem.getData());
1073            if (nodes != null) {
1074                for (int i = 0; i < nodes.length; i++) {
1075                    ModelNode parentNode = nodes[i];
1076                    Widget parentWidget = findItem(parentNode);
1077                    if (parentWidget == parentItem) {
1078                        ModelNode[] childrenNodes = parentNode.getChildrenNodes();
1079                        if (childrenNodes != null && index < childrenNodes.length) {
1080                            node = childrenNodes[index];
1081                        }
1082                    }
1083                }
1084            }
1085        }
1086
1087        // otherwise, build a path to the model node
1088
if (node == null) {
1089            setNodeIndex(index, level);
1090            while (parentItem instanceof Item) {
1091                level++;
1092                Widget parent = getParentWidget(parentItem);
1093                int pindex = indexOf(parent, parentItem);
1094                if (pindex < 0) {
1095                    return;
1096                }
1097                setNodeIndex(pindex, level);
1098                parentItem = parent;
1099            }
1100            
1101            node = getModel().getRootNode();
1102            if (node == null) {
1103                if (DEBUG_VIEWER) {
1104                    DebugUIPlugin.debug("\tFAILED - root model node is null"); //$NON-NLS-1$
1105
}
1106                return;
1107            }
1108            for (int i = level; i >= 0; i--) {
1109                ModelNode[] childrenNodes = node.getChildrenNodes();
1110                if (childrenNodes == null) {
1111                    if (DEBUG_VIEWER) {
1112                        DebugUIPlugin.debug("\tFAILED - no children nodes for " + node); //$NON-NLS-1$
1113
}
1114                    return;
1115                }
1116                int pindex = getNodeIndex(i);
1117                if (pindex < childrenNodes.length) {
1118                    node = childrenNodes[pindex];
1119                } else {
1120                    if (DEBUG_VIEWER) {
1121                        DebugUIPlugin.debug("\tFAILED - no children nodes for " + node); //$NON-NLS-1$
1122
}
1123                    return;
1124                }
1125            }
1126        }
1127        
1128        
1129        // map the node to the element and refresh it
1130
if (node != null) {
1131            mapElement(node, item);
1132            item.setData(node.getElement());
1133            if (DEBUG_VIEWER) {
1134                DebugUIPlugin.debug("\titem mapped: " + node); //$NON-NLS-1$
1135
}
1136            internalRefresh(node);
1137        } else {
1138            if (DEBUG_VIEWER) {
1139                DebugUIPlugin.debug("\tFAILED - unable to find corresponding node"); //$NON-NLS-1$
1140
}
1141        }
1142    }
1143    
1144    /**
1145     * Sets the index of a child node being mapped at the given expansion level
1146     * in the tree.
1147     *
1148     * @param nodeIndex
1149     * @param level
1150     */

1151    private void setNodeIndex(int nodeIndex, int level) {
1152        if (level > (fSetDataIndicies.length - 1)) {
1153            // grow the array
1154
int[] next = new int[level+5];
1155            System.arraycopy(fSetDataIndicies, 0, next, 0, fSetDataIndicies.length);
1156            fSetDataIndicies = next;
1157        }
1158        fSetDataIndicies[level] = nodeIndex;
1159    }
1160    
1161    /**
1162     * Returns the index of a child node being mapped at the given expansion level in
1163     * the tree.
1164     *
1165     * @param level
1166     * @return
1167     */

1168    private int getNodeIndex(int level) {
1169        return fSetDataIndicies[level];
1170    }
1171    
1172    protected abstract int indexOf(Widget parent, Widget child);
1173    
1174    protected abstract void restoreLabels(Item item);
1175    
1176    /**
1177     * Returns the parent widget for the given widget or <code>null</code>
1178     *
1179     * @param widget
1180     * @return parent widget or <code>null</code>
1181     */

1182    protected abstract Widget getParentWidget(Widget widget);
1183
1184    /**
1185     * Updates the children of the given node.
1186     *
1187     * @param parent
1188     * node of which to update children
1189     */

1190    protected void updateChildren(ModelNode parent) {
1191        getModel().updateChildren(parent);
1192    }
1193}
1194
Popular Tags