Home | History | Annotate | Download | only in newproject
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.wizards.newproject;
     17 
     18 import com.android.ide.eclipse.adt.AdtPlugin;
     19 import com.android.tools.lint.detector.api.LintUtils;
     20 
     21 import org.eclipse.core.resources.IProject;
     22 import org.eclipse.core.resources.IWorkspaceRoot;
     23 import org.eclipse.core.resources.ResourcesPlugin;
     24 import org.eclipse.core.runtime.IStatus;
     25 import org.eclipse.core.runtime.Status;
     26 import org.eclipse.jface.dialogs.IMessageProvider;
     27 import org.eclipse.jface.viewers.CellEditor;
     28 import org.eclipse.jface.viewers.CellLabelProvider;
     29 import org.eclipse.jface.viewers.CheckStateChangedEvent;
     30 import org.eclipse.jface.viewers.CheckboxTableViewer;
     31 import org.eclipse.jface.viewers.ColumnViewer;
     32 import org.eclipse.jface.viewers.EditingSupport;
     33 import org.eclipse.jface.viewers.ICheckStateListener;
     34 import org.eclipse.jface.viewers.IStructuredContentProvider;
     35 import org.eclipse.jface.viewers.IStructuredSelection;
     36 import org.eclipse.jface.viewers.TableViewerColumn;
     37 import org.eclipse.jface.viewers.TextCellEditor;
     38 import org.eclipse.jface.viewers.Viewer;
     39 import org.eclipse.jface.viewers.ViewerCell;
     40 import org.eclipse.jface.wizard.IWizardPage;
     41 import org.eclipse.jface.wizard.WizardPage;
     42 import org.eclipse.swt.SWT;
     43 import org.eclipse.swt.events.ControlEvent;
     44 import org.eclipse.swt.events.ControlListener;
     45 import org.eclipse.swt.events.KeyEvent;
     46 import org.eclipse.swt.events.KeyListener;
     47 import org.eclipse.swt.events.SelectionEvent;
     48 import org.eclipse.swt.events.SelectionListener;
     49 import org.eclipse.swt.events.TraverseEvent;
     50 import org.eclipse.swt.events.TraverseListener;
     51 import org.eclipse.swt.graphics.Color;
     52 import org.eclipse.swt.graphics.Rectangle;
     53 import org.eclipse.swt.layout.GridData;
     54 import org.eclipse.swt.layout.GridLayout;
     55 import org.eclipse.swt.widgets.Button;
     56 import org.eclipse.swt.widgets.Composite;
     57 import org.eclipse.swt.widgets.DirectoryDialog;
     58 import org.eclipse.swt.widgets.Display;
     59 import org.eclipse.swt.widgets.Label;
     60 import org.eclipse.swt.widgets.Table;
     61 import org.eclipse.swt.widgets.TableColumn;
     62 import org.eclipse.swt.widgets.Text;
     63 import org.eclipse.ui.IWorkbenchPart;
     64 import org.eclipse.ui.IWorkingSet;
     65 
     66 import java.io.File;
     67 import java.util.ArrayList;
     68 import java.util.Collections;
     69 import java.util.List;
     70 
     71 /** WizardPage for importing Android projects */
     72 class ImportPage extends WizardPage implements SelectionListener, IStructuredContentProvider,
     73         ICheckStateListener, KeyListener, TraverseListener, ControlListener {
     74     private static final int DIR_COLUMN = 0;
     75     private static final int NAME_COLUMN = 1;
     76 
     77     private final NewProjectWizardState mValues;
     78     private List<ImportedProject> mProjectPaths;
     79     private final IProject[] mExistingProjects;
     80 
     81     private Text mDir;
     82     private Button mBrowseButton;
     83     private Button mCopyCheckBox;
     84     private Button mRefreshButton;
     85     private Button mDeselectAllButton;
     86     private Button mSelectAllButton;
     87     private Table mTable;
     88     private CheckboxTableViewer mCheckboxTableViewer;
     89     private WorkingSetGroup mWorkingSetGroup;
     90 
     91     ImportPage(NewProjectWizardState values) {
     92         super("importPage"); //$NON-NLS-1$
     93         mValues = values;
     94         setTitle("Import Projects");
     95         setDescription("Select a directory to search for existing Android projects");
     96         mWorkingSetGroup = new WorkingSetGroup();
     97         setWorkingSets(new IWorkingSet[0]);
     98 
     99         // Record all projects such that we can ensure that the project names are unique
    100         IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
    101         mExistingProjects = workspaceRoot.getProjects();
    102     }
    103 
    104     public void init(IStructuredSelection selection, IWorkbenchPart activePart) {
    105         setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart));
    106     }
    107 
    108     @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused
    109     @Override
    110     public void createControl(Composite parent) {
    111         Composite container = new Composite(parent, SWT.NULL);
    112         setControl(container);
    113         container.setLayout(new GridLayout(3, false));
    114 
    115         Label directoryLabel = new Label(container, SWT.NONE);
    116         directoryLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    117         directoryLabel.setText("Root Directory:");
    118 
    119         mDir = new Text(container, SWT.BORDER);
    120         mDir.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    121         mDir.addKeyListener(this);
    122         mDir.addTraverseListener(this);
    123 
    124         mBrowseButton = new Button(container, SWT.NONE);
    125         mBrowseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    126         mBrowseButton.setText("Browse...");
    127         mBrowseButton.addSelectionListener(this);
    128 
    129         Label projectsLabel = new Label(container, SWT.NONE);
    130         projectsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
    131         projectsLabel.setText("Projects:");
    132 
    133         mTable = new Table(container, SWT.CHECK);
    134         mTable.setHeaderVisible(true);
    135         mCheckboxTableViewer = new CheckboxTableViewer(mTable);
    136 
    137         TableViewerColumn dirViewerColumn = new TableViewerColumn(mCheckboxTableViewer, SWT.NONE);
    138         TableColumn dirColumn = dirViewerColumn.getColumn();
    139         dirColumn.setWidth(200);
    140         dirColumn.setText("Project to Import");
    141         TableViewerColumn nameViewerColumn = new TableViewerColumn(mCheckboxTableViewer, SWT.NONE);
    142         TableColumn nameColumn = nameViewerColumn.getColumn();
    143         nameColumn.setWidth(200);
    144         nameColumn.setText("New Project Name");
    145         nameViewerColumn.setEditingSupport(new ProjectNameEditingSupport(mCheckboxTableViewer));
    146 
    147         mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 4));
    148         mTable.setLinesVisible(true);
    149         mTable.setHeaderVisible(true);
    150         mTable.addSelectionListener(this);
    151         mTable.addControlListener(this);
    152         mCheckboxTableViewer.setContentProvider(this);
    153         mCheckboxTableViewer.setInput(this);
    154         mCheckboxTableViewer.addCheckStateListener(this);
    155         mCheckboxTableViewer.setLabelProvider(new ProjectCellLabelProvider());
    156 
    157         mSelectAllButton = new Button(container, SWT.NONE);
    158         mSelectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    159         mSelectAllButton.setText("Select All");
    160         mSelectAllButton.addSelectionListener(this);
    161 
    162         mDeselectAllButton = new Button(container, SWT.NONE);
    163         mDeselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    164         mDeselectAllButton.setText("Deselect All");
    165         mDeselectAllButton.addSelectionListener(this);
    166 
    167         mRefreshButton = new Button(container, SWT.NONE);
    168         mRefreshButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
    169         mRefreshButton.setText("Refresh");
    170         mRefreshButton.addSelectionListener(this);
    171         new Label(container, SWT.NONE);
    172 
    173         mCopyCheckBox = new Button(container, SWT.CHECK);
    174         mCopyCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1));
    175         mCopyCheckBox.setText("Copy projects into workspace");
    176         mCopyCheckBox.addSelectionListener(this);
    177 
    178         Composite group = mWorkingSetGroup.createControl(container);
    179         group.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));
    180 
    181         updateColumnWidths();
    182     }
    183 
    184     private void updateColumnWidths() {
    185         Rectangle r = mTable.getClientArea();
    186         int availableWidth = r.width;
    187         // Add all available size to the first column
    188         for (int i = 1; i < mTable.getColumnCount(); i++) {
    189             TableColumn column = mTable.getColumn(i);
    190             availableWidth -= column.getWidth();
    191         }
    192         if (availableWidth > 100) {
    193             mTable.getColumn(0).setWidth(availableWidth);
    194         }
    195     }
    196 
    197     @Override
    198     public void setVisible(boolean visible) {
    199         super.setVisible(visible);
    200         validatePage();
    201     }
    202 
    203     private void refresh() {
    204         File root = new File(mDir.getText().trim());
    205         mProjectPaths = searchForProjects(root);
    206         mCheckboxTableViewer.refresh();
    207         mCheckboxTableViewer.setAllChecked(true);
    208 
    209         updateValidity();
    210         validatePage();
    211     }
    212 
    213     private void updateValidity(){
    214         List<ImportedProject> selected = new ArrayList<ImportedProject>();
    215         List<ImportedProject> disabled = new ArrayList<ImportedProject>();
    216         for (ImportedProject project : mProjectPaths) {
    217             String projectName = project.getProjectName();
    218             boolean invalid = false;
    219             for (IProject existingProject : mExistingProjects) {
    220                 if (projectName.equals(existingProject.getName())) {
    221                     invalid = true;
    222                     break;
    223                 }
    224             }
    225             if (invalid) {
    226                 disabled.add(project);
    227             } else {
    228                 selected.add(project);
    229             }
    230         }
    231 
    232         mValues.importProjects = selected;
    233 
    234         mCheckboxTableViewer.setGrayedElements(disabled.toArray());
    235         mCheckboxTableViewer.setCheckedElements(selected.toArray());
    236         mCheckboxTableViewer.refresh();
    237         mCheckboxTableViewer.getTable().setFocus();
    238     }
    239 
    240     private List<ImportedProject> searchForProjects(File dir) {
    241         List<ImportedProject> projects = new ArrayList<ImportedProject>();
    242         addProjects(dir, projects, dir.getPath().length() + 1);
    243         return projects;
    244     }
    245 
    246     /** Finds all project directories under the given directory */
    247     private void addProjects(File dir, List<ImportedProject> projects, int prefixLength) {
    248         if (dir.isDirectory()) {
    249             if (LintUtils.isManifestFolder(dir)) {
    250                 String relative = dir.getPath();
    251                 if (relative.length() > prefixLength) {
    252                     relative = relative.substring(prefixLength);
    253                 }
    254                 projects.add(new ImportedProject(dir, relative));
    255             }
    256 
    257             File[] children = dir.listFiles();
    258             if (children != null) {
    259                 for (File child : children) {
    260                     addProjects(child, projects, prefixLength);
    261                 }
    262             }
    263         }
    264     }
    265 
    266     private void validatePage() {
    267         IStatus status = null;
    268 
    269         // Validate project name -- unless we're creating a sample, in which case
    270         // the user will get a chance to pick the name on the Sample page
    271         if (mProjectPaths == null || mProjectPaths.isEmpty()) {
    272             status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    273                     "Select a directory to search for existing Android projects");
    274         } else if (mValues.importProjects == null || mValues.importProjects.isEmpty()) {
    275             status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    276                     "Select at least one project");
    277         } else {
    278             for (ImportedProject project : mValues.importProjects) {
    279                 if (mCheckboxTableViewer.getGrayed(project)) {
    280                     status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
    281                             String.format("Cannot import %1$s because the project name is in use",
    282                                     project.getProjectName()));
    283                     break;
    284                 } else {
    285                     status = ProjectNamePage.validateProjectName(project.getProjectName());
    286                     if (status != null && !status.isOK()) {
    287                         // Need to insert project name to make it clear which project name
    288                         // is in violation
    289                         if (mValues.importProjects.size() > 1) {
    290                             String message = String.format("%1$s: %2$s",
    291                                     project.getProjectName(), status.getMessage());
    292                             status = new Status(status.getSeverity(), AdtPlugin.PLUGIN_ID,
    293                                     message);
    294                         }
    295                         break;
    296                     } else {
    297                         status = null; // Don't leave non null status with isOK() == true
    298                     }
    299                 }
    300             }
    301         }
    302 
    303         // -- update UI & enable finish if there's no error
    304         setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
    305         if (status != null) {
    306             setMessage(status.getMessage(),
    307                     status.getSeverity() == IStatus.ERROR
    308                         ? IMessageProvider.ERROR : IMessageProvider.WARNING);
    309         } else {
    310             setErrorMessage(null);
    311             setMessage(null);
    312         }
    313     }
    314 
    315     /**
    316      * Returns the working sets to which the new project should be added.
    317      *
    318      * @return the selected working sets to which the new project should be added
    319      */
    320     private IWorkingSet[] getWorkingSets() {
    321         return mWorkingSetGroup.getSelectedWorkingSets();
    322     }
    323 
    324     /**
    325      * Sets the working sets to which the new project should be added.
    326      *
    327      * @param workingSets the initial selected working sets
    328      */
    329     private void setWorkingSets(IWorkingSet[] workingSets) {
    330         assert workingSets != null;
    331         mWorkingSetGroup.setWorkingSets(workingSets);
    332     }
    333 
    334     @Override
    335     public IWizardPage getNextPage() {
    336         // Sync working set data to the value object, since the WorkingSetGroup
    337         // doesn't let us add listeners to do this lazily
    338         mValues.workingSets = getWorkingSets();
    339 
    340         return super.getNextPage();
    341     }
    342 
    343     // ---- Implements SelectionListener ----
    344 
    345     @Override
    346     public void widgetSelected(SelectionEvent e) {
    347         Object source = e.getSource();
    348         if (source == mBrowseButton) {
    349             // Choose directory
    350             DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.OPEN);
    351             String path = mDir.getText().trim();
    352             if (path.length() > 0) {
    353                 dialog.setFilterPath(path);
    354             }
    355             String file = dialog.open();
    356             if (file != null) {
    357                 mDir.setText(file);
    358                 refresh();
    359             }
    360         } else if (source == mSelectAllButton) {
    361             mCheckboxTableViewer.setAllChecked(true);
    362             mValues.importProjects = mProjectPaths;
    363         } else if (source == mDeselectAllButton) {
    364             mCheckboxTableViewer.setAllChecked(false);
    365             mValues.importProjects = Collections.emptyList();
    366         } else if (source == mRefreshButton || source == mDir) {
    367             refresh();
    368         } else if (source == mCopyCheckBox) {
    369             mValues.copyIntoWorkspace = mCopyCheckBox.getSelection();
    370         }
    371 
    372         validatePage();
    373     }
    374 
    375     @Override
    376     public void widgetDefaultSelected(SelectionEvent e) {
    377     }
    378 
    379     // ---- KeyListener ----
    380 
    381     @Override
    382     public void keyPressed(KeyEvent e) {
    383         if (e.getSource() == mDir) {
    384             if (e.keyCode == SWT.CR) {
    385                 refresh();
    386             }
    387         }
    388     }
    389 
    390     @Override
    391     public void keyReleased(KeyEvent e) {
    392     }
    393 
    394     // ---- TraverseListener ----
    395 
    396     @Override
    397     public void keyTraversed(TraverseEvent e) {
    398         // Prevent Return from running through the wizard; return is handled by
    399         // key listener to refresh project list instead
    400         if (SWT.TRAVERSE_RETURN == e.detail) {
    401             e.doit = false;
    402         }
    403     }
    404 
    405     // ---- Implements IStructuredContentProvider ----
    406 
    407     @Override
    408     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    409     }
    410 
    411     @Override
    412     public Object[] getElements(Object inputElement) {
    413         return mProjectPaths != null ? mProjectPaths.toArray() : new Object[0];
    414     }
    415 
    416     // ---- Implements ICheckStateListener ----
    417 
    418     @Override
    419     public void checkStateChanged(CheckStateChangedEvent event) {
    420         // Try to disable other elements that conflict with this
    421         Object[] checked = mCheckboxTableViewer.getCheckedElements();
    422         List<ImportedProject> selected = new ArrayList<ImportedProject>(checked.length);
    423         for (Object o : checked) {
    424             if (!mCheckboxTableViewer.getGrayed(o)) {
    425                 selected.add((ImportedProject) o);
    426             }
    427         }
    428         mValues.importProjects = selected;
    429         validatePage();
    430 
    431         mCheckboxTableViewer.update(event.getElement(), null);
    432     }
    433 
    434     // ---- Implements ControlListener ----
    435 
    436     @Override
    437     public void controlMoved(ControlEvent e) {
    438     }
    439 
    440     @Override
    441     public void controlResized(ControlEvent e) {
    442         updateColumnWidths();
    443     }
    444 
    445     private final class ProjectCellLabelProvider extends CellLabelProvider {
    446         @Override
    447         public void update(ViewerCell cell) {
    448             Object element = cell.getElement();
    449             int index = cell.getColumnIndex();
    450             ImportedProject project = (ImportedProject) element;
    451 
    452             Display display = mTable.getDisplay();
    453             Color fg;
    454             if (mCheckboxTableViewer.getGrayed(element)) {
    455                 fg = display.getSystemColor(SWT.COLOR_DARK_GRAY);
    456             } else {
    457                 fg = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
    458             }
    459             cell.setForeground(fg);
    460             cell.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND));
    461 
    462             switch (index) {
    463                 case DIR_COLUMN: {
    464                     // Directory name
    465                     cell.setText(project.getRelativePath());
    466                     return;
    467                 }
    468 
    469                 case NAME_COLUMN: {
    470                     // New name
    471                     cell.setText(project.getProjectName());
    472                     return;
    473                 }
    474                 default:
    475                     assert false : index;
    476             }
    477             cell.setText("");
    478         }
    479     }
    480 
    481     /** Editing support for the project name column */
    482     private class ProjectNameEditingSupport extends EditingSupport {
    483         private ProjectNameEditingSupport(ColumnViewer viewer) {
    484             super(viewer);
    485         }
    486 
    487         @Override
    488         protected void setValue(Object element, Object value) {
    489             ImportedProject project = (ImportedProject) element;
    490             project.setProjectName(value.toString());
    491             mCheckboxTableViewer.update(element, null);
    492             updateValidity();
    493             validatePage();
    494         }
    495 
    496         @Override
    497         protected Object getValue(Object element) {
    498             ImportedProject project = (ImportedProject) element;
    499             return project.getProjectName();
    500         }
    501 
    502         @Override
    503         protected CellEditor getCellEditor(Object element) {
    504             return new TextCellEditor(mTable);
    505         }
    506 
    507         @Override
    508         protected boolean canEdit(Object element) {
    509             return true;
    510         }
    511     }
    512 }
    513