KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > compare > CompareEditorInput


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.compare;
12
13 import java.lang.reflect.InvocationTargetException JavaDoc;
14 import java.util.*;
15
16 import org.eclipse.compare.contentmergeviewer.IFlushable;
17 import org.eclipse.compare.internal.*;
18 import org.eclipse.compare.structuremergeviewer.*;
19 import org.eclipse.core.resources.IFile;
20 import org.eclipse.core.runtime.*;
21 import org.eclipse.core.runtime.jobs.Job;
22 import org.eclipse.jface.action.*;
23 import org.eclipse.jface.dialogs.ErrorDialog;
24 import org.eclipse.jface.dialogs.IDialogConstants;
25 import org.eclipse.jface.operation.IRunnableWithProgress;
26 import org.eclipse.jface.preference.IPreferenceStore;
27 import org.eclipse.jface.resource.ImageDescriptor;
28 import org.eclipse.jface.text.IFindReplaceTarget;
29 import org.eclipse.jface.util.IPropertyChangeListener;
30 import org.eclipse.jface.util.PropertyChangeEvent;
31 import org.eclipse.jface.viewers.*;
32 import org.eclipse.osgi.util.NLS;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.BusyIndicator;
35 import org.eclipse.swt.events.DisposeEvent;
36 import org.eclipse.swt.events.DisposeListener;
37 import org.eclipse.swt.graphics.Image;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Control;
40 import org.eclipse.ui.*;
41 import org.eclipse.ui.part.*;
42 import org.eclipse.ui.services.IServiceLocator;
43
44
45 /**
46  * A compare operation which can present its results in a special editor.
47  * Running the compare operation and presenting the results in a compare editor
48  * are combined in one class because it allows a client to keep the implementation
49  * all in one place while separating it from the innards of a specific UI implementation of compare/merge.
50  * <p>
51  * A <code>CompareEditorInput</code> defines methods for the following sequence steps:
52  * <UL>
53  * <LI>running a lengthy compare operation under progress monitor control,
54  * <LI>creating a UI for displaying the model and initializing the some widgets with the compare result,
55  * <LI>tracking the dirty state of the model in case of merge,
56  * <LI>saving the model.
57  * </UL>
58  * The Compare plug-in's <code>openCompareEditor</code> method takes an <code>ICompareEditorInput</code>
59  * and starts sequencing through the above steps. If the compare result is not empty a new compare editor
60  * is opened and takes over the sequence until eventually closed.
61  * <p>
62  * The <code>prepareInput</code> method should contain the
63  * code of the compare operation. It is executed under control of a progress monitor
64  * and can be canceled. If the result of the compare is not empty, that is if there are differences
65  * that needs to be presented, the <code>ICompareEditorInput</code> should hold onto them and return them with
66  * the <code>getCompareResult</code> method.
67  * If the value returned from <code>getCompareResult</code> is not <code>null</code>
68  * a compare editor is opened on the <code>ICompareEditorInput</code> with title and title image initialized by the
69  * corresponding methods of the <code>ICompareEditorInput</code>.
70  * <p>
71  * Creation of the editor's SWT controls is delegated to the <code>createContents</code> method.
72  * Here the SWT controls must be created and initialized with the result of the compare operation.
73  * <p>
74  * If merging is allowed, the modification state of the compared constituents must be tracked and the dirty
75  * state returned from method <code>isSaveNeeded</code>. The value <code>true</code> triggers a subsequent call
76  * to <code>save</code> where the modified resources can be saved.
77  * <p>
78  * The most important part of this implementation is the setup of the compare/merge UI.
79  * The UI uses a simple browser metaphor to present compare results.
80  * The top half of the layout shows the structural compare results (e.g. added, deleted, and changed files),
81  * the bottom half the content compare results (e.g. textual differences between two files).
82  * A selection in the top pane is fed to the bottom pane. If a content viewer is registered
83  * for the type of the selected object, this viewer is installed in the pane.
84  * In addition if a structure viewer is registered for the selection type the top pane
85  * is split horizontally to make room for another pane and the structure viewer is installed
86  * in it. When comparing Java files this second structure viewer would show the structural
87  * differences within a Java file, e.g. added, deleted or changed methods and fields.
88  * <p>
89  * Subclasses provide custom setups, e.g. for a Catch-up/Release operation
90  * by passing a subclass of <code>CompareConfiguration</code> and by implementing the <code>prepareInput</code> method.
91  * If a subclass cannot use the <code>DiffTreeViewer</code> which is installed by default in the
92  * top left pane, method <code>createDiffViewer</code> can be overridden.
93  * <p>
94  * If subclasses of this class implement {@link ISaveablesSource}, the compare editor will
95  * pass these models through to the workbench. The editor will still show the dirty indicator
96  * if one of these underlying models is dirty. It is the responsibility of subclasses that
97  * implement this interface to call {@link #setDirty(boolean)} when the dirty state of
98  * any of the models managed by the subclass change dirty state.
99  *
100  * @see CompareUI
101  * @see CompareEditorInput
102  */

