001: /*******************************************************************************
002: * Copyright (c) 2003, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.navigator;
011:
012: import org.eclipse.core.runtime.IAdaptable;
013: import org.eclipse.core.runtime.IProgressMonitor;
014: import org.eclipse.jface.action.IAction;
015: import org.eclipse.jface.viewers.DoubleClickEvent;
016: import org.eclipse.jface.viewers.IDoubleClickListener;
017: import org.eclipse.jface.viewers.ILabelProvider;
018: import org.eclipse.jface.viewers.ISelection;
019: import org.eclipse.jface.viewers.ISelectionChangedListener;
020: import org.eclipse.jface.viewers.IStructuredSelection;
021: import org.eclipse.jface.viewers.SelectionChangedEvent;
022: import org.eclipse.jface.viewers.StructuredSelection;
023: import org.eclipse.jface.viewers.TreeViewer;
024: import org.eclipse.jface.viewers.ViewerFilter;
025: import org.eclipse.swt.SWT;
026: import org.eclipse.swt.widgets.Composite;
027: import org.eclipse.ui.IEditorInput;
028: import org.eclipse.ui.IMemento;
029: import org.eclipse.ui.ISaveablePart;
030: import org.eclipse.ui.ISaveablesLifecycleListener;
031: import org.eclipse.ui.ISaveablesSource;
032: import org.eclipse.ui.IViewSite;
033: import org.eclipse.ui.PartInitException;
034: import org.eclipse.ui.PlatformUI;
035: import org.eclipse.ui.Saveable;
036: import org.eclipse.ui.SaveablesLifecycleEvent;
037: import org.eclipse.ui.actions.ActionGroup;
038: import org.eclipse.ui.internal.navigator.CommonNavigatorActionGroup;
039: import org.eclipse.ui.internal.navigator.CommonNavigatorManager;
040: import org.eclipse.ui.internal.navigator.NavigatorContentService;
041: import org.eclipse.ui.internal.navigator.NavigatorPlugin;
042: import org.eclipse.ui.internal.navigator.extensions.LinkHelperService;
043: import org.eclipse.ui.part.ISetSelectionTarget;
044: import org.eclipse.ui.part.IShowInTarget;
045: import org.eclipse.ui.part.ShowInContext;
046: import org.eclipse.ui.part.ViewPart;
047:
048: /**
049: * <p>
050: * This class provides the IViewPart for the Common Navigator framework in the
051: * Eclipse workbench. This class also serves as the backbone for navigational
052: * viewers. The following types are used by this class to render the Common
053: * Navigator:
054: * <ul>
055: * <li>
056: * <p>
057: * {@link org.eclipse.ui.navigator.CommonViewer}: The viewer that renders the
058: * extensible tree. Creates and manages the lifecylce of the Navigator Content
059: * Service (described below).
060: * </p>
061: * </li>
062: * <li>
063: * <p>
064: * {@link org.eclipse.ui.navigator.NavigatorActionService}: Manages instances
065: * of {@link org.eclipse.ui.navigator.CommonActionProvider}s provided by
066: * individual extensions and content extensions.
067: * </p>
068: * </li>
069: * <li>
070: * <p>
071: * {@link org.eclipse.ui.navigator.INavigatorContentService}: Manages instances
072: * of Navigator Content Extensions. Instances are created as needed, and
073: * disposed of upon the disposal of the Navigator Content Service.
074: * </p>
075: * </li>
076: * </ul>
077: * <p>
078: * Clients are not expected to subclass CommonNavigator. Clients that wish to
079: * define their own custom extensible navigator view need to specify an instance
080: * of the <b>org.eclipse.ui.views</b> extension point:
081: *
082: * <pre>
083: *
084: * <extension
085: * point="org.eclipse.ui.views">
086: * <view
087: * name="My Custom View"
088: * icon="relative/path/to/icon.gif"
089: * category="org.acme.mycategory"
090: * class="org.eclipse.ui.navigator.CommonNavigator"
091: * id="org.acme.MyCustomNavigatorID">
092: * </view>
093: * </extension>
094: *
095: * </pre>
096: *
097: * </p>
098: * Clients that wish to extend the view menu provided via the
099: * <b>org.eclipse.ui.popupMenu</b>s extension may specify the the <i>popupMenuId</i>
100: * specified by <b>org.eclipse.ui.navigator.viewer</b> (or a nested <b>popupMenu</b> element) of their target viewer
101: * as their target menu id.
102: *
103: * <p>
104: * This class may be instantiated; it is not intended to be subclassed.
105: * </p>
106: *
107: * @since 3.2
108: */
109: public class CommonNavigator extends ViewPart implements
110: ISetSelectionTarget, ISaveablePart, ISaveablesSource,
111: IShowInTarget {
112:
113: private static final Class INAVIGATOR_CONTENT_SERVICE = INavigatorContentService.class;
114: private static final Class COMMON_VIEWER_CLASS = CommonViewer.class;
115: private static final Class ISHOW_IN_TARGET_CLASS = IShowInTarget.class;
116:
117: private static final String HELP_CONTEXT = NavigatorPlugin.PLUGIN_ID
118: + ".common_navigator"; //$NON-NLS-1$
119:
120: /**
121: * <p>
122: * Used to track changes to the {@link #isLinkingEnabled} property.
123: * </p>
124: */
125: public static final int IS_LINKING_ENABLED_PROPERTY = 1;
126:
127: private CommonViewer commonViewer;
128:
129: private CommonNavigatorManager commonManager;
130:
131: private ActionGroup commonActionGroup;
132:
133: private IMemento memento;
134:
135: private boolean isLinkingEnabled = false;
136:
137: private String LINKING_ENABLED = "CommonNavigator.LINKING_ENABLED"; //$NON-NLS-1$
138:
139: private LinkHelperService linkService;
140:
141: /**
142: *
143: */
144: public CommonNavigator() {
145: super ();
146: }
147:
148: /**
149: * <p>
150: * Create the CommonViewer part control and setup the default providers as
151: * necessary.
152: * </p>
153: *
154: *
155: * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
156: */
157: public void createPartControl(Composite aParent) {
158:
159: commonViewer = createCommonViewer(aParent);
160:
161: try {
162: commonViewer.getControl().setRedraw(false);
163:
164: INavigatorFilterService filterService = commonViewer
165: .getNavigatorContentService().getFilterService();
166: ViewerFilter[] visibleFilters = filterService
167: .getVisibleFilters(true);
168: for (int i = 0; i < visibleFilters.length; i++) {
169: commonViewer.addFilter(visibleFilters[i]);
170: }
171:
172: commonViewer.setSorter(new CommonViewerSorter());
173:
174: /*
175: * make sure input is set after sorters and filters to avoid unnecessary
176: * refreshes
177: */
178: commonViewer.setInput(getInitialInput());
179:
180: getSite().setSelectionProvider(commonViewer);
181:
182: updateTitle();
183: } finally {
184: commonViewer.getControl().setRedraw(true);
185: }
186:
187: /*
188: * Create the CommonNavigatorManager last because information about the
189: * state of the CommonNavigator is required for the initialization of
190: * the CommonNavigatorManager
191: */
192: commonManager = createCommonManager();
193: if (memento != null) {
194: commonViewer.getNavigatorContentService().restoreState(
195: memento);
196: }
197:
198: commonActionGroup = createCommonActionGroup();
199: commonActionGroup.fillActionBars(getViewSite().getActionBars());
200:
201: ISaveablesLifecycleListener saveablesLifecycleListener = new ISaveablesLifecycleListener() {
202: ISaveablesLifecycleListener siteSaveablesLifecycleListener = (ISaveablesLifecycleListener) getSite()
203: .getService(ISaveablesLifecycleListener.class);
204:
205: public void handleLifecycleEvent(
206: SaveablesLifecycleEvent event) {
207: if (event.getEventType() == SaveablesLifecycleEvent.DIRTY_CHANGED) {
208: firePropertyChange(PROP_DIRTY);
209: }
210: siteSaveablesLifecycleListener
211: .handleLifecycleEvent(event);
212: }
213: };
214: commonViewer.getNavigatorContentService().getSaveablesService()
215: .init(this , getCommonViewer(),
216: saveablesLifecycleListener);
217:
218: commonViewer
219: .addSelectionChangedListener(new ISelectionChangedListener() {
220:
221: public void selectionChanged(
222: SelectionChangedEvent event) {
223: firePropertyChange(PROP_DIRTY);
224: }
225: });
226:
227: PlatformUI.getWorkbench().getHelpSystem().setHelp(
228: commonViewer.getControl(), HELP_CONTEXT);
229: }
230:
231: /**
232: * <p>
233: * Note: This method is for internal use only. Clients should not call this
234: * method.
235: * </p>
236: * <p>
237: * This method will be invoked when the DisposeListener is notified of the
238: * disposal of the Eclipse view part.
239: * </p>
240: *
241: * @see org.eclipse.ui.part.WorkbenchPart#dispose()
242: */
243: public void dispose() {
244: if (commonManager != null) {
245: commonManager.dispose();
246: }
247: if (commonActionGroup != null) {
248: commonActionGroup.dispose();
249: }
250: super .dispose();
251: }
252:
253: /**
254: * <p>
255: * Note: This method is for internal use only. Clients should not call this
256: * method.
257: * </p>
258: *
259: * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite,
260: * org.eclipse.ui.IMemento)
261: */
262: public void init(IViewSite aSite, IMemento aMemento)
263: throws PartInitException {
264: super .init(aSite, aMemento);
265: memento = aMemento;
266: if (memento != null) {
267: Integer linkingEnabledInteger = memento
268: .getInteger(LINKING_ENABLED);
269: setLinkingEnabled(((linkingEnabledInteger != null) ? linkingEnabledInteger
270: .intValue() == 1
271: : false));
272: }
273:
274: }
275:
276: /**
277: *
278: * <p>
279: * Note: This method is for internal use only. Clients should not call this
280: * method.
281: * </p>
282: *
283: * @see org.eclipse.ui.part.ViewPart#saveState(org.eclipse.ui.IMemento)
284: */
285: public void saveState(IMemento aMemento) {
286: aMemento
287: .putInteger(LINKING_ENABLED, (isLinkingEnabled) ? 1 : 0);
288: super .saveState(aMemento);
289: commonManager.saveState(aMemento);
290: commonViewer.getNavigatorContentService().saveState(aMemento);
291: }
292:
293: /**
294: * <p>
295: * Force the workbench to focus on the Common Navigator tree.
296: * </p>
297: *
298: * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
299: */
300: public void setFocus() {
301: if (commonViewer != null) {
302: commonViewer.getTree().setFocus();
303: }
304: }
305:
306: /**
307: * <p>
308: * Set the selection to the Common Navigator tree, and expand nodes if
309: * necessary. Use caution when invoking this method as it can cause
310: * Navigator Content Extensions to load, thus causing plugin activation.
311: * </p>
312: *
313: * @see org.eclipse.ui.part.ISetSelectionTarget#selectReveal(org.eclipse.jface.viewers.ISelection)
314: */
315: public void selectReveal(ISelection selection) {
316: if (commonViewer != null) {
317: if (selection instanceof IStructuredSelection) {
318: Object[] newSelection = ((IStructuredSelection) selection)
319: .toArray();
320: Object[] expandedElements = commonViewer
321: .getExpandedElements();
322: Object[] newExpandedElements = new Object[newSelection.length
323: + expandedElements.length];
324: System
325: .arraycopy(expandedElements, 0,
326: newExpandedElements, 0,
327: expandedElements.length);
328: System.arraycopy(newSelection, 0, newExpandedElements,
329: expandedElements.length, newSelection.length);
330: commonViewer.setExpandedElements(newExpandedElements);
331: }
332: commonViewer.setSelection(selection, true);
333: }
334: }
335:
336: /**
337: * <p>
338: * Linking is handled by an action which listens for
339: * changes to the {@link CommonNavigator#IS_LINKING_ENABLED_PROPERTY}.
340: * Custom implementations that wish to override this functionality, need to
341: * override the action used by the default ActionGroup and listen for
342: * changes to the above property.
343: *
344: * @param toEnableLinking
345: * True enables linking the current selection with open editors
346: */
347: public final void setLinkingEnabled(boolean toEnableLinking) {
348: isLinkingEnabled = toEnableLinking;
349: firePropertyChange(IS_LINKING_ENABLED_PROPERTY);
350: }
351:
352: /**
353: * @return Whether linking the current selection with open editors is
354: * enabled.
355: */
356: public final boolean isLinkingEnabled() {
357: return isLinkingEnabled;
358: }
359:
360: /**
361: * <p>
362: * Provides access to the commonViewer used by the current CommonNavigator.
363: * The field will not be valid until after
364: * {@link #init(IViewSite, IMemento)} has been called by the
365: * Workbench.
366: * </p>
367: *
368: * @return The (already created) instance of Common Viewer.
369: */
370: public CommonViewer getCommonViewer() {
371: return commonViewer;
372: }
373:
374: /**
375: * @return The Navigator Content Service which populates this instance of
376: * Common Navigator
377: */
378: public INavigatorContentService getNavigatorContentService() {
379: return getCommonViewer().getNavigatorContentService();
380: }
381:
382: /**
383: * Returns an object which is an instance of the given class
384: * associated with this object. Returns <code>null</code> if
385: * no such object can be found.
386: *
387: * @param adapter the adapter class to look up
388: * @return a object castable to the given class,
389: * or <code>null</code> if this object does not
390: * have an adapter for the given class
391: */
392: public Object getAdapter(Class adapter) {
393: if (adapter == COMMON_VIEWER_CLASS) {
394: return getCommonViewer();
395: } else if (adapter == INAVIGATOR_CONTENT_SERVICE) {
396: return getCommonViewer().getNavigatorContentService();
397: } else if (adapter == ISHOW_IN_TARGET_CLASS) {
398: return this ;
399: }
400: return super .getAdapter(adapter);
401: }
402:
403: /**
404: * @return The Navigator Content Service which populates this instance of
405: * Common Navigator
406: */
407: public NavigatorActionService getNavigatorActionService() {
408: return commonManager.getNavigatorActionService();
409: }
410:
411: /**
412: * <p>
413: * Constructs and returns an instance of {@link CommonViewer}. The ID of
414: * the Eclipse view part will be used to create the viewer. The ID is
415: * important as some extensions indicate they should only be used with a
416: * particular viewer ID.
417: * <p>
418: *
419: * @param aParent
420: * A composite parent to contain the Common Viewer
421: * @return An initialized instance of CommonViewer
422: */
423: protected CommonViewer createCommonViewer(Composite aParent) {
424: CommonViewer aViewer = new CommonViewer(getViewSite().getId(),
425: aParent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
426: initListeners(aViewer);
427: aViewer.getNavigatorContentService().restoreState(memento);
428: return aViewer;
429: }
430:
431: /**
432: * <p>
433: * Adds the listeners to the Common Viewer.
434: * </p>
435: *
436: * @param viewer
437: * The viewer
438: * @since 2.0
439: */
440: protected void initListeners(TreeViewer viewer) {
441:
442: viewer.addDoubleClickListener(new IDoubleClickListener() {
443:
444: public void doubleClick(DoubleClickEvent event) {
445: try {
446: handleDoubleClick(event);
447: } catch (RuntimeException re) {
448: re.printStackTrace();
449: }
450: }
451: });
452: }
453:
454: /**
455: * <p>
456: * Note: This method is for internal use only. Clients should not call this
457: * method.
458: * </p>
459: *
460: * @param anEvent
461: * Supplied by the DoubleClick listener.
462: */
463: protected void handleDoubleClick(DoubleClickEvent anEvent) {
464:
465: IAction openHandler = getViewSite().getActionBars()
466: .getGlobalActionHandler(ICommonActionConstants.OPEN);
467:
468: if (openHandler == null) {
469: IStructuredSelection selection = (IStructuredSelection) anEvent
470: .getSelection();
471: Object element = selection.getFirstElement();
472:
473: TreeViewer viewer = getCommonViewer();
474: if (viewer.isExpandable(element)) {
475: viewer.setExpandedState(element, !viewer
476: .getExpandedState(element));
477: }
478: }
479: }
480:
481: /**
482: * <p>
483: * The Common Navigator Manager handles the setup of the Common Navigator
484: * Menu, manages updates to the ActionBars from
485: * {@link CommonActionProvider} extensions as the user's selection
486: * changes, and also updates the status bar based on the current selection.
487: *
488: * @return The Common Navigator Manager class which handles menu population
489: * and ActionBars
490: */
491: protected CommonNavigatorManager createCommonManager() {
492: return new CommonNavigatorManager(this , memento);
493: }
494:
495: /**
496: * <p>
497: * The ActionGroup is used to populate the ActionBars of Common Navigator
498: * View Part, and the returned implementation will have an opportunity to
499: * fill the ActionBars of the view as soon as it is created. ({@link ActionGroup#fillActionBars(org.eclipse.ui.IActionBars)}.
500: * </p>
501: * <p>
502: * The default implementation returns an action group which will add the
503: * following actions:
504: * <ul>
505: * <li>
506: * <p>
507: * Link with editor support. Allows the user to toggling linking the current
508: * selection with the active editors.
509: * </p>
510: * <li>
511: * <p>
512: * Collapse all. Collapses all expanded nodes.
513: * </p>
514: * <li>
515: * <p>
516: * Select Filters. Provides access to the "Select Filters" dialog that
517: * allows users to enable/disable filters and also the Content Extension
518: * activations.
519: * </p>
520: * </ul>
521: *
522: * @return The Action Group to be associated with the Common Navigator View
523: * Part.
524: */
525: protected ActionGroup createCommonActionGroup() {
526: return new CommonNavigatorActionGroup(this , commonViewer,
527: getLinkHelperService());
528: }
529:
530: /**
531: * @return The initial input for the viewer. Defaults to
532: * getSite().getPage().getInput()
533: */
534: protected IAdaptable getInitialInput() {
535: return getSite().getPage().getInput();
536: }
537:
538: /**
539: * <p>
540: * Updates the title text and title tool tip. Called whenever the input of
541: * the viewer changes.
542: * </p>
543: */
544: protected void updateTitle() {
545:
546: if (commonViewer == null) {
547: return;
548: }
549:
550: Object input = commonViewer.getInput();
551: String viewName = getConfigurationElement()
552: .getAttribute("name"); //$NON-NLS-1$
553: // IWorkingSet workingSet = workingSetFilter.getWorkingSet();
554:
555: if (input == null) {
556: setPartName(viewName);
557: setTitleToolTip(""); //$NON-NLS-1$
558: } else {
559: String inputToolTip = getFrameToolTipText(input);
560:
561: setPartName(viewName);
562: setTitleToolTip(inputToolTip);
563: }
564: }
565:
566: /**
567: * <p>
568: * Returns the tool tip text for the given element. Used as the tool tip
569: * text for the current frame, and for the view title tooltip.
570: * </p>
571: */
572: protected String getFrameToolTipText(Object anElement) {
573: if (commonViewer != null) {
574: return ((ILabelProvider) commonViewer.getLabelProvider())
575: .getText(anElement);
576: }
577: return ""; //$NON-NLS-1$
578: }
579:
580: /* (non-Javadoc)
581: * @see org.eclipse.ui.ISaveablesSource#getSaveables()
582: */
583: public Saveable[] getSaveables() {
584: return getNavigatorContentService().getSaveablesService()
585: .getSaveables();
586: }
587:
588: /* (non-Javadoc)
589: * @see org.eclipse.ui.ISaveablesSource#getActiveSaveables()
590: */
591: public Saveable[] getActiveSaveables() {
592: return getNavigatorContentService().getSaveablesService()
593: .getActiveSaveables();
594: }
595:
596: /* (non-Javadoc)
597: * @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
598: */
599: public void doSave(IProgressMonitor monitor) {
600: // Ignore. This method is not called because CommonNavigator implements
601: // ISaveablesSource. All saves will go through the ISaveablesSource /
602: // Saveable protocol.
603: }
604:
605: /* (non-Javadoc)
606: * @see org.eclipse.ui.ISaveablePart#doSaveAs()
607: */
608: public void doSaveAs() {
609: // ignore
610: }
611:
612: /* (non-Javadoc)
613: * @see org.eclipse.ui.ISaveablePart#isDirty()
614: */
615: public boolean isDirty() {
616: Saveable[] saveables = getSaveables();
617: for (int i = 0; i < saveables.length; i++) {
618: if (saveables[i].isDirty()) {
619: return true;
620: }
621: }
622: return false;
623: }
624:
625: /* (non-Javadoc)
626: * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
627: */
628: public boolean isSaveAsAllowed() {
629: return false;
630: }
631:
632: /* (non-Javadoc)
633: * @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
634: */
635: public boolean isSaveOnCloseNeeded() {
636: return isDirty();
637: }
638:
639: /* (non-Javadoc)
640: * @see org.eclipse.ui.part.IShowInTarget#show(org.eclipse.ui.part.ShowInContext)
641: */
642: public boolean show(ShowInContext context) {
643: IStructuredSelection selection = getSelection(context);
644: if (selection != null && !selection.isEmpty()) {
645: selectReveal(selection);
646: return true;
647: }
648: return false;
649: }
650:
651: private IStructuredSelection getSelection(ShowInContext context) {
652: if (context == null)
653: return StructuredSelection.EMPTY;
654: ISelection selection = context.getSelection();
655: if (selection != null && !selection.isEmpty()
656: && selection instanceof IStructuredSelection)
657: return (IStructuredSelection) selection;
658: Object input = context.getInput();
659: if (input instanceof IEditorInput) {
660: LinkHelperService lhs = getLinkHelperService();
661: return lhs.getSelectionFor((IEditorInput) input);
662: }
663: if (input != null) {
664: return new StructuredSelection(input);
665: }
666: return StructuredSelection.EMPTY;
667: }
668:
669: private synchronized LinkHelperService getLinkHelperService() {
670: if (linkService == null)
671: linkService = new LinkHelperService(
672: (NavigatorContentService) getCommonViewer()
673: .getNavigatorContentService());
674: return linkService;
675: }
676:
677: }
|