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.Arrays;
  import java.util.List;
  
  import org.eclipse.jface.action.ToolBarManager;
  import org.eclipse.jface.operation.IRunnableContext;
  import org.eclipse.jface.preference.IPreferenceStore;
  import org.eclipse.jface.viewers.StructuredSelection;
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.custom.SashForm;
  import org.eclipse.swt.layout.GridData;
  import org.eclipse.swt.layout.GridLayout;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Control;
  import org.eclipse.swt.widgets.Display;
  import org.eclipse.ui.forms.events.ExpansionAdapter;
  import org.eclipse.ui.forms.events.ExpansionEvent;
  import org.eclipse.ui.forms.widgets.ExpandableComposite;
  import org.eclipse.wst.jsdt.core.IIncludePathEntry;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IPackageFragmentRoot;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.corext.buildpath.BuildpathDelta;
  import org.eclipse.wst.jsdt.internal.corext.buildpath.ClasspathModifier;
  import org.eclipse.wst.jsdt.internal.corext.buildpath.IBuildpathModifierListener;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  import org.eclipse.wst.jsdt.internal.ui.preferences.ScrolledPageContent;
  import org.eclipse.wst.jsdt.internal.ui.util.PixelConverter;
  import org.eclipse.wst.jsdt.internal.ui.util.ViewerPane;
  import org.eclipse.wst.jsdt.internal.ui.wizards.NewWizardMessages;
  import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathBasePage;
  import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathsBlock;
  import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.CPListElement;
  import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.CPListElementAttribute;
  import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.ListDialogField;
  
  public class NewSourceContainerWorkbookPage extends BuildPathBasePage implements IBuildpathModifierListener {
      
      public static final String OPEN_SETTING= "org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.NewSourceContainerPage.openSetting";  //$NON-NLS-1$
      
      private ListDialogField fClassPathList;
      private HintTextGroup fHintTextGroup;
      private DialogPackageExplorer fPackageExplorer;
  	private DialogPackageExplorerActionGroup fActionGroup;
  	
  	private IJavaScriptProject fJavaScriptProject;
  
  	private final IRunnableContext fContext;
  
      /**
       * Constructor of the <code>NewSourceContainerWorkbookPage</code> which consists of 
       * a tree representing the project, a toolbar with the available actions, an area 
       * containing hyperlinks that perform the same actions as those in the toolbar but 
       * additionally with some short description.
       * 
       * @param classPathList
       * @param context a runnable context, can be <code>null</code>
       * @param buildPathsBlock 
       */
      public NewSourceContainerWorkbookPage(ListDialogField classPathList, IRunnableContext context, BuildPathsBlock buildPathsBlock) {
          fClassPathList= classPathList;
  		fContext= context;
  
  		fPackageExplorer= new DialogPackageExplorer();
  		fHintTextGroup= new HintTextGroup();
       }
      
      /**
       * Initialize the controls displaying
       * the content of the java project and saving 
       * the '.classpath' and '.project' file.
       * 
       * Must be called before initializing the 
       * controls using <code>getControl(Composite)</code>.
       * 
       * @param javaProject the current java project
       */
      public void init(IJavaScriptProject javaProject) {
  		fJavaScriptProject= javaProject;
  		fPackageExplorer.addPostSelectionChangedListener(fHintTextGroup);
  	    fActionGroup.getResetAllAction().setBreakPoint(javaProject);
  
  		if (Display.getCurrent() != null) {
  			doUpdateUI();
  		} else {
  			Display.getDefault().asyncExec(new Runnable() {
  				public void run() {
  					doUpdateUI();
  				}
  			});
  		}
      }
      
  	private void doUpdateUI() {
          fPackageExplorer.setInput(fJavaScriptProject);
      }
      
      public void dispose() {
      	if (fActionGroup != null) {
      		fActionGroup.removeBuildpathModifierListener(this);
      		fActionGroup= null;
      	}
      	fPackageExplorer.removePostSelectionChangedListener(fHintTextGroup);
      	fPackageExplorer.dispose();
      }
       
      /**
       * Initializes controls and return composite containing
       * these controls.
       * 
       * Before calling this method, make sure to have 
       * initialized this instance with a java project 
       * using <code>init(IJavaScriptProject)</code>.
       * 
       * @param parent the parent composite
       * @return composite containing controls
       * 
       * @see #init(IJavaScriptProject)
       */
      public Control getControl(Composite parent) {
          final int[] sashWeight= {60};
          final IPreferenceStore preferenceStore= JavaScriptPlugin.getDefault().getPreferenceStore();
          preferenceStore.setDefault(OPEN_SETTING, true);
          
          // ScrolledPageContent is needed for resizing on expand the expandable composite
          ScrolledPageContent scrolledContent = new ScrolledPageContent(parent);
          Composite body= scrolledContent.getBody();
          body.setLayout(new GridLayout());
          
          final SashForm sashForm= new SashForm(body, SWT.VERTICAL | SWT.NONE);
          sashForm.setFont(sashForm.getFont());
          
          ViewerPane pane= new ViewerPane(sashForm, SWT.BORDER | SWT.FLAT);
          pane.setContent(fPackageExplorer.createControl(pane));
  		fPackageExplorer.setContentProvider();
          
          final ExpandableComposite excomposite= new ExpandableComposite(sashForm, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);
          excomposite.setFont(sashForm.getFont());
          excomposite.setText(NewWizardMessages.NewSourceContainerWorkbookPage_HintTextGroup_title);
          final boolean isExpanded= preferenceStore.getBoolean(OPEN_SETTING);
          excomposite.setExpanded(isExpanded);
          excomposite.addExpansionListener(new ExpansionAdapter() {
                         public void expansionStateChanged(ExpansionEvent e) {
                             ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(excomposite);
                             if (parentScrolledComposite != null) {
                                boolean expanded= excomposite.isExpanded();
                                parentScrolledComposite.reflow(true);
                                adjustSashForm(sashWeight, sashForm, expanded);
                                preferenceStore.setValue(OPEN_SETTING, expanded);
                             }
                         }
                   });
          
          excomposite.setClient(fHintTextGroup.createControl(excomposite));
  		
          fActionGroup= new DialogPackageExplorerActionGroup(fHintTextGroup, fContext, fPackageExplorer, this);
  		fActionGroup.addBuildpathModifierListener(this);
            
          // Create toolbar with actions on the left
          ToolBarManager tbm= fActionGroup.createLeftToolBarManager(pane);
          pane.setTopCenter(null);
          pane.setTopLeft(tbm.getControl());
          
          // Create toolbar with help on the right
          tbm= fActionGroup.createLeftToolBar(pane);
          pane.setTopRight(tbm.getControl());
          
          fHintTextGroup.setActionGroup(fActionGroup);
          fPackageExplorer.setActionGroup(fActionGroup);
          
  		sashForm.setWeights(new int[] {60, 40});
  		adjustSashForm(sashWeight, sashForm, excomposite.isExpanded());
  		GridData gd= new GridData(GridData.FILL_BOTH);
  		PixelConverter converter= new PixelConverter(parent);
  		gd.heightHint= converter.convertHeightInCharsToPixels(20);
  		sashForm.setLayoutData(gd);
          
          parent.layout(true);
  
          return scrolledContent;
      }
      
      /**
       * Adjust the size of the sash form.
       * 
       * @param sashWeight the weight to be read or written
       * @param sashForm the sash form to apply the new weights to
       * @param isExpanded <code>true</code> if the expandable composite is 
       * expanded, <code>false</code> otherwise
       */
      private void adjustSashForm(int[] sashWeight, SashForm sashForm, boolean isExpanded) {
          if (isExpanded) {
              int upperWeight= sashWeight[0];
              sashForm.setWeights(new int[]{upperWeight, 100 - upperWeight});
          }
          else {
              // TODO Dividing by 10 because of https://bugs.eclipse.org/bugs/show_bug.cgi?id=81939
              sashWeight[0]= sashForm.getWeights()[0] / 10;
              sashForm.setWeights(new int[]{95, 5});
          }
          sashForm.layout(true);
      }
      
      /**
       * Get the scrolled page content of the given control by 
       * traversing the parents.
       * 
       * @param control the control to get the scrolled page content for 
       * @return the scrolled page content or <code>null</code> if none found
       */
      private ScrolledPageContent getParentScrolledComposite(Control control) {
         Control parent= control.getParent();
         while (!(parent instanceof ScrolledPageContent)) {
             parent= parent.getParent();
         }
         if (parent instanceof ScrolledPageContent) {
             return (ScrolledPageContent) parent;
         }
         return null;
     }
      
      /* (non-Javadoc)
       * @see org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathBasePage#getSelection()
       */
      public List getSelection() {
          List selectedList= new ArrayList();
          
          IJavaScriptProject project= fJavaScriptProject;
          try {
              List list= ((StructuredSelection)fPackageExplorer.getSelection()).toList();
              List existingEntries= ClasspathModifier.getExistingEntries(project);
          
              for(int i= 0; i < list.size(); i++) {
                  Object obj= list.get(i);
                  if (obj instanceof IPackageFragmentRoot) {
                      IPackageFragmentRoot element= (IPackageFragmentRoot)obj;
                      CPListElement cpElement= ClasspathModifier.getClasspathEntry(existingEntries, element); 
                      selectedList.add(cpElement);
                  }
                  else if (obj instanceof IJavaScriptProject) {
                      IIncludePathEntry entry= ClasspathModifier.getClasspathEntryFor(project.getPath(), project, IIncludePathEntry.CPE_SOURCE);
                      if (entry == null)
                          continue;
                      CPListElement cpElement= CPListElement.createFromExisting(entry, project);
                      selectedList.add(cpElement);
                  }
              }
          } catch (JavaScriptModelException e) {
              return new ArrayList();
          }
          return selectedList;
      }
  
      /* (non-Javadoc)
       * @see org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathBasePage#setSelection(java.util.List)
       */
      public void setSelection(List selection, boolean expand) {
  		// page switch
  		
          if (selection.size() == 0)
              return;
  	    
  		List cpEntries= new ArrayList();
  		
  		for (int i= 0; i < selection.size(); i++) {
  			Object obj= selection.get(i);
  			if (obj instanceof CPListElement) {
  				CPListElement element= (CPListElement) obj;
  				if (element.getEntryKind() == IIncludePathEntry.CPE_SOURCE) {
  					cpEntries.add(element);
  				}
  			} else if (obj instanceof CPListElementAttribute) {
  				CPListElementAttribute attribute= (CPListElementAttribute)obj;
  				CPListElement element= attribute.getParent();
  				if (element.getEntryKind() == IIncludePathEntry.CPE_SOURCE) {
  					cpEntries.add(element);
  				}
  			}
  		}
  		
          // refresh classpath
          List list= fClassPathList.getElements();
          IIncludePathEntry[] entries= new IIncludePathEntry[list.size()];
          for(int i= 0; i < list.size(); i++) {
              CPListElement entry= (CPListElement) list.get(i);
              entries[i]= entry.getClasspathEntry(); 
          }
          try {
  			fJavaScriptProject.setRawIncludepath(entries, null);
          } catch (JavaScriptModelException e) {
              JavaScriptPlugin.log(e);
          }
          
          fPackageExplorer.setSelection(cpEntries);
      }
  
      /* (non-Javadoc)
       * @see org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.BuildPathBasePage#isEntryKind(int)
       */
      public boolean isEntryKind(int kind) {
          return kind == IIncludePathEntry.CPE_SOURCE;
      }
      
      /**
       * Update <code>fClassPathList</code>.
       */
      public void buildpathChanged(BuildpathDelta delta) {
          fClassPathList.setElements(Arrays.asList(delta.getNewEntries()));
      }
  
  	/**
       * {@inheritDoc}
       */
      public void setFocus() {
      	fPackageExplorer.getViewerControl().setFocus();
      }
  
  	public IJavaScriptProject getJavaScriptProject() {
  		return fJavaScriptProject;
  	}
  }
  