103 public abstract class CompareEditorInput implements IEditorInput, IPropertyChangeNotifier, IRunnableWithProgress, ICompareContainer {
104
105     private static final boolean DEBUG= false;
106
107     /**
108      * The name of the "dirty" property (value <code>"DIRTY_STATE"</code>).
109      */

110     public static final String JavaDoc DIRTY_STATE= "DIRTY_STATE"; //$NON-NLS-1$
111

112     /**
113      * The name of the "title" property. This property is fired when the title
114      * of the compare input changes. Clients should also re-obtain the tool tip
115      * when this property changes.
116      * @see #getTitle()
117      * @since 3.3
118      */

119     public static final String JavaDoc PROP_TITLE= ICompareUIConstants.PROP_TITLE;
120     
121     /**
122      * The name of the "title image" property. This property is fired when the title
123      * image of the compare input changes.
124      * @see #getTitleImage()
125      * @since 3.3
126      */

127     public static final String JavaDoc PROP_TITLE_IMAGE= ICompareUIConstants.PROP_TITLE_IMAGE;
128     
129     /**
130      * The name of the "selected edition" property. This property is fired when the selected
131      * edition of the compare input changes.
132      * @see #isEditionSelectionDialog()
133      * @see #getSelectedEdition()
134      * @since 3.3
135      */

136     public static final String JavaDoc PROP_SELECTED_EDITION= ICompareUIConstants.PROP_SELECTED_EDITION;
137         
138     private static final String JavaDoc COMPARE_EDITOR_IMAGE_NAME= "eview16/compare_view.gif"; //$NON-NLS-1$
139
private static Image fgTitleImage;
140     
141     private Splitter fComposite;
142     private CompareConfiguration fCompareConfiguration;
143     private CompareViewerPane fStructureInputPane;
144     private CompareViewerSwitchingPane fStructurePane1;
145     private CompareViewerSwitchingPane fStructurePane2;
146     private CompareViewerSwitchingPane fContentInputPane;
147     private CompareViewerPane fFocusPane;
148     private String JavaDoc fMessage;
149     private Object JavaDoc fInput;
150     private String JavaDoc fTitle;
151     private ListenerList fListenerList= new ListenerList();
152     private CompareNavigator fNavigator;
153     private boolean fDirty= false;
154     private ArrayList fDirtyViewers= new ArrayList();
155     private IPropertyChangeListener fDirtyStateListener;
156     
157     boolean fStructureCompareOnSingleClick= true;
158
159     private ICompareContainer fContainer;
160     private boolean fContainerProvided;
161
162     private String JavaDoc fHelpContextId;
163     private InternalOutlineViewerCreator fOutlineView;
164     private ICompareAsText fCompareAsText;
165     
166     private class InternalOutlineViewerCreator extends OutlineViewerCreator {
167         private OutlineViewerCreator getWrappedCreator() {
168             if (fContentInputPane != null) {
169                 Viewer v = fContentInputPane.getViewer();
170                 if (v != null) {
171                     return (OutlineViewerCreator)Utilities.getAdapter(v, OutlineViewerCreator.class);
172                 }
173             }
174             return null;
175         }
176         public Viewer findStructureViewer(Viewer oldViewer,
177                 ICompareInput input, Composite parent,
178                 CompareConfiguration configuration) {
179             OutlineViewerCreator creator = getWrappedCreator();
180             if (creator != null)
181                 return creator.findStructureViewer(oldViewer, input, parent, configuration);
182             return null;
183         }
184
185         public boolean hasViewerFor(Object JavaDoc input) {
186             OutlineViewerCreator creator = getWrappedCreator();
187             return (creator != null);
188         }
189
190         public Object JavaDoc getInput() {
191             OutlineViewerCreator creator = getWrappedCreator();
192             if (creator != null)
193                 return creator.getInput();
194             return null;
195         }
196     }
197     
198     private class CompareAsText implements ICompareAsText {
199         public void compareAsText(Object JavaDoc input) {
200             internalCompareAsText(input);
201         }
202     }
203
204     /**
205      * Creates a <code>CompareEditorInput</code> which is initialized with the given
206      * compare configuration.
207      * The compare configuration is passed to subsequently created viewers.
208      *
209      * @param configuration the compare configuration
210      */

211     public CompareEditorInput(CompareConfiguration configuration) {
212         fCompareConfiguration= configuration;
213         Assert.isNotNull(configuration);
214
215         fDirtyStateListener= new IPropertyChangeListener() {
216             public void propertyChange(PropertyChangeEvent e) {
217                 String JavaDoc propertyName= e.getProperty();
218                 if (CompareEditorInput.DIRTY_STATE.equals(propertyName)) {
219                     boolean changed= false;
220                     Object JavaDoc newValue= e.getNewValue();
221                     if (newValue instanceof Boolean JavaDoc)
222                         changed= ((Boolean JavaDoc)newValue).booleanValue();
223                     setDirty(e.getSource(), changed);
224                 }
225             }
226         };
227
228         IPreferenceStore ps= configuration.getPreferenceStore();
229         if (ps != null)
230             fStructureCompareOnSingleClick= ps.getBoolean(ComparePreferencePage.OPEN_STRUCTURE_COMPARE);
231         
232         fContainer = configuration.getContainer();
233         configuration.setContainer(this);
234     }
235     
236     /* package */ void internalCompareAsText(Object JavaDoc input) {
237         Set set = (Set)getCompareConfiguration().getProperty(ICompareAsText.PROP_TEXT_INPUTS);
238         if (set == null) {
239             set = new HashSet();
240             getCompareConfiguration().setProperty(ICompareAsText.PROP_TEXT_INPUTS, set);
241         }
242         set.add(input);
243         if (fContentInputPane.getInput().equals(input)) {
244             // We need to null the input and then reset it so we get the text merge viewer
245
fContentInputPane.setInput(null);
246             fContentInputPane.setInput(input);
247         }
248     }
249
250     private boolean structureCompareOnSingleClick() {
251         return fStructureCompareOnSingleClick;
252     }
253     
254     private boolean isShowStructureInOutlineView() {
255         Object JavaDoc object= getCompareConfiguration().getProperty(CompareConfiguration.USE_OUTLINE_VIEW);
256         return (object instanceof Boolean JavaDoc && ((Boolean JavaDoc)object).booleanValue());
257     }
258         
259     /* (non Javadoc)
260      * see IAdaptable.getAdapter
261      */

262     public Object JavaDoc getAdapter(Class JavaDoc adapter) {
263         if (ICompareNavigator.class.equals(adapter) || CompareNavigator.class.equals(adapter)) {
264             return getNavigator();
265         }
266         if (adapter == IShowInSource.class) {
267             final IFile file = (IFile)Utilities.getAdapter(this, IFile.class);
268             if (file != null)
269                 return new IShowInSource() {
270                     public ShowInContext getShowInContext() {
271                         return new ShowInContext(new FileEditorInput(file), StructuredSelection.EMPTY);
272                     }
273                 };
274         }
275         if (adapter == OutlineViewerCreator.class) {
276             synchronized (this) {
277                 if (fOutlineView == null)
278                     fOutlineView = new InternalOutlineViewerCreator();
279                 return fOutlineView;
280             }
281         }
282         if (adapter == ICompareAsText.class) {
283             if (fCompareAsText == null)
284                 fCompareAsText = new CompareAsText();
285             return fCompareAsText;
286         }
287         if (adapter == IFindReplaceTarget.class) {
288             if (fContentInputPane != null) {
289                 Viewer v = fContentInputPane.getViewer();
290                 if (v != null) {
291                     return Utilities.getAdapter(v, IFindReplaceTarget.class);
292                 }
293             }
294         }
295             
296         return null;
297     }
298
299     public synchronized ICompareNavigator getNavigator() {
300         if (fNavigator == null)
301             fNavigator= new CompareEditorInputNavigator(
302                 new Object JavaDoc[] {
303                     fStructureInputPane,
304                     fStructurePane1,
305                     fStructurePane2,
306                     fContentInputPane
307                 }
308             );
309         return fNavigator;
310     }
311     
312     /* (non Javadoc)
313      * see IEditorInput.getImageDescriptor
314      */

315     public ImageDescriptor getImageDescriptor() {
316         return null;
317     }
318     
319     /* (non Javadoc)
320      * see IEditorInput.getToolTipText
321      */

322     public String JavaDoc getToolTipText() {
323         return getTitle();
324     }
325     
326     /* (non Javadoc)
327      * see IEditorInput.getName
328      */

329     public String JavaDoc getName() {
330         return getTitle();
331     }
332             
333     /**
334      * Returns <code>null</code> since this editor cannot be persisted.
335      *
336      * @return <code>null</code> because this editor cannot be persisted
337      */

338     public IPersistableElement getPersistable() {
339         return null;
340     }
341         
342     /**
343      * Returns <code>false</code> to indicate that this input
344      * should not appear in the "File Most Recently Used" menu.
345      *
346      * @return <code>false</code>
347      */

348     public boolean exists() {
349         return false;
350     }
351     
352     /*
353      * FIXME!
354      */

355     protected void setMessage(String JavaDoc message) {
356         fMessage= message;
357     }
358     
359     /*
360      * FIXME!
361      */

362     public String JavaDoc getMessage() {
363         return fMessage;
364     }
365                 
366     /**
367      * Returns the title which will be used in the compare editor's title bar.
368      * It can be set with <code>setTitle</code>.
369      *
370      * @return the title
371      */

372     public String JavaDoc getTitle() {
373         if (fTitle == null)
374             return Utilities.getString("CompareEditorInput.defaultTitle"); //$NON-NLS-1$
375
return fTitle;
376     }
377     
378     /**
379      * Sets the title which will be used when presenting the compare result.
380      * This method must be called before the editor is opened.
381      *
382      * @param title the title to use for the CompareEditor
383      */

384     public void setTitle(String JavaDoc title) {
385         String JavaDoc oldTitle = fTitle;
386         fTitle= title;
387         Utilities.firePropertyChange(fListenerList, this, PROP_TITLE, oldTitle, title);
388     }
389     
390     /**
391      * Returns the title image which will be used in the compare editor's title bar.
392      * Returns the title image which will be used when presenting the compare result.
393      * This implementation returns a generic compare icon.
394      * Subclasses can override.
395      *
396      * @return the title image, or <code>null</code> if none
397      */

398     public Image getTitleImage() {
399         if (fgTitleImage == null) {
400             fgTitleImage= CompareUIPlugin.getImageDescriptor(COMPARE_EDITOR_IMAGE_NAME).createImage();
401             CompareUI.disposeOnShutdown(fgTitleImage);
402         }
403         return fgTitleImage;
404     }
405     
406     /**
407      * Returns the configuration object for the viewers within the compare editor.
408      * Returns the configuration which was passed to the constructor.
409      *
410      * @return the compare configuration
411      */

412     public CompareConfiguration getCompareConfiguration() {
413         return fCompareConfiguration;
414     }
415
416     /**
417      * Adds standard actions to the given <code>ToolBarManager</code>.
418      * <p>
419      * Subclasses may override to add their own actions.
420      * </p>
421      *
422      * @param toolBarManager the <code>ToolBarManager</code> to which to contribute
423      */

424     public void contributeToToolBar(ToolBarManager toolBarManager) {
425         ResourceBundle bundle= CompareUI.getResourceBundle();
426         ChangePropertyAction ignoreWhitespace= ChangePropertyAction.createIgnoreWhiteSpaceAction(bundle, getCompareConfiguration());
427         toolBarManager.getControl().addDisposeListener(ignoreWhitespace);
428         ChangePropertyAction showPseudoConflicts= ChangePropertyAction.createShowPseudoConflictsAction(bundle, getCompareConfiguration());
429         toolBarManager.getControl().addDisposeListener(showPseudoConflicts);
430         toolBarManager.add(new Separator());
431         toolBarManager.add(ignoreWhitespace);
432         toolBarManager.add(showPseudoConflicts);
433     }
434     
435     /**
436      * Runs the compare operation and stores the compare result.
437      *
438      * @param monitor the progress monitor to use to display progress and receive
439      * requests for cancelation
440      * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
441      * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
442      * wrapped in an <code>InvocationTargetException</code> by the calling context
443      * @exception InterruptedException if the operation detects a request to cancel,
444      * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
445      * <code>InterruptedException</code>
446      */

447     public void run(IProgressMonitor monitor) throws InterruptedException JavaDoc, InvocationTargetException JavaDoc {
448         fInput= prepareInput(monitor);
449     }
450
451     /**
452      * Runs the compare operation and returns the compare result.
453      * If <code>null</code> is returned no differences were found and no compare editor needs to be opened.
454      * Progress should be reported to the given progress monitor.
455      * A request to cancel the operation should be honored and acknowledged
456      * by throwing <code>InterruptedException</code>.
457      * <p>
458      * Note: this method is typically called in a modal context thread which doesn't have a Display assigned.
459      * Implementors of this method shouldn't therefore allocated any SWT resources in this method.
460      * </p>
461      *
462      * @param monitor the progress monitor to use to display progress and receive
463      * requests for cancelation
464      * @return the result of the compare operation, or <code>null</code> if there are no differences
465      * @exception InvocationTargetException if the <code>prepareInput</code> method must propagate a checked exception,
466      * it should wrap it inside an <code>InvocationTargetException</code>; runtime exceptions are automatically
467      * wrapped in an <code>InvocationTargetException</code> by the calling context
468      * @exception InterruptedException if the operation detects a request to cancel,
469      * using <code>IProgressMonitor.isCanceled()</code>, it should exit by throwing
470      * <code>InterruptedException</code>
471      */

472     protected abstract Object JavaDoc prepareInput(IProgressMonitor monitor)
473                 throws InvocationTargetException JavaDoc, InterruptedException JavaDoc;
474      
475     /**
476      * Returns the compare result computed by the most recent call to the
477      * <code>run</code> method. Returns <code>null</code> if no
478      * differences were found.
479      *
480      * @return the compare result prepared in method <code>prepareInput</code>
481      * or <code>null</code> if there were no differences
482      */

483     public Object JavaDoc getCompareResult() {
484         return fInput;
485     }
486     
487     /**
488      * Create the SWT controls that are used to display the result of the compare operation.
489      * Creates the SWT Controls and sets up the wiring between the individual panes.
490      * This implementation creates all four panes but makes only the necessary ones visible.
491      * Finally it feeds the compare result into the top left structure viewer
492      * and the content viewer.
493      * <p>
494      * Subclasses may override if they need to change the layout or wiring between panes.
495      *
496      * @param parent the parent control under which the control must be created
497      * @return the SWT control hierarchy for the compare editor
498      */

499     public Control createContents(Composite parent) {
500
501         fComposite= new Splitter(parent, SWT.VERTICAL);
502         fComposite.setData(this);
503                 
504         Control outline= createOutlineContents(fComposite, SWT.HORIZONTAL);
505                     
506         fContentInputPane= new CompareViewerSwitchingPane(fComposite, SWT.BORDER | SWT.FLAT) {
507             protected Viewer getViewer(Viewer oldViewer, Object JavaDoc input) {
508                 if (input instanceof ICompareInput)
509                     return findContentViewer(oldViewer, (ICompareInput)input, this);
510                 return null;
511             }
512         };
513         if (fFocusPane == null)
514             fFocusPane= fContentInputPane;
515         if (outline != null)
516             fComposite.setVisible(outline, false);
517         fComposite.setVisible(fContentInputPane, true);
518         
519         if (fStructureInputPane != null)
520             fComposite.setWeights(new int[] { 30, 70 });
521         
522         fComposite.layout();
523
524         feedInput();
525     
526         fComposite.addDisposeListener(new DisposeListener() {
527             public void widgetDisposed(DisposeEvent e) {
528                 handleDispose();
529             }
530         });
531         if (fHelpContextId != null)
532             PlatformUI.getWorkbench().getHelpSystem().setHelp(fComposite, fHelpContextId);
533         contentsCreated();
534         return fComposite;
535     }
536     
537     /**
538      * Callback that occurs when the UI associated with this compare editor
539      * input is disposed. This method will only be invoked if the UI has been
540      * created (i.e. after the call to {@link #createContents(Composite)}.
541      * Subclasses can extend this method but ensure that the overridden method
542      * is invoked.
543      *
544      * @since 3.3
545      */

546     protected void handleDispose() {
547         fContainerProvided = false;
548         fContainer = null;
549         fCompareConfiguration.dispose();
550     }
551     
552     /**
553      * Callback that occurs after the control for the input has
554      * been created. If this method gets invoked then {@link #handleDispose()}
555      * will be invoked when the control is disposed. Subclasses may extend this
556      * method to register any listeners that need to be de-registered when the
557      * input is disposed.
558      * @since 3.3
559      */

560     protected void contentsCreated() {
561         // Default is to do nothing
562
}
563
564     /**
565      * @param parent the parent control under which the control must be created
566      * @param direction the layout direction of the contents, either </code>SWT.HORIZONTAL<code> or </code>SWT.VERTICAL<code>
567      * @return the SWT control hierarchy for the outline part of the compare editor
568      * @since 3.0
569      */

570     public Control createOutlineContents(Composite parent, int direction) {
571         final Splitter h= new Splitter(parent, direction);
572
573         fStructureInputPane= createStructureInputPane(h);
574         if (hasChildren(getCompareResult()))
575             fFocusPane= fStructureInputPane;
576         
577         fStructurePane1= new CompareViewerSwitchingPane(h, SWT.BORDER | SWT.FLAT, true) {
578             protected Viewer getViewer(Viewer oldViewer, Object JavaDoc input) {
579                 if (input instanceof ICompareInput)
580                     return findStructureViewer(oldViewer, (ICompareInput)input, this);
581                 return null;
582             }
583         };
584         h.setVisible(fStructurePane1, false);
585         
586         fStructurePane2= new CompareViewerSwitchingPane(h, SWT.BORDER | SWT.FLAT, true) {
587             protected Viewer getViewer(Viewer oldViewer, Object JavaDoc input) {
588                 if (input instanceof ICompareInput)
589                     return findStructureViewer(oldViewer, (ICompareInput)input, this);
590                 return null;
591             }
592         };
593         h.setVisible(fStructurePane2, false);
594         
595         // setup the wiring for top left pane
596
fStructureInputPane.addOpenListener(
597             new IOpenListener() {
598                 public void open(OpenEvent oe) {
599                     feed1(oe.getSelection());
600                 }
601             }
602         );
603         fStructureInputPane.addSelectionChangedListener(
604             new ISelectionChangedListener() {
605                 public void selectionChanged(SelectionChangedEvent e) {
606                     ISelection s= e.getSelection();
607                     if (s == null || s.isEmpty())
608                         feed1(s);
609                     if (isEditionSelectionDialog())
610                         firePropertyChange(new PropertyChangeEvent(this, PROP_SELECTED_EDITION, null, getSelectedEdition()));
611                 }
612             }
613         );
614         fStructureInputPane.addDoubleClickListener(
615             new IDoubleClickListener() {
616                 public void doubleClick(DoubleClickEvent event) {
617                     feedDefault1(event.getSelection());
618                 }
619             }
620         );
621         
622         fStructurePane1.addSelectionChangedListener(
623             new ISelectionChangedListener() {
624                 public void selectionChanged(SelectionChangedEvent e) {
625                     feed2(e.getSelection());
626                 }
627             }
628         );
629
630         fStructurePane2.addSelectionChangedListener(
631             new ISelectionChangedListener() {
632                 public void selectionChanged(SelectionChangedEvent e) {
633                     feed3(e.getSelection());
634                 }
635             }
636         );
637
638         return h;
639     }
640
641     /**
642      * Create the pane that will contain the structure input pane (upper left).
643      * By default, a {@link CompareViewerSwitchingPane} is returned. Subclasses
644      * may override to provide an alternate pane.
645      * @param parent the parent composite
646      * @return the structure input pane
647      * @since 3.3
648      */

649     protected CompareViewerPane createStructureInputPane(
650             final Composite parent) {
651         return new CompareViewerSwitchingPane(parent, SWT.BORDER | SWT.FLAT, true) {
652             protected Viewer getViewer(Viewer oldViewer, Object JavaDoc input) {
653                 if (CompareEditorInput.this.hasChildren(input)) {
654                     return createDiffViewer(this);
655                 }
656                 if (input instanceof ICompareInput)
657                     return findStructureViewer(oldViewer, (ICompareInput)input, this);
658                 return null;
659             }
660         };
661     }
662     
663     /* private */ boolean hasChildren(Object JavaDoc input) {
664         if (input instanceof IDiffContainer) {
665             IDiffContainer dn= (IDiffContainer) input;
666             return (dn.hasChildren());
667         }
668         return false;
669     }
670
671     private void feedInput() {
672         if (fStructureInputPane != null
673                 && (fInput instanceof ICompareInput
674                         || isCustomStructureInputPane())) {
675             if (hasChildren(fInput) || isCustomStructureInputPane()) {
676                 // The input has multiple entries so set the input of the structure input pane
677
fStructureInputPane.setInput(fInput);
678             } else if (!structureCompareOnSingleClick() || isShowStructureInOutlineView()) {
679                 // We want to avoid showing the structure in the editor if we can so first
680
// we'll set the content pane to see if we need to provide a structure
681
internalSetContentPaneInput(fInput);
682                 // If the content viewer is unusable
683
if (hasUnusableContentViewer()
684                         || (structureCompareOnSingleClick()
685                                 && isShowStructureInOutlineView()
686                                 && !hasOutlineViewer(fInput))) {
687                     fStructureInputPane.setInput(fInput);
688                 }
689             } else {
690                 fStructureInputPane.setInput(fInput);
691             }
692             ISelection sel= fStructureInputPane.getSelection();
693             if (sel == null || sel.isEmpty())
694                 feed1(sel); // we only feed downstream viewers if the top left pane is empty
695
}
696     }
697
698     private boolean hasOutlineViewer(Object JavaDoc input) {
699         if (!isShowStructureInOutlineView())
700             return false;
701         OutlineViewerCreator creator = (OutlineViewerCreator)getAdapter(OutlineViewerCreator.class);
702         if (creator != null)
703             return creator.hasViewerFor(input);
704         return false;
705     }
706
707     private boolean hasUnusableContentViewer() {
708         return fContentInputPane.isEmpty() || fContentInputPane.getViewer() instanceof BinaryCompareViewer;
709     }
710     
711     private boolean isCustomStructureInputPane() {
712         return !(fStructureInputPane instanceof CompareViewerSwitchingPane);
713     }
714
715     private void feed1(final ISelection selection) {
716         BusyIndicator.showWhile(fComposite.getDisplay(),
717             new Runnable JavaDoc() {
718                 public void run() {
719                     if (selection == null || selection.isEmpty()) {
720                         Object JavaDoc input= fStructureInputPane.getInput();
721                         if (input != null)
722                             internalSetContentPaneInput(input);
723                         fStructurePane2.setInput(null); // clear downstream pane
724
fStructurePane1.setInput(null);
725                     } else {
726                         Object JavaDoc input= getElement(selection);
727                         internalSetContentPaneInput(input);
728                         if (structureCompareOnSingleClick() || hasUnusableContentViewer())
729                             fStructurePane1.setInput(input);
730                         fStructurePane2.setInput(null); // clear downstream pane
731
if (fStructurePane1.getInput() != input)
732                             fStructurePane1.setInput(null);
733                     }
734                 }
735             }
736         );
737     }
738     
739     private void feedDefault1(final ISelection selection) {
740         BusyIndicator.showWhile(fComposite.getDisplay(),
741             new Runnable JavaDoc() {
742                 public void run() {
743                     if (!selection.isEmpty())
744                         fStructurePane1.setInput(getElement(selection));
745                 }
746             }
747         );
748     }
749     
750     private void feed2(final ISelection selection) {
751         BusyIndicator.showWhile(fComposite.getDisplay(),
752             new Runnable JavaDoc() {
753                 public void run() {
754                     if (selection.isEmpty()) {
755                         Object JavaDoc input= fStructurePane1.getInput();
756                         internalSetContentPaneInput(input);
757                         fStructurePane2.setInput(null);
758                     } else {
759                         Object JavaDoc input= getElement(selection);
760                         internalSetContentPaneInput(input);
761                         fStructurePane2.setInput(input);
762                     }
763                 }
764             }
765         );
766     }
767     
768     private void feed3(final ISelection selection) {
769         BusyIndicator.showWhile(fComposite.getDisplay(),
770             new Runnable JavaDoc() {
771                 public void run() {
772                     if (selection.isEmpty())
773                         internalSetContentPaneInput(fStructurePane2.getInput());
774                     else
775                         internalSetContentPaneInput(getElement(selection));
776                 }
777             }
778         );
779         
780     }
781     
782     private void internalSetContentPaneInput(Object JavaDoc input) {
783         Object JavaDoc oldInput = fContentInputPane.getInput();
784         fContentInputPane.setInput(input);
785         if (fOutlineView != null)
786             fOutlineView.fireInputChange(oldInput, input);
787     }
788     
789     /**
790      * Returns the first element of the given selection if the selection
791      * is a <code>IStructuredSelection</code> with exactly one element. Returns
792      * <code>null</code> otherwise.
793      *
794      * @param selection the selection
795      * @return the first element of the selection, or <code>null</code>
796      */

797     private static Object JavaDoc getElement(ISelection selection) {
798         if (selection instanceof IStructuredSelection) {
799             IStructuredSelection ss= (IStructuredSelection) selection;
800             if (ss.size() == 1)
801                 return ss.getFirstElement();
802         }
803         return null;
804     }
805     
806     /**
807      * Asks this input to take focus within its container (editor).
808      * <p>
809      * Clients should not call this method but they may
810      * override if they implement a different layout with different visual
811      * components. Clients are free to call the inherited method.
812      * </p>
813      */

814     public void setFocus() {
815         if (fFocusPane != null) {
816             fFocusPane.setFocus();
817         } else if (fComposite != null)
818             fComposite.setFocus();
819     }
820     
821     /**
822      * Factory method for creating a differences viewer for the top left pane.
823      * It is called from <code>createContents</code> and returns a <code>DiffTreeViewer</code>.
824      * <p>
825      * Subclasses may override if they need a different viewer.
826      * </p>
827      *
828      * @param parent the SWT parent control under which to create the viewer's SWT controls
829      * @return a compare viewer for the top left pane
830      */

831     public Viewer createDiffViewer(Composite parent) {
832         return new DiffTreeViewer(parent, fCompareConfiguration);
833     }
834
835     /**
836      * Implements the dynamic viewer switching for structure viewers.
837      * The method must return a compare viewer based on the old (or current) viewer
838      * and a new input object. If the old viewer is suitable for showing the new input the old viewer
839      * can be returned. Otherwise a new viewer must be created under the given parent composite or
840      * <code>null</code> can be returned to indicate that no viewer could be found.
841      * <p>
842      * This implementation forwards the request to <code>CompareUI.findStructureViewer</code>.
843      * <p>
844      * Subclasses may override to implement a different strategy.
845      * </p>
846      * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
847      * @param input the input object for which to find a structure viewer
848      * @param parent the SWT parent composite under which the new viewer is created
849      * @return a compare viewer which is suitable for the given input object or <code>null</code>
850      */

851     public Viewer findStructureViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
852         return CompareUI.findStructureViewer(oldViewer, input, parent, fCompareConfiguration);
853     }
854
855     /**
856      * Implements the dynamic viewer switching for content viewers.
857      * The method must return a compare viewer based on the old (or current) viewer
858      * and a new input object. If the old viewer is suitable for showing the new input the old viewer
859      * can be returned. Otherwise a new viewer must be created under the given parent composite or
860      * <code>null</code> can be returned to indicate that no viewer could be found.
861      * <p>
862      * This implementation forwards the request to <code>CompareUI.findContentViewer</code>.
863      * <p>
864      * Subclasses may override to implement a different strategy.
865      * </p>
866      * @param oldViewer a new viewer is only created if this old viewer cannot show the given input
867      * @param input the input object for which to find a structure viewer
868      * @param parent the SWT parent composite under which the new viewer is created
869      * @return a compare viewer which is suitable for the given input object or <code>null</code>
870      */

