content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  
  package org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.newsourcepage;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IFolder;
  import org.eclipse.core.resources.IResource;
  import org.eclipse.core.resources.IWorkspace;
  import org.eclipse.core.resources.IWorkspaceRunnable;
  import org.eclipse.core.resources.ResourcesPlugin;
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IProgressMonitor;
  import org.eclipse.core.runtime.NullProgressMonitor;
  import org.eclipse.jface.action.IMenuListener;
  import org.eclipse.jface.action.IMenuManager;
  import org.eclipse.jface.action.MenuManager;
  import org.eclipse.jface.viewers.DoubleClickEvent;
  import org.eclipse.jface.viewers.IDoubleClickListener;
  import org.eclipse.jface.viewers.IPostSelectionProvider;
  import org.eclipse.jface.viewers.ISelection;
  import org.eclipse.jface.viewers.ISelectionChangedListener;
  import org.eclipse.jface.viewers.ISelectionProvider;
  import org.eclipse.jface.viewers.IStructuredSelection;
  import org.eclipse.jface.viewers.StructuredSelection;
  import org.eclipse.jface.viewers.TreeViewer;
  import org.eclipse.jface.viewers.Viewer;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.events.DisposeEvent;
  import org.eclipse.swt.events.DisposeListener;
  import org.eclipse.swt.graphics.Color;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Control;
  import org.eclipse.swt.widgets.Display;
  import org.eclipse.swt.widgets.Menu;
  import org.eclipse.ui.part.ISetSelectionTarget;
  import org.eclipse.wst.jsdt.core.IIncludePathEntry;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IPackageFragment;
  import org.eclipse.wst.jsdt.core.IPackageFragmentRoot;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.core.JavaProject;
  import org.eclipse.wst.jsdt.internal.corext.buildpath.ClasspathModifier;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.filters.LibraryFilter;
  import org.eclipse.wst.jsdt.internal.ui.packageview.PackageExplorerContentProvider;
  import org.eclipse.wst.jsdt.internal.ui.packageview.PackageFragmentRootContainer;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.DecoratingJavaLabelProvider;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.JavaElementImageProvider;
  import org.eclipse.wst.jsdt.internal.ui.wizards.NewWizardMessages;
  import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.CPListElementAttribute;
  import org.eclipse.wst.jsdt.internal.ui.workingsets.WorkingSetModel;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementComparator;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
  
  /**
   * A package explorer widget that can be used in dialogs. It uses its own 
   * content provider, label provider, element sorter and filter to display
   * elements that are not shown usually in the package explorer of the
   * workspace.
   */
  public class DialogPackageExplorer implements IMenuListener, ISelectionProvider, IPostSelectionProvider, ISetSelectionTarget {
      /**
       * A extended content provider for the package explorer.
       */
      private final class PackageContentProvider extends PackageExplorerContentProvider {
          public PackageContentProvider() {
              super(false);
          }
          
          /**
           * Get the elements of the current project
           * 
           * @param element the element to get the children from, will
           * not be used, instead the project children are returned directly
           * @return returns the children of the project
           */
          public Object[] getElements(Object element) {
              if (fCurrJProject == null || !fCurrJProject.exists())
                  return new Object[0];
              return new Object[] {fCurrJProject};
          }
      }
      
      /**
       * A extended label provider for the package explorer.
       */
      private final class PackageLabelProvider extends AppearanceAwareLabelProvider {
          
          public PackageLabelProvider(long textFlags, int imageFlags) {
              super(textFlags, imageFlags);
          }
          
          public String getText(Object element) {
              if (element instanceof CPListElementAttribute)
                  return null;
              String text= super.getText(element);
              try {
                  if (element instanceof IPackageFragmentRoot) {
                      IPackageFragmentRoot root= (IPackageFragmentRoot)element;
                      if (root.exists() && ClasspathModifier.filtersSet(root)) {
                          IIncludePathEntry entry= root.getRawIncludepathEntry();
                          int excluded= entry.getExclusionPatterns().length;
                          if (excluded == 1)
                              return Messages.format(NewWizardMessages.DialogPackageExplorer_LabelProvider_SingleExcluded, text); 
                          else if (excluded > 1)
                              return Messages.format(NewWizardMessages.DialogPackageExplorer_LabelProvider_MultiExcluded, new Object[] {text, Integer.valueOf(excluded)}); 
                      }
                  }
                  if (element instanceof IJavaScriptProject) {
                      IJavaScriptProject project= (IJavaScriptProject)element;
                      if (project.exists() && project.isOnIncludepath(project)) {
                          IPackageFragmentRoot root= project.findPackageFragmentRoot(project.getPath());
                          if (ClasspathModifier.filtersSet(root)) {
                              IIncludePathEntry entry= root.getRawIncludepathEntry();
                              int excluded= entry.getExclusionPatterns().length;
                              if (excluded == 1)
                                  return Messages.format(NewWizardMessages.DialogPackageExplorer_LabelProvider_SingleExcluded, text); 
                              else if (excluded > 1)
                                  return Messages.format(NewWizardMessages.DialogPackageExplorer_LabelProvider_MultiExcluded, new Object[] {text, Integer.valueOf(excluded)}); 
                          }
                      }
                  }
                  if (element instanceof IFile || element instanceof IFolder) {
                      IResource resource= (IResource)element;
                          if (resource.exists() && ClasspathModifier.isExcluded(resource, fCurrJProject))
                              return Messages.format(NewWizardMessages.DialogPackageExplorer_LabelProvider_Excluded, text); 
                  }
              } catch (JavaScriptModelException e) {
                  JavaScriptPlugin.log(e);
              }
              return text;
          }
          
          /* (non-Javadoc)
           * @see org.eclipse.wst.jsdt.internal.ui.viewsupport.JavaUILabelProvider#getForeground(java.lang.Object)
           */
          public Color getForeground(Object element) {
              try {
                  if (element instanceof IPackageFragmentRoot) {
                      IPackageFragmentRoot root= (IPackageFragmentRoot)element;
                      if (root.exists() && ClasspathModifier.filtersSet(root))
                          return getBlueColor();
                  }
                  if (element instanceof IJavaScriptProject) {
                      IJavaScriptProject project= (IJavaScriptProject)element;
                      if (project.exists() && project.isOnIncludepath(project)) {
                          IPackageFragmentRoot root= project.findPackageFragmentRoot(project.getPath());
                          if (root != null && ClasspathModifier.filtersSet(root))
                              return getBlueColor();
                      }
                  }
                  if (element instanceof IFile || element instanceof IFolder) {
                      IResource resource= (IResource)element;
                      if (resource.exists() && ClasspathModifier.isExcluded(resource, fCurrJProject))
                          return getBlueColor();
                  } 
              } catch (JavaScriptModelException e) {
                  JavaScriptPlugin.log(e);
              }
              return null;
          }
          
          private Color getBlueColor() {
              return Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
          }
      }
      
      /**
       * A extended element sorter for the package explorer. The java elements
       * are sorted in the normal way.
       */
      private final class ExtendedJavaElementSorter extends JavaScriptElementComparator {
          public ExtendedJavaElementSorter() {
              super();
          }
          
          public int compare(Viewer viewer, Object e1, Object e2) {
              if (e1 instanceof CPListElementAttribute)
                  return -1;
              if (e2 instanceof CPListElementAttribute)
                  return 1;
              return super.compare(viewer, e1, e2);
          }
      }
      
      /**
       * An extended filter for the package explorer which filters
       * libraries,
       * files named ".classpath" or ".project",
       * the default package, and
       * hidden folders.
       */
      private final class PackageFilter extends LibraryFilter {
          public boolean select(Viewer viewer, Object parentElement, Object element) {
              try {
                  if (element instanceof IFile) {
                      IFile file= (IFile) element;
                      if (file.getName().equals(JavaProject.CLASSPATH_FILENAME) || file.getName().equals(".project")) //$NON-NLS-1$//$NON-NLS-2$
                          return false;
                  } else if (element instanceof IPackageFragmentRoot) {
                      IIncludePathEntry cpe= ((IPackageFragmentRoot)element).getRawIncludepathEntry();
                      if (cpe == null || cpe.getEntryKind() == IIncludePathEntry.CPE_CONTAINER || cpe.getEntryKind() == IIncludePathEntry.CPE_LIBRARY || cpe.getEntryKind() == IIncludePathEntry.CPE_VARIABLE)
                          return false;
                  } else if (element instanceof PackageFragmentRootContainer) {
                  	return false;
                  } else if (element instanceof IPackageFragment) {
  					IPackageFragment fragment= (IPackageFragment)element;
                  	if (fragment.isDefaultPackage() && !fragment.hasChildren())
                  		return false;
                  } else if (element instanceof IFolder) {
                  	IFolder folder= (IFolder)element;
                  	if (folder.getName().startsWith(".")) //$NON-NLS-1$
                  	return false;
                  }
              } catch (JavaScriptModelException e) {
                  JavaScriptPlugin.log(e);
              }
              /*if (element instanceof IPackageFragmentRoot) {
                  IPackageFragmentRoot root= (IPackageFragmentRoot)element;
                  if (root.getElementName().endsWith(".jar") || root.getElementName().endsWith(".zip")) //$NON-NLS-1$ //$NON-NLS-2$
                      return false;
              }*/
              return super.select(viewer, parentElement, element);
          }
      }
      
      /** The tree showing the project like in the package explorer */
      private TreeViewer fPackageViewer;
      /** The tree's context menu */
      private Menu fContextMenu;
      /** The action group which is used to fill the context menu. The action group
       * is also called if the selection on the tree changes */
      private DialogPackageExplorerActionGroup fActionGroup;
      
      /** Stores the current selection in the tree 
       * @see #getSelection()
       */
      private IStructuredSelection fCurrentSelection;
      
      /** The current java project
       * @see #setInput(IJavaScriptProject)
       */
      private IJavaScriptProject fCurrJProject;
  	private PackageContentProvider fContentProvider;
      
      public DialogPackageExplorer() {
          fActionGroup= null;
          fCurrJProject= null;
          fCurrentSelection= new StructuredSelection();
      }
      
      public Control createControl(Composite parent) {
          fPackageViewer= new TreeViewer(parent, SWT.MULTI);
          fPackageViewer.setComparer(WorkingSetModel.COMPARER);
          fPackageViewer.addFilter(new PackageFilter());
          fPackageViewer.setComparator(new ExtendedJavaElementSorter());
          fPackageViewer.addDoubleClickListener(new IDoubleClickListener() {
              public void doubleClick(DoubleClickEvent event) {
                  Object element= ((IStructuredSelection)event.getSelection()).getFirstElement();
                  if (fPackageViewer.isExpandable(element)) {
                      fPackageViewer.setExpandedState(element, !fPackageViewer.getExpandedState(element));
                  }
              }
          });
          
          MenuManager menuMgr= new MenuManager("#PopupMenu"); //$NON-NLS-1$
          menuMgr.setRemoveAllWhenShown(true);
          menuMgr.addMenuListener(this);
          fContextMenu= menuMgr.createContextMenu(fPackageViewer.getTree());
          fPackageViewer.getTree().setMenu(fContextMenu);
          parent.addDisposeListener(new DisposeListener() {
              public void widgetDisposed(DisposeEvent e) {
                  fContextMenu.dispose();
              }
          });
          
          return fPackageViewer.getControl();
      }
      
      /**
       * Sets the action group for the package explorer.
       * The action group is necessary to populate the 
       * context menu with available actions. If no 
       * context menu is needed, then this method does not 
       * have to be called.
       * 
       * Should only be called once.
       *  
       * @param actionGroup the action group to be used for 
       * the context menu.
       */
      public void setActionGroup(final DialogPackageExplorerActionGroup actionGroup) {
          fActionGroup= actionGroup;
      }
      
      /**
       * Populate the context menu with the necessary actions.
       * 
       * @see org.eclipse.jface.action.IMenuListener#menuAboutToShow(org.eclipse.jface.action.IMenuManager)
       */
      public void menuAboutToShow(IMenuManager manager) {
          if (fActionGroup == null) // no context menu
              return;
          JavaScriptPlugin.createStandardGroups(manager);
          fActionGroup.fillContextMenu(manager);
      }
      
      /**
       * Set the content and label provider of the
       * <code>fPackageViewer</code>
       */
      public void setContentProvider() {
      	if (fContentProvider != null) {
      		fContentProvider.dispose();
      	}
  		fContentProvider= new PackageContentProvider();
  		fContentProvider.setIsFlatLayout(true);
  		PackageLabelProvider labelProvider= new PackageLabelProvider(AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS | JavaScriptElementLabels.P_COMPRESSED,
  				AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS | JavaElementImageProvider.SMALL_ICONS);
  		fPackageViewer.setContentProvider(fContentProvider);
  		fPackageViewer.setLabelProvider(new DecoratingJavaLabelProvider(labelProvider, false));
      }
      
      /**
       * Set the input for the package explorer.
       * 
       * @param project the project to be displayed
       */
      public void setInput(IJavaScriptProject project) {
      	IJavaScriptProject oldProject= fCurrJProject;
          fCurrJProject= project;
      	if (fContentProvider != null)
          	fContentProvider.inputChanged(fPackageViewer, oldProject, fCurrJProject);
  		fPackageViewer.setInput(new Object[0]);
  		
          List selectedElements= new ArrayList();
          selectedElements.add(fCurrJProject);
          setSelection(selectedElements);
      }
      
      public void dispose() {
      	if (fContentProvider != null) {
      		fContentProvider.dispose();
      		fContentProvider= null;
      	}
      	if (fActionGroup != null) {
      		fActionGroup.dispose();
      		fActionGroup= null;
      	}
      	fPackageViewer= null;
      }
      
      /**
       * Set the selection and focus to the list of elements
       * @param elements the object to be selected and displayed
       */
      public void setSelection(final List elements) {
          if (elements == null || elements.size() == 0)
              return;
  		try {
  	        ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
  	        	public void run(IProgressMonitor monitor) throws CoreException {
  	        		fPackageViewer.refresh();
  	                IStructuredSelection selection= new StructuredSelection(elements);
  	                fPackageViewer.setSelection(selection, true);
  	                fPackageViewer.getTree().setFocus();
  	                
  	                if (elements.size() == 1 && elements.get(0) instanceof IJavaScriptProject)
  	                    fPackageViewer.expandToLevel(elements.get(0), 1);
  	            }
  	        }, ResourcesPlugin.getWorkspace().getRoot(), IWorkspace.AVOID_UPDATE, new NullProgressMonitor());
          } catch (CoreException e) {
  	        JavaScriptPlugin.log(e);
          }
      }
      
      /**
       * The current list of selected elements. The 
       * list may be empty if no element is selected.
       * 
       * @return the current selection
       */
      public ISelection getSelection() {
          return fCurrentSelection;
      }
      
      /**
       * Get the viewer's control
       * 
       * @return the viewers control
       */
      public Control getViewerControl() {
          return fPackageViewer.getControl();
      }
  
  	/**
       * {@inheritDoc}
       */
      public void addSelectionChangedListener(ISelectionChangedListener listener) {
      	fPackageViewer.addSelectionChangedListener(listener);
      }
  
  	/**
       * {@inheritDoc}
       */
      public void removeSelectionChangedListener(ISelectionChangedListener listener) {
      	fPackageViewer.removeSelectionChangedListener(listener);
      }
  
  	/**
       * {@inheritDoc}
       */
      public void setSelection(ISelection selection) {
      	setSelection(((StructuredSelection)selection).toList());
      }
  
  	/**
       * {@inheritDoc}
       */
      public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
      	fPackageViewer.addPostSelectionChangedListener(listener);
      }
  
  	/**
       * {@inheritDoc}
       */
      public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
      	fPackageViewer.removePostSelectionChangedListener(listener);
      }
  
  	/**
       * {@inheritDoc}
       */
      public void selectReveal(ISelection selection) {
      	setSelection(selection);
      }
  }
  