content: /*******************************************************************************
   * Copyright (c) 2000, 2008 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *   Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation
   *             (report 36180: Callers/Callees view)
   *   Michael Fraenkel (fraenkel@us.ibm.com) - patch
   *             (report 60714: Call Hierarchy: display search scope in view title)
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.ui.callhierarchy;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.eclipse.help.IContextProvider;
  import org.eclipse.jface.action.IMenuListener;
  import org.eclipse.jface.action.IMenuManager;
  import org.eclipse.jface.action.IStatusLineManager;
  import org.eclipse.jface.action.IToolBarManager;
  import org.eclipse.jface.action.MenuManager;
  import org.eclipse.jface.action.Separator;
  import org.eclipse.jface.dialogs.IDialogSettings;
  import org.eclipse.jface.util.TransferDragSourceListener;
  import org.eclipse.jface.util.TransferDropTargetListener;
  import org.eclipse.jface.viewers.IOpenListener;
  import org.eclipse.jface.viewers.ISelection;
  import org.eclipse.jface.viewers.ISelectionChangedListener;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.jface.viewers.OpenEvent;
  import org.eclipse.jface.viewers.SelectionChangedEvent;
  import org.eclipse.jface.viewers.StructuredSelection;
  import org.eclipse.jface.viewers.StructuredViewer;
  import org.eclipse.jface.viewers.Viewer;
  import org.eclipse.jface.viewers.ViewerComparator;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.custom.SashForm;
  import org.eclipse.swt.dnd.Clipboard;
  import org.eclipse.swt.dnd.DND;
  import org.eclipse.swt.dnd.DropTarget;
  import org.eclipse.swt.dnd.Transfer;
  import org.eclipse.swt.events.ControlEvent;
  import org.eclipse.swt.events.ControlListener;
  import org.eclipse.swt.events.KeyAdapter;
  import org.eclipse.swt.events.KeyEvent;
  import org.eclipse.swt.events.KeyListener;
  import org.eclipse.swt.graphics.Point;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Label;
  import org.eclipse.ui.IActionBars;
  import org.eclipse.ui.IEditorPart;
  import org.eclipse.ui.IMemento;
  import org.eclipse.ui.IPageLayout;
  import org.eclipse.ui.IPartListener2;
  import org.eclipse.ui.IViewSite;
  import org.eclipse.ui.IWorkbenchPage;
  import org.eclipse.ui.IWorkbenchPartReference;
  import org.eclipse.ui.IWorkbenchPartSite;
  import org.eclipse.ui.PartInitException;
  import org.eclipse.ui.PlatformUI;
  import org.eclipse.ui.actions.ActionContext;
  import org.eclipse.ui.actions.ActionGroup;
  import org.eclipse.ui.part.IShowInTargetList;
  import org.eclipse.ui.part.PageBook;
  import org.eclipse.ui.part.ResourceTransfer;
  import org.eclipse.ui.part.ViewPart;
  import org.eclipse.ui.texteditor.ITextEditor;
  import org.eclipse.ui.views.navigator.LocalSelectionTransfer;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchScope;
  import org.eclipse.wst.jsdt.internal.corext.callhierarchy.CallHierarchy;
  import org.eclipse.wst.jsdt.internal.corext.callhierarchy.CallLocation;
  import org.eclipse.wst.jsdt.internal.corext.callhierarchy.MethodWrapper;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.IJavaHelpContextIds;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.actions.CompositeActionGroup;
  import org.eclipse.wst.jsdt.internal.ui.dnd.DelegatingDropAdapter;
  import org.eclipse.wst.jsdt.internal.ui.dnd.JdtViewerDragAdapter;
  import org.eclipse.wst.jsdt.internal.ui.dnd.ResourceTransferDragAdapter;
  import org.eclipse.wst.jsdt.internal.ui.javaeditor.EditorUtility;
  import org.eclipse.wst.jsdt.internal.ui.packageview.SelectionTransferDragAdapter;
  import org.eclipse.wst.jsdt.internal.ui.util.JavaUIHelp;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.SelectionProviderMediator;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.StatusBarUpdater;
  import org.eclipse.wst.jsdt.ui.IContextMenuConstants;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  import org.eclipse.wst.jsdt.ui.JavaScriptUI;
  import org.eclipse.wst.jsdt.ui.actions.CCPActionGroup;
  import org.eclipse.wst.jsdt.ui.actions.GenerateActionGroup;
  import org.eclipse.wst.jsdt.ui.actions.JavaSearchActionGroup;
  import org.eclipse.wst.jsdt.ui.actions.OpenEditorActionGroup;
  import org.eclipse.wst.jsdt.ui.actions.OpenViewActionGroup;
  import org.eclipse.wst.jsdt.ui.actions.RefactorActionGroup;
  
  /**
   * This is the main view for the callers plugin. It builds a tree of callers/callees
   * and allows the user to double click an entry to go to the selected method.
   *
   */
  public class CallHierarchyViewPart extends ViewPart implements ICallHierarchyViewPart,
      ISelectionChangedListener {
  	
  	private class CallHierarchySelectionProvider extends SelectionProviderMediator {
  		
  		public CallHierarchySelectionProvider(StructuredViewer[] viewers) {
  			super(viewers, null);
  		}
  
  		/* (non-Javadoc)
  		 * @see org.eclipse.wst.jsdt.internal.ui.typehierarchy.SelectionProviderMediator#getSelection()
  		 */
  		public ISelection getSelection() {
  			ISelection selection= super.getSelection();
  			if (!selection.isEmpty()) {
  				return CallHierarchyUI.convertSelection(selection);
  			}
  			return selection;
  		}
  	}
  	
      private static final String DIALOGSTORE_VIEWORIENTATION = "CallHierarchyViewPart.orientation"; //$NON-NLS-1$
      private static final String DIALOGSTORE_CALL_MODE = "CallHierarchyViewPart.call_mode"; //$NON-NLS-1$
  	/**
  	 * The key to be used is <code>DIALOGSTORE_RATIO + fCurrentOrientation</code>.
  	 */
  	private static final String DIALOGSTORE_RATIO= "CallHierarchyViewPart.ratio"; //$NON-NLS-1$
  	
      static final int VIEW_ORIENTATION_VERTICAL = 0;
      static final int VIEW_ORIENTATION_HORIZONTAL = 1;
      static final int VIEW_ORIENTATION_SINGLE = 2;
      static final int VIEW_ORIENTATION_AUTOMATIC = 3;
      static final int CALL_MODE_CALLERS = 0;
      static final int CALL_MODE_CALLEES = 1;
      static final String GROUP_SEARCH_SCOPE = "MENU_SEARCH_SCOPE"; //$NON-NLS-1$
  	static final String ID_CALL_HIERARCHY = "org.eclipse.wst.jsdt.callhierarchy.view"; //$NON-NLS-1$
  	private static final String GROUP_FOCUS = "group.focus"; //$NON-NLS-1$
      private static final int PAGE_EMPTY = 0;
      private static final int PAGE_VIEWER = 1;
      private Label fNoHierarchyShownLabel;
      private PageBook fPagebook;
      private IDialogSettings fDialogSettings;
      private int fCurrentOrientation;
      int fOrientation= VIEW_ORIENTATION_AUTOMATIC;
      private int fCurrentCallMode;
      private MethodWrapper fCalleeRoot;
      private MethodWrapper fCallerRoot;
      private IMemento fMemento;
      private IFunction fShownMethod;
      private CallHierarchySelectionProvider fSelectionProviderMediator;
      private List fMethodHistory;
      private LocationViewer fLocationViewer;
      private SashForm fHierarchyLocationSplitter;
      private Clipboard fClipboard;
      private SearchScopeActionGroup fSearchScopeActions;
      private ToggleOrientationAction[] fToggleOrientationActions;
      private ToggleCallModeAction[] fToggleCallModeActions;
      private CallHierarchyFiltersActionGroup fFiltersActionGroup;
      private HistoryDropDownAction fHistoryDropDownAction;
      private RefreshAction fRefreshAction;
      private OpenLocationAction fOpenLocationAction;
  	private LocationCopyAction fLocationCopyAction;
      private FocusOnSelectionAction fFocusOnSelectionAction;
      private CopyCallHierarchyAction fCopyAction;
      private CancelSearchAction fCancelSearchAction;
      private CompositeActionGroup fActionGroups;
      private CallHierarchyViewer fCallHierarchyViewer;
      private boolean fShowCallDetails;
  	protected Composite fParent;
  	private IPartListener2 fPartListener;
  
      public CallHierarchyViewPart() {
          super();
  
          fDialogSettings = JavaScriptPlugin.getDefault().getDialogSettings();
  
          fMethodHistory = new ArrayList();
      }
  
      public void setFocus() {
          fPagebook.setFocus();
      }
  
      /**
       * Sets the history entries
       */
      public void setHistoryEntries(IFunction[] elems) {
          fMethodHistory.clear();
  
          for (int i = 0; i < elems.length; i++) {
              fMethodHistory.add(elems[i]);
          }
  
          updateHistoryEntries();
      }
  
      /**
       * Gets all history entries.
       */
      public IFunction[] getHistoryEntries() {
          if (fMethodHistory.size() > 0) {
              updateHistoryEntries();
          }
  
          return (IFunction[]) fMethodHistory.toArray(new IFunction[fMethodHistory.size()]);
      }
  
      /**
       * Method setMethod.
       * @param method
       */
      public void setMethod(IFunction method) {
          if (method == null) {
              showPage(PAGE_EMPTY);
  
              return;
          }
          if (! method.equals(fShownMethod)) {
              addHistoryEntry(method);
          }
  
          this.fShownMethod = method;
  
          refresh();
      }
  
      public IFunction getMethod() {
          return fShownMethod;
      }
  
      public MethodWrapper getCurrentMethodWrapper() {
          if (fCurrentCallMode == CALL_MODE_CALLERS) {
              return fCallerRoot;
          } else {
              return fCalleeRoot;
          }
      }
             
      /**
       * called from ToggleOrientationAction.
       * @param orientation VIEW_ORIENTATION_HORIZONTAL or VIEW_ORIENTATION_VERTICAL
       */
      void setOrientation(int orientation) {
          if (fCurrentOrientation != orientation) {
              if ((fLocationViewer != null) && !fLocationViewer.getControl().isDisposed() &&
                          (fHierarchyLocationSplitter != null) &&
                          !fHierarchyLocationSplitter.isDisposed()) {
                  if (orientation == VIEW_ORIENTATION_SINGLE) {
                      setShowCallDetails(false);
                  } else {
                      if (fCurrentOrientation == VIEW_ORIENTATION_SINGLE) {
                          setShowCallDetails(true);
                      }
  
                      boolean horizontal = orientation == VIEW_ORIENTATION_HORIZONTAL;
                      fHierarchyLocationSplitter.setOrientation(horizontal ? SWT.HORIZONTAL
                                                                           : SWT.VERTICAL);
                  }
  
                  fHierarchyLocationSplitter.layout();
              }
  
              updateCheckedState();
  
              fCurrentOrientation = orientation;
  			
  			restoreSplitterRatio();
          }
      }
  
  	private void updateCheckedState() {
  		for (int i= 0; i < fToggleOrientationActions.length; i++) {
  			fToggleOrientationActions[i].setChecked(fOrientation == fToggleOrientationActions[i].getOrientation());
  		}
  	}
  
      /**
       * called from ToggleCallModeAction.
       * @param mode CALL_MODE_CALLERS or CALL_MODE_CALLEES
       */
      void setCallMode(int mode) {
          if (fCurrentCallMode != mode) {
              for (int i = 0; i < fToggleCallModeActions.length; i++) {
                  fToggleCallModeActions[i].setChecked(mode == fToggleCallModeActions[i].getMode());
              }
  
              fCurrentCallMode = mode;
              fDialogSettings.put(DIALOGSTORE_CALL_MODE, mode);
  
              updateView();
          }
      }
  
      public IJavaScriptSearchScope getSearchScope() {
          return fSearchScopeActions.getSearchScope();
      }
  
      public void setShowCallDetails(boolean show) {
          fShowCallDetails = show;
          showOrHideCallDetailsView();
      }
  
      private void initDragAndDrop() {
          addDragAdapters(fCallHierarchyViewer);
          addDropAdapters(fCallHierarchyViewer);
          addDropAdapters(fLocationViewer);
  
          //dnd on empty hierarchy
          DropTarget dropTarget = new DropTarget(fPagebook, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT);
          dropTarget.setTransfer(new Transfer[] { LocalSelectionTransfer.getInstance() });
          dropTarget.addDropListener(new CallHierarchyTransferDropAdapter(this, fCallHierarchyViewer));
      }
          
  	private void addDropAdapters(StructuredViewer viewer) {
  		Transfer[] transfers= new Transfer[] { LocalSelectionTransfer.getInstance() };
  		int ops= DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK | DND.DROP_DEFAULT;
  		
  		TransferDropTargetListener[] dropListeners= new TransferDropTargetListener[] {
  			new CallHierarchyTransferDropAdapter(this, viewer)
  		};
  		viewer.addDropSupport(ops, transfers, new DelegatingDropAdapter(dropListeners));
  	}
  
  	private void addDragAdapters(StructuredViewer viewer) {
  		int ops= DND.DROP_COPY | DND.DROP_LINK;
  		Transfer[] transfers= new Transfer[] { LocalSelectionTransfer.getInstance(), ResourceTransfer.getInstance()};
  
  		TransferDragSourceListener[] dragListeners= new TransferDragSourceListener[] {
  			new SelectionTransferDragAdapter(viewer),
  			new ResourceTransferDragAdapter(viewer)
  		};
  		viewer.addDragSupport(ops, transfers, new JdtViewerDragAdapter(viewer, dragListeners));
  	}	
              
      public void createPartControl(Composite parent) {
      	fParent= parent;
      	addResizeListener(parent);
          fPagebook = new PageBook(parent, SWT.NONE);
  
          // Page 1: Viewers
          createHierarchyLocationSplitter(fPagebook);
          createCallHierarchyViewer(fHierarchyLocationSplitter);
          createLocationViewer(fHierarchyLocationSplitter);
  
          // Page 2: Nothing selected
          fNoHierarchyShownLabel = new Label(fPagebook, SWT.TOP + SWT.LEFT + SWT.WRAP);
          fNoHierarchyShownLabel.setText(CallHierarchyMessages.CallHierarchyViewPart_empty); //   
  
  		initDragAndDrop();
  
          showPage(PAGE_EMPTY);
          
          PlatformUI.getWorkbench().getHelpSystem().setHelp(fPagebook, IJavaHelpContextIds.CALL_HIERARCHY_VIEW);
          
          fSelectionProviderMediator = new CallHierarchySelectionProvider(new StructuredViewer[] {
                      fCallHierarchyViewer, fLocationViewer
                  });
  
          IStatusLineManager slManager = getViewSite().getActionBars().getStatusLineManager();
          fSelectionProviderMediator.addSelectionChangedListener(new StatusBarUpdater(slManager));
          getSite().setSelectionProvider(fSelectionProviderMediator);
  
          fCallHierarchyViewer.initContextMenu(
          		new IMenuListener() {
  		            public void menuAboutToShow(IMenuManager menu) {
  		                fillCallHierarchyViewerContextMenu(menu);
  		            }
  		        }, getSite(), fSelectionProviderMediator);
  
          
          fClipboard= new Clipboard(parent.getDisplay());
          
          makeActions();
          fillViewMenu();
          fillActionBars();
  
          initOrientation();
          initCallMode();
  
          if (fMemento != null) {
              restoreState(fMemento);
          }
  		restoreSplitterRatio();
  		addPartListener();
     }
  
  	private void restoreSplitterRatio() {
  		String ratio= fDialogSettings.get(DIALOGSTORE_RATIO + fCurrentOrientation);
  		if (ratio == null)
  			return;
  		int intRatio= Integer.parseInt(ratio);
          fHierarchyLocationSplitter.setWeights(new int[] {intRatio, 1000 - intRatio});
  	}
  
  	private void saveSplitterRatio() {
  		if (fHierarchyLocationSplitter != null && ! fHierarchyLocationSplitter.isDisposed()) {
  	        int[] weigths = fHierarchyLocationSplitter.getWeights();
  	        int ratio = (weigths[0] * 1000) / (weigths[0] + weigths[1]);
  			String key= DIALOGSTORE_RATIO + fCurrentOrientation;
  	        fDialogSettings.put(key, ratio);
  		}
  	}
  
  	private void addPartListener() {
  		fPartListener= new IPartListener2() {
  					public void partActivated(IWorkbenchPartReference partRef) { }
  					public void partBroughtToTop(IWorkbenchPartReference partRef) { }
  					public void partClosed(IWorkbenchPartReference partRef) {
  						if (ID_CALL_HIERARCHY.equals(partRef.getId()))
  							saveViewSettings();
  					}
  					public void partDeactivated(IWorkbenchPartReference partRef) {
  						if (ID_CALL_HIERARCHY.equals(partRef.getId()))
  							saveViewSettings();
  					}
  					public void partOpened(IWorkbenchPartReference partRef) { }
  					public void partHidden(IWorkbenchPartReference partRef) { }
  					public void partVisible(IWorkbenchPartReference partRef) { }
  					public void partInputChanged(IWorkbenchPartReference partRef) { }
  				};
  		getViewSite().getPage().addPartListener(fPartListener);
  	}
  
  	protected void saveViewSettings() {
  		saveSplitterRatio();
  		fDialogSettings.put(DIALOGSTORE_VIEWORIENTATION, fOrientation);
  	}
  
  	private void addResizeListener(Composite parent) {
  		parent.addControlListener(new ControlListener() {
  			public void controlMoved(ControlEvent e) {
  			}
  			public void controlResized(ControlEvent e) {
  				computeOrientation();
  			}
  		});
  	}
  
  	void computeOrientation() {
  		saveSplitterRatio();
  		fDialogSettings.put(DIALOGSTORE_VIEWORIENTATION, fOrientation);
  		if (fOrientation != VIEW_ORIENTATION_AUTOMATIC) {
  			setOrientation(fOrientation);
  		}
  		else {
  			if (fOrientation == VIEW_ORIENTATION_SINGLE)
  				return;
  			Point size= fParent.getSize();
  			if (size.x != 0 && size.y != 0) {
  				if (size.x > size.y) 
  					setOrientation(VIEW_ORIENTATION_HORIZONTAL);
  				else 
  					setOrientation(VIEW_ORIENTATION_VERTICAL);
  			}
  		}
  	}
  
      private void showPage(int page) {
          if (page == PAGE_EMPTY) {
              fPagebook.showPage(fNoHierarchyShownLabel);
          } else {
              fPagebook.showPage(fHierarchyLocationSplitter);
          }
      }
  
      /**
       * Restores the type hierarchy settings from a memento.
       */
      private void restoreState(IMemento memento) {
          fSearchScopeActions.restoreState(memento);
      }
  
      private void initCallMode() {
          int mode;
  
          try {
              mode = fDialogSettings.getInt(DIALOGSTORE_CALL_MODE);
  
              if ((mode < 0) || (mode > 1)) {
                  mode = CALL_MODE_CALLERS;
              }
          } catch (NumberFormatException e) {
              mode = CALL_MODE_CALLERS;
          }
  
          // force the update
          fCurrentCallMode = -1;
  
          // will fill the main tool bar
          setCallMode(mode);
      }
  
      private void initOrientation() {
  
          try {
              fOrientation = fDialogSettings.getInt(DIALOGSTORE_VIEWORIENTATION);
  
              if ((fOrientation < 0) || (fOrientation > 3)) {
              	fOrientation = VIEW_ORIENTATION_AUTOMATIC;
              }
          } catch (NumberFormatException e) {
          	fOrientation = VIEW_ORIENTATION_AUTOMATIC;
          }
  
          // force the update
          fCurrentOrientation = -1;
          setOrientation(fOrientation);
      }
  
      private void fillViewMenu() {
          IActionBars actionBars = getViewSite().getActionBars();
          IMenuManager viewMenu = actionBars.getMenuManager();
          viewMenu.add(new Separator());
  
          for (int i = 0; i < fToggleCallModeActions.length; i++) {
              viewMenu.add(fToggleCallModeActions[i]);
          }
  
          viewMenu.add(new Separator());
          
          MenuManager layoutSubMenu= new MenuManager(CallHierarchyMessages.CallHierarchyViewPart_layout_menu);
          for (int i = 0; i < fToggleOrientationActions.length; i++) {
          	layoutSubMenu.add(fToggleOrientationActions[i]);
          }
          viewMenu.add(layoutSubMenu);
      }
  
      /**
       *
       */
      public void dispose() {
          if (fActionGroups != null)
              fActionGroups.dispose();
  		
  		if (fClipboard != null)
  	        fClipboard.dispose();
  		
  		if (fPartListener != null) {
  			getViewSite().getPage().removePartListener(fPartListener);
  			fPartListener= null;
  		}
  
          super.dispose();
      }
  
      /**
       * Goes to the selected entry, without updating the order of history entries.
       */
      public void gotoHistoryEntry(IFunction entry) {
          if (fMethodHistory.contains(entry)) {
              setMethod(entry);
          }
      }
  
      /* (non-Javadoc)
       * Method declared on IViewPart.
       */
      public void init(IViewSite site, IMemento memento)
          throws PartInitException {
          super.init(site, memento);
          fMemento = memento;
      }
  
      /**
       *
       */
      public void refresh() {
          setCalleeRoot(null);
          setCallerRoot(null);
  
          updateView();
      }
  
      public void saveState(IMemento memento) {
          if (fPagebook == null) {
              // part has not been created
              if (fMemento != null) { //Keep the old state;
                  memento.putMemento(fMemento);
              }
  
              return;
          }
  
          fSearchScopeActions.saveState(memento);
      }
  
      public void selectionChanged(SelectionChangedEvent e) {
          if (e.getSelectionProvider() == fCallHierarchyViewer) {
              methodSelectionChanged(e.getSelection());
          }
      }
  
      /**
       * @param selection
       */
      private void methodSelectionChanged(ISelection selection) {
          if (selection instanceof IStructuredSelection && ((IStructuredSelection) selection).size() == 1) {
              Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
  
              if (selectedElement instanceof MethodWrapper) {
                  MethodWrapper methodWrapper = (MethodWrapper) selectedElement;
  
                  revealElementInEditor(methodWrapper, fCallHierarchyViewer);
                  updateLocationsView(methodWrapper);
              } else {
                  updateLocationsView(null);
              }
          } else {
          	updateLocationsView(null);
          }
      }
  
      private void revealElementInEditor(Object elem, Viewer originViewer) {
          // only allow revealing when the type hierarchy is the active pagae
          // no revealing after selection events due to model changes
          if (getSite().getPage().getActivePart() != this) {
              return;
          }
  
          if (fSelectionProviderMediator.getViewerInFocus() != originViewer) {
              return;
          }
  
          if (elem instanceof MethodWrapper) {
              CallLocation callLocation = CallHierarchy.getCallLocation(elem);
  
              if (callLocation != null) {
                  IEditorPart editorPart = CallHierarchyUI.isOpenInEditor(callLocation);
  
                  if (editorPart != null) {
                      getSite().getPage().bringToTop(editorPart);
  
                      if (editorPart instanceof ITextEditor) {
                          ITextEditor editor = (ITextEditor) editorPart;
                          editor.selectAndReveal(callLocation.getStart(),
                              (callLocation.getEnd() - callLocation.getStart()));
                      }
                  }
              } else {
                  IEditorPart editorPart = CallHierarchyUI.isOpenInEditor(elem);
                  getSite().getPage().bringToTop(editorPart);
                  EditorUtility.revealInEditor(editorPart,
                      ((MethodWrapper) elem).getMember());
              }
          } else if (elem instanceof IJavaScriptElement) {
              IEditorPart editorPart = EditorUtility.isOpenInEditor(elem);
  
              if (editorPart != null) {
                  //            getSite().getPage().removePartListener(fPartListener);
                  getSite().getPage().bringToTop(editorPart);
                  EditorUtility.revealInEditor(editorPart, (IJavaScriptElement) elem);
  
                  //            getSite().getPage().addPartListener(fPartListener);
              }
          }
      }
      
      /**
  	 * {@inheritDoc}
  	 */
      public Object getAdapter(Class adapter) {
      	if (adapter == IContextProvider.class) {
      		return JavaUIHelp.getHelpContextProvider(this, IJavaHelpContextIds.CALL_HIERARCHY_VIEW);
      	}
  		if (adapter == IShowInTargetList.class) {
  			return new IShowInTargetList() {
  				public String[] getShowInTargetIds() {
  					return new String[] { JavaScriptUI.ID_PACKAGES, IPageLayout.ID_RES_NAV  };
  				}
  			};
  		}
      	return super.getAdapter(adapter);
      }
  
      /**
       * Returns the current selection.
       */
      protected ISelection getSelection() {
      	StructuredViewer viewerInFocus= fSelectionProviderMediator.getViewerInFocus();
  		if (viewerInFocus != null) {
  			return viewerInFocus.getSelection();
  		}
  		return StructuredSelection.EMPTY;
      }
  
      protected void fillLocationViewerContextMenu(IMenuManager menu) {
          JavaScriptPlugin.createStandardGroups(menu);
  
          menu.appendToGroup(IContextMenuConstants.GROUP_SHOW, fOpenLocationAction);
          menu.appendToGroup(IContextMenuConstants.GROUP_SHOW, fRefreshAction);
          menu.appendToGroup(IContextMenuConstants.GROUP_REORGANIZE, fLocationCopyAction);
      }
  
      protected void handleKeyEvent(KeyEvent event) {
          if (event.stateMask == 0) {
              if (event.keyCode == SWT.F5) {
                  if ((fRefreshAction != null) && fRefreshAction.isEnabled()) {
                      fRefreshAction.run();
  
                      return;
                  }
              }
          }
      }
  
      private IActionBars getActionBars() {
          return getViewSite().getActionBars();
      }
  
      private void setCalleeRoot(MethodWrapper calleeRoot) {
          this.fCalleeRoot = calleeRoot;
      }
  
      private MethodWrapper getCalleeRoot() {
          if (fCalleeRoot == null) {
              fCalleeRoot = CallHierarchy.getDefault().getCalleeRoot(fShownMethod);
          }
  
          return fCalleeRoot;
      }
  
      private void setCallerRoot(MethodWrapper callerRoot) {
          this.fCallerRoot = callerRoot;
      }
  
      private MethodWrapper getCallerRoot() {
          if (fCallerRoot == null) {
              fCallerRoot = CallHierarchy.getDefault().getCallerRoot(fShownMethod);
          }
  
          return fCallerRoot;
      }
  
      /**
       * Adds the entry if new. Inserted at the beginning of the history entries list.
       */
      private void addHistoryEntry(IJavaScriptElement entry) {
          if (fMethodHistory.contains(entry)) {
              fMethodHistory.remove(entry);
          }
  
          fMethodHistory.add(0, entry);
          fHistoryDropDownAction.setEnabled(!fMethodHistory.isEmpty());
      }
  
      /**
       * @param parent
       */
      private void createLocationViewer(Composite parent) {
          fLocationViewer= new LocationViewer(parent);
  
          fLocationViewer.getControl().addKeyListener(createKeyListener());
  
          fLocationViewer.initContextMenu(new IMenuListener() {
                  public void menuAboutToShow(IMenuManager menu) {
                      fillLocationViewerContextMenu(menu);
                  }
              }, ID_CALL_HIERARCHY, getSite());
      }
  
      private void createHierarchyLocationSplitter(Composite parent) {
          fHierarchyLocationSplitter = new SashForm(parent, SWT.NONE);
  
          fHierarchyLocationSplitter.addKeyListener(createKeyListener());
      }
  
      private void createCallHierarchyViewer(Composite parent) {
          fCallHierarchyViewer = new CallHierarchyViewer(parent, this);
  
          fCallHierarchyViewer.addKeyListener(createKeyListener());
          fCallHierarchyViewer.addSelectionChangedListener(this);
      }
  
      /**
       * @param menu
       */
      protected void fillCallHierarchyViewerContextMenu(IMenuManager menu) {
          JavaScriptPlugin.createStandardGroups(menu);
  
          menu.appendToGroup(IContextMenuConstants.GROUP_SHOW, fRefreshAction);
          menu.appendToGroup(IContextMenuConstants.GROUP_SHOW, new Separator(GROUP_FOCUS));
  
          if (fFocusOnSelectionAction.canActionBeAdded()) {
              menu.appendToGroup(GROUP_FOCUS, fFocusOnSelectionAction);
          }
          if (fCopyAction.canActionBeAdded()) {
          	menu.appendToGroup(GROUP_FOCUS, fCopyAction);
          }
  
          fActionGroups.setContext(new ActionContext(getSelection()));
          fActionGroups.fillContextMenu(menu);
          fActionGroups.setContext(null);
      }
  
      private void fillActionBars() {
          IActionBars actionBars = getActionBars();
          IToolBarManager toolBar = actionBars.getToolBarManager();
  
          fActionGroups.fillActionBars(actionBars);
          
          toolBar.add(fCancelSearchAction);
          for (int i = 0; i < fToggleCallModeActions.length; i++) {
              toolBar.add(fToggleCallModeActions[i]);
          }
          toolBar.add(fHistoryDropDownAction);
      }
  
      private KeyListener createKeyListener() {
          KeyListener keyListener = new KeyAdapter() {
                  public void keyReleased(KeyEvent event) {
                      handleKeyEvent(event);
                  }
              };
  
          return keyListener;
      }
  
      /**
       *
       */
      private void makeActions() {
          fRefreshAction = new RefreshAction(this);
  
          fOpenLocationAction = new OpenLocationAction(this, getSite());
          fLocationViewer.addOpenListener(new IOpenListener() {
                  public void open(OpenEvent event) {
                      fOpenLocationAction.run();
                  }
              });
          
          fLocationCopyAction= fLocationViewer.initCopyAction(getViewSite(), fClipboard);
          
          fFocusOnSelectionAction = new FocusOnSelectionAction(this);
          fCopyAction= new CopyCallHierarchyAction(this, fClipboard, fCallHierarchyViewer);
          fSearchScopeActions = new SearchScopeActionGroup(this, fDialogSettings);
          fFiltersActionGroup = new CallHierarchyFiltersActionGroup(this,
                  fCallHierarchyViewer);
          fHistoryDropDownAction = new HistoryDropDownAction(this);
          fHistoryDropDownAction.setEnabled(false);
          fCancelSearchAction = new CancelSearchAction(this);
          setCancelEnabled(false);
          fToggleOrientationActions = new ToggleOrientationAction[] {
                  new ToggleOrientationAction(this, VIEW_ORIENTATION_VERTICAL),
                  new ToggleOrientationAction(this, VIEW_ORIENTATION_HORIZONTAL),
                  new ToggleOrientationAction(this, VIEW_ORIENTATION_AUTOMATIC),
                  new ToggleOrientationAction(this, VIEW_ORIENTATION_SINGLE)
              };
          fToggleCallModeActions = new ToggleCallModeAction[] {
                  new ToggleCallModeAction(this, CALL_MODE_CALLERS),
                  new ToggleCallModeAction(this, CALL_MODE_CALLEES)
              };
          fActionGroups = new CompositeActionGroup(new ActionGroup[] {
                      new OpenEditorActionGroup(this), 
                      new OpenViewActionGroup(this),
                      new CCPActionGroup(this),
                      new GenerateActionGroup(this), 
                      new RefactorActionGroup(this),
                      new JavaSearchActionGroup(this),
                      fSearchScopeActions, fFiltersActionGroup
                  });
      }
  
      private void showOrHideCallDetailsView() {
          if (fShowCallDetails) {
              fHierarchyLocationSplitter.setMaximizedControl(null);
          } else {
              fHierarchyLocationSplitter.setMaximizedControl(fCallHierarchyViewer.getControl());
          }
      }
  
      private void updateLocationsView(MethodWrapper methodWrapper) {
          if (methodWrapper != null && methodWrapper.getMethodCall().hasCallLocations()) {
              fLocationViewer.setInput(methodWrapper.getMethodCall().getCallLocations());
          } else {
              fLocationViewer.clearViewer();
          }
      }
  
      private void updateHistoryEntries() {
          for (int i = fMethodHistory.size() - 1; i >= 0; i--) {
              IFunction method = (IFunction) fMethodHistory.get(i);
  
              if (!method.exists()) {
                  fMethodHistory.remove(i);
              }
          }
  
          fHistoryDropDownAction.setEnabled(!fMethodHistory.isEmpty());
      }
  
      /**
  	 * Method updateView.
  	 */
  	private void updateView() {
  		if ( (fShownMethod != null)) {
  			showPage(PAGE_VIEWER);
  
  			CallHierarchy.getDefault().setSearchScope(getSearchScope());
  
  			String elementName= JavaScriptElementLabels.getElementLabel(fShownMethod, JavaScriptElementLabels.ALL_DEFAULT);
  			String scopeDescription= fSearchScopeActions.getFullDescription();
  			String[] args= new String[] { elementName, scopeDescription };
  			// set input to null so that setSorter does not cause a refresh on the old contents:
  			fCallHierarchyViewer.setInput(null);
  			if (fCurrentCallMode == CALL_MODE_CALLERS) {
  				setContentDescription(Messages.format(CallHierarchyMessages.CallHierarchyViewPart_callsToMethod, args)); 
  				fCallHierarchyViewer.setComparator(new ViewerComparator()); // bug 111423: sort caller hierarchy alphabetically
      			fCallHierarchyViewer.setMethodWrapper(getCallerRoot());
  			} else {
  				setContentDescription(Messages.format(CallHierarchyMessages.CallHierarchyViewPart_callsFromMethod, args));
  				fCallHierarchyViewer.setComparator(null);
  				fCallHierarchyViewer.setMethodWrapper(getCalleeRoot());
  			}
  		}
      }
  
      static CallHierarchyViewPart findAndShowCallersView(IWorkbenchPartSite site) {
          IWorkbenchPage workbenchPage = site.getPage();
          CallHierarchyViewPart callersView = null;
  
          try {
              callersView = (CallHierarchyViewPart) workbenchPage.showView(CallHierarchyViewPart.ID_CALL_HIERARCHY);
          } catch (PartInitException e) {
              JavaScriptPlugin.log(e);
          }
  
          return callersView;
      }
  
      /**
       * Cancels the caller/callee search jobs that are currently running.  
       */
      void cancelJobs() {
          fCallHierarchyViewer.cancelJobs();
      }
  
      /**
       * Sets the enablement state of the cancel button.
       * @param enabled 
       */
      void setCancelEnabled(boolean enabled) {
          fCancelSearchAction.setEnabled(enabled);
      }
  }
  