871     public Viewer findContentViewer(Viewer oldViewer, ICompareInput input, Composite parent) {
872
873         Viewer newViewer= CompareUI.findContentViewer(oldViewer, input, parent, fCompareConfiguration);
874         
875         boolean isNewViewer= newViewer != oldViewer;
876         if (DEBUG) System.out.println("CompareEditorInput.findContentViewer: " + isNewViewer); //$NON-NLS-1$
877

878         if (isNewViewer && newViewer instanceof IPropertyChangeNotifier) {
879             final IPropertyChangeNotifier dsp= (IPropertyChangeNotifier) newViewer;
880             dsp.addPropertyChangeListener(fDirtyStateListener);
881             
882             Control c= newViewer.getControl();
883             c.addDisposeListener(
884                 new DisposeListener() {
885                     public void widgetDisposed(DisposeEvent e) {
886                         dsp.removePropertyChangeListener(fDirtyStateListener);
887                     }
888                 }
889             );
890         }
891         
892         return newViewer;
893     }
894     
895     /**
896      * Returns <code>true</code> if there are unsaved changes.
897      * The value returned is the value of the <code>DIRTY_STATE</code> property of this input object.
898      
899      * Returns <code>true</code> if this input has unsaved changes,
900      * that is if <code>setDirty(true)</code> has been called.
901      * Subclasses don't have to override if the functionality provided by <code>setDirty</code>
902      * is sufficient.
903      *
904      * @return <code>true</code> if there are changes that need to be saved
905      */

906     public boolean isSaveNeeded() {
907         return fDirty || fDirtyViewers.size() > 0;
908     }
909     
910     /**
911      * Returns <code>true</code> if there are unsaved changes.
912      * The method should be called by any parts or dialogs
913      * that contain the input.
914      * By default, this method calls {@link #isSaveNeeded()}
915      * but subclasses may extend.
916      * @return <code>true</code> if there are unsaved changes
917      * @since 3.3
918      */

919     public boolean isDirty() {
920         return isSaveNeeded();
921     }
922         
923     /**
924      * Sets the dirty state of this input to the given
925      * value and sends out a <code>PropertyChangeEvent</code> if the new value differs from the old value.
926      *
927      * @param dirty the dirty state for this compare input
928      */

929     public void setDirty(boolean dirty) {
930         boolean oldDirty = fDirty || fDirtyViewers.size() > 0;
931         fDirty= dirty;
932         if (!fDirty)
933             fDirtyViewers.clear();
934         if (oldDirty != dirty)
935             Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean JavaDoc(oldDirty), new Boolean JavaDoc(dirty));
936     }
937     
938     private void setDirty(Object JavaDoc source, boolean dirty) {
939         Assert.isNotNull(source);
940         boolean oldDirty= fDirty || fDirtyViewers.size() > 0;
941         if (dirty)
942             fDirtyViewers.add(source);
943         else
944             fDirtyViewers.remove(source);
945         boolean newDirty= fDirty || fDirtyViewers.size() > 0;
946         if (DEBUG) System.out.println("setDirty("+source+", "+dirty+"): " + newDirty); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
947
if (oldDirty != newDirty)
948             Utilities.firePropertyChange(fListenerList, this, DIRTY_STATE, new Boolean JavaDoc(oldDirty), new Boolean JavaDoc(newDirty));
949     }
950     
951     /* (non Javadoc)
952      * see IPropertyChangeNotifier.addListener
953      */

954     public void addPropertyChangeListener(IPropertyChangeListener listener) {
955         if (listener != null)
956             fListenerList.add(listener);
957     }
958
959     /* (non Javadoc)
960      * see IPropertyChangeNotifier.removeListener
961      */

962     public void removePropertyChangeListener(IPropertyChangeListener listener) {
963         if (listener != null)
964             fListenerList.remove(listener);
965     }
966
967     /**
968      * Save any unsaved changes.
969      * Empty implementation.
970      * Subclasses must override to save any changes.
971      *
972      * @param pm an <code>IProgressMonitor</code> that the implementation of save may use to show progress
973      * @deprecated Override method saveChanges instead.
974      */

975     public void save(IProgressMonitor pm) {
976         // empty default implementation
977
}
978     
979     /**
980      * Save any unsaved changes.
981      * Subclasses must override to save any changes.
982      * This implementation tries to flush changes in all viewers by
983      * calling <code>ISavable.save</code> on them.
984      *
985      * @param monitor an <code>IProgressMonitor</code> that the implementation of save may use to show progress
986      * @throws CoreException
987      * @since 2.0
988      */

989     public void saveChanges(IProgressMonitor monitor) throws CoreException {
990         
991         flushViewers(monitor);
992
993         save(monitor);
994     }
995
996     /**
997      * Flush the viewer contents into the input.
998      * @param monitor a progress monitor
999      * @since 3.3
1000     */

1001    protected void flushViewers(IProgressMonitor monitor) {
1002        // flush changes in any dirty viewer
1003
flushViewer(fStructureInputPane, monitor);
1004        flushViewer(fStructurePane1, monitor);
1005        flushViewer(fStructurePane2, monitor);
1006        flushViewer(fContentInputPane, monitor);
1007    }
1008        
1009    private static void flushViewer(CompareViewerPane pane, IProgressMonitor pm) {
1010        if (pane != null) {
1011            IFlushable flushable = (IFlushable)Utilities.getAdapter(pane, IFlushable.class);
1012            if (flushable != null)
1013                flushable.flush(pm);
1014        }
1015    }
1016    
1017    /* (non-Javadoc)
1018     * @see org.eclipse.compare.ICompareContainer#addCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
1019     */

1020    public void addCompareInputChangeListener(ICompareInput input,
1021            ICompareInputChangeListener listener) {
1022        if (fContainer == null) {
1023            input.addCompareInputChangeListener(listener);
1024        } else {
1025            fContainer.addCompareInputChangeListener(input, listener);
1026        }
1027    }
1028    
1029    /* (non-Javadoc)
1030     * @see org.eclipse.compare.ICompareContainer#removeCompareInputChangeListener(org.eclipse.compare.structuremergeviewer.ICompareInput, org.eclipse.compare.structuremergeviewer.ICompareInputChangeListener)
1031     */

1032    public void removeCompareInputChangeListener(ICompareInput input,
1033            ICompareInputChangeListener listener) {
1034        if (fContainer == null) {
1035            input.removeCompareInputChangeListener(listener);
1036        } else {
1037            fContainer.removeCompareInputChangeListener(input, listener);
1038        }
1039    }
1040    
1041    /* (non-Javadoc)
1042     * @see org.eclipse.compare.ICompareContainer#registerContextMenu(org.eclipse.jface.action.MenuManager, org.eclipse.jface.viewers.ISelectionProvider)
1043     */

1044    public void registerContextMenu(MenuManager menu, ISelectionProvider selectionProvider) {
1045        if (fContainer != null)
1046            fContainer.registerContextMenu(menu, selectionProvider);
1047    }
1048    
1049    /* (non-Javadoc)
1050     * @see org.eclipse.compare.ICompareContainer#setStatusMessage(java.lang.String)
1051     */

1052    public void setStatusMessage(String JavaDoc message) {
1053        if (!fContainerProvided) {
1054            // Try the action bars directly
1055
IActionBars actionBars= getActionBars();
1056            if (actionBars != null) {
1057                IStatusLineManager slm= actionBars.getStatusLineManager();
1058                if (slm != null) {
1059                    slm.setMessage(message);
1060                }
1061            }
1062        } else if (fContainer != null) {
1063            fContainer.setStatusMessage(message);
1064        }
1065    }
1066
1067    /* (non-Javadoc)
1068     * @see org.eclipse.compare.ICompareContainer#getActionBars()
1069     */

1070    public IActionBars getActionBars() {
1071        IActionBars actionBars = fContainer.getActionBars();
1072        if (actionBars == null && !fContainerProvided) {
1073            // The old way to find the action bars
1074
return Utilities.findActionBars(fComposite);
1075        }
1076        return actionBars;
1077    }
1078    
1079    /* (non-Javadoc)
1080     * @see org.eclipse.compare.ICompareContainer#getServiceLocator()
1081     */

1082    public IServiceLocator getServiceLocator() {
1083        IServiceLocator serviceLocator = fContainer.getServiceLocator();
1084        if (serviceLocator == null && !fContainerProvided) {
1085            // The old way to find the service locator
1086
return Utilities.findSite(fComposite);
1087        }
1088        return serviceLocator;
1089    }
1090    
1091    /* (non-Javadoc)
1092     * @see org.eclipse.compare.ICompareContainer#getWorkbenchPart()
1093     */

1094    public IWorkbenchPart getWorkbenchPart() {
1095        if (fContainer != null)
1096            return fContainer.getWorkbenchPart();
1097        return null;
1098    }
1099    
1100    /* (non-Javadoc)
1101     * @see org.eclipse.jface.operation.IRunnableContext#run(boolean, boolean, org.eclipse.jface.operation.IRunnableWithProgress)
1102     */

1103    public void run(boolean fork, boolean cancelable,
1104            IRunnableWithProgress runnable) throws InvocationTargetException JavaDoc,
1105            InterruptedException JavaDoc {
1106        if (fContainer != null)
1107            fContainer.run(fork, cancelable, runnable);
1108    }
1109    
1110    public void runAsynchronously(IRunnableWithProgress runnable) {
1111        if (fContainer != null)
1112            fContainer.runAsynchronously(runnable);
1113    }
1114    
1115    /**
1116     * Set the container of this input to the given container
1117     * @param container the container
1118     * @since 3.3
1119     */

1120    public void setContainer(ICompareContainer container) {
1121        Assert.isNotNull(container);
1122        this.fContainer = container;
1123        fContainerProvided = true;
1124    }
1125
1126    /**
1127     * Return the container of this input or <code>null</code> if there is no container
1128     * set.
1129     * @return the container of this input or <code>null</code>
1130     * @since 3.3
1131     */

1132    public final ICompareContainer getContainer() {
1133        return fContainer;
1134    }
1135    
1136    /**
1137     * Fire the given property change event to all listeners
1138     * registered with this compare editor input.
1139     * @param event the property change event
1140     * @since 3.3
1141     */

1142    protected void firePropertyChange(PropertyChangeEvent event) {
1143        Utilities.firePropertyChange(fListenerList, event);
1144    }
1145    
1146    /**
1147     * Return whether this compare editor input can be run as a job.
1148     * By default, <code>false</code> is returned since traditionally inputs
1149     * were prepared in the foreground (i.e the UI was blocked when the
1150     * {@link #run(IProgressMonitor)} method (and indirectly the
1151     * {@link #prepareInput(IProgressMonitor)} method) was invoked. Subclasses
1152     * may override.
1153     * @return whether this compare editor input can be run in the background
1154     * @since 3.3
1155     */

1156    public boolean canRunAsJob() {
1157        return false;
1158    }
1159
1160    /**
1161     * Return whether this input belongs to the given family
1162     * when it is run as a job.
1163     * @see #canRunAsJob()
1164     * @see Job#belongsTo(Object)
1165     * @param family the job family
1166     * @return whether this input belongs to the given family
1167     * @since 3.3
1168     */

1169    public boolean belongsTo(Object JavaDoc family) {
1170        return family == this;
1171    }
1172    
1173    /**
1174     * Return whether this input is intended to be used to select
1175     * a particular edition of an element in a dialog. The result
1176     * of this method is only consider if neither sides of the
1177     * input are editable. By default, <code>false</code> is returned.
1178     * @return whether this input is intended to be used to select
1179     * a particular edition of an element in a dialog
1180     * @see #getOKButtonLabel()
1181     * @see #okPressed()
1182     * @see #getSelectedEdition()
1183     * @since 3.3
1184     */

1185    public boolean isEditionSelectionDialog() {
1186        return false;
1187    }
1188    
1189    /**
1190     * Return the label to be used for the <code>OK</code>
1191     * button when this input is displayed in a dialog.
1192     * By default, different labels are used depending on
1193     * whether the input is editable or is for edition selection
1194     * (see {@link #isEditionSelectionDialog()}.
1195     * @return the label to be used for the <code>OK</code>
1196     * button when this input is displayed in a dialog
1197     * @since 3.3
1198     */

1199    public String JavaDoc getOKButtonLabel() {
1200        if (isEditable())
1201            return CompareMessages.CompareDialog_commit_button;
1202        if (isEditionSelectionDialog())
1203            return CompareMessages.CompareEditorInput_0;
1204        return IDialogConstants.OK_LABEL;
1205    }
1206    
1207    /**
1208     * Return the label used for the <code>CANCEL</code>
1209     * button when this input is shown in a compare dialog
1210     * using {@link CompareUI#openCompareDialog(CompareEditorInput)}.
1211     * @return the label used for the <code>CANCEL</code> button
1212     * @since 3.3
1213     */

1214    public String JavaDoc getCancelButtonLabel() {
1215        return IDialogConstants.CANCEL_LABEL;
1216    }
1217
1218    private boolean isEditable() {
1219        return getCompareConfiguration().isLeftEditable()
1220            || getCompareConfiguration().isRightEditable();
1221    }
1222    
1223    /**
1224     * The <code>OK</code> button was pressed in a dialog. If one or both of
1225     * the sides of the input is editable then any changes will be saved. If the
1226     * input is for edition selection (see {@link #isEditionSelectionDialog()}),
1227     * it is up to subclasses to override this method in order to perform the
1228     * appropriate operation on the selected edition.
1229     *
1230     * @return whether the dialog should be closed or not.
1231     * @since 3.3
1232     */

1233    public boolean okPressed() {
1234        if (isEditable()) {
1235            if (!saveChanges())
1236                return false;
1237        }
1238        return true;
1239    }
1240    
1241    /**
1242     * The <code>CANCEL</code> button was pressed in a dialog.
1243     * By default, nothing is done. Subclasses may override.
1244     * @since 3.3
1245     */

1246    public void cancelPressed() {
1247        // Do nothing
1248
}
1249    
1250    private boolean saveChanges() {
1251        try {
1252            PlatformUI.getWorkbench().getProgressService().run(true, true, new IRunnableWithProgress() {
1253                public void run(IProgressMonitor monitor) throws InvocationTargetException JavaDoc, InterruptedException JavaDoc {
1254                    try {
1255                        saveChanges(monitor);
1256                    } catch (CoreException e) {
1257                        throw new InvocationTargetException JavaDoc(e);
1258                    }
1259                }
1260            
1261            });
1262            return true;
1263        } catch (InterruptedException JavaDoc x) {
1264            // Ignore
1265
} catch (OperationCanceledException x) {
1266            // Ignore
1267
} catch (InvocationTargetException JavaDoc x) {
1268            ErrorDialog.openError(fComposite.getShell(), CompareMessages.CompareDialog_error_title, null,
1269                new Status(IStatus.ERROR, CompareUIPlugin.PLUGIN_ID, 0,
1270                    NLS.bind(CompareMessages.CompareDialog_error_message, x.getTargetException().getMessage()), x.getTargetException()));
1271        }
1272        return false;
1273    }
1274    
1275    /**
1276     * Return the selected edition or <code>null</code> if no edition is selected.
1277     * The result of this method should only be considered if {@link #isEditionSelectionDialog()}
1278     * returns <code>true</code>.
1279     * @return the selected edition or <code>null</code>
1280     * @since 3.3
1281     */

1282    public Object JavaDoc getSelectedEdition() {
1283        if (fStructureInputPane != null) {
1284            ISelection selection = fStructureInputPane.getSelection();
1285            if (selection instanceof IStructuredSelection) {
1286                IStructuredSelection ss = (IStructuredSelection) selection;
1287                if (!ss.isEmpty())
1288                    return ss.getFirstElement();
1289                
1290            }
1291        }
1292        return null;
1293    }
1294    
1295    /**
1296     * Set the help context id for this input.
1297     * @param helpContextId the help context id.
1298     * @since 3.3
1299     */

1300    public void setHelpContextId(String JavaDoc helpContextId) {
1301        this.fHelpContextId = helpContextId;
1302    }
1303    
1304}
1305
1306
Popular Tags