/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.wizards.newproject; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.tools.lint.detector.api.LintUtils; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnViewer; import org.eclipse.jface.viewers.EditingSupport; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.DirectoryDialog; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkingSet; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** WizardPage for importing Android projects */ class ImportPage extends WizardPage implements SelectionListener, IStructuredContentProvider, ICheckStateListener, KeyListener, TraverseListener, ControlListener { private static final int DIR_COLUMN = 0; private static final int NAME_COLUMN = 1; private final NewProjectWizardState mValues; private List<ImportedProject> mProjectPaths; private final IProject[] mExistingProjects; private Text mDir; private Button mBrowseButton; private Button mCopyCheckBox; private Button mRefreshButton; private Button mDeselectAllButton; private Button mSelectAllButton; private Table mTable; private CheckboxTableViewer mCheckboxTableViewer; private WorkingSetGroup mWorkingSetGroup; ImportPage(NewProjectWizardState values) { super("importPage"); //$NON-NLS-1$ mValues = values; setTitle("Import Projects"); setDescription("Select a directory to search for existing Android projects"); mWorkingSetGroup = new WorkingSetGroup(); setWorkingSets(new IWorkingSet[0]); // Record all projects such that we can ensure that the project names are unique IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); mExistingProjects = workspaceRoot.getProjects(); } public void init(IStructuredSelection selection, IWorkbenchPart activePart) { setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart)); } @SuppressWarnings("unused") // SWT constructors have side effects and aren't unused @Override public void createControl(Composite parent) { Composite container = new Composite(parent, SWT.NULL); setControl(container); container.setLayout(new GridLayout(3, false)); Label directoryLabel = new Label(container, SWT.NONE); directoryLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); directoryLabel.setText("Root Directory:"); mDir = new Text(container, SWT.BORDER); mDir.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); mDir.addKeyListener(this); mDir.addTraverseListener(this); mBrowseButton = new Button(container, SWT.NONE); mBrowseButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); mBrowseButton.setText("Browse..."); mBrowseButton.addSelectionListener(this); Label projectsLabel = new Label(container, SWT.NONE); projectsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); projectsLabel.setText("Projects:"); mTable = new Table(container, SWT.CHECK); mTable.setHeaderVisible(true); mCheckboxTableViewer = new CheckboxTableViewer(mTable); TableViewerColumn dirViewerColumn = new TableViewerColumn(mCheckboxTableViewer, SWT.NONE); TableColumn dirColumn = dirViewerColumn.getColumn(); dirColumn.setWidth(200); dirColumn.setText("Project to Import"); TableViewerColumn nameViewerColumn = new TableViewerColumn(mCheckboxTableViewer, SWT.NONE); TableColumn nameColumn = nameViewerColumn.getColumn(); nameColumn.setWidth(200); nameColumn.setText("New Project Name"); nameViewerColumn.setEditingSupport(new ProjectNameEditingSupport(mCheckboxTableViewer)); mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 4)); mTable.setLinesVisible(true); mTable.setHeaderVisible(true); mTable.addSelectionListener(this); mTable.addControlListener(this); mCheckboxTableViewer.setContentProvider(this); mCheckboxTableViewer.setInput(this); mCheckboxTableViewer.addCheckStateListener(this); mCheckboxTableViewer.setLabelProvider(new ProjectCellLabelProvider()); mSelectAllButton = new Button(container, SWT.NONE); mSelectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); mSelectAllButton.setText("Select All"); mSelectAllButton.addSelectionListener(this); mDeselectAllButton = new Button(container, SWT.NONE); mDeselectAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); mDeselectAllButton.setText("Deselect All"); mDeselectAllButton.addSelectionListener(this); mRefreshButton = new Button(container, SWT.NONE); mRefreshButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); mRefreshButton.setText("Refresh"); mRefreshButton.addSelectionListener(this); new Label(container, SWT.NONE); mCopyCheckBox = new Button(container, SWT.CHECK); mCopyCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); mCopyCheckBox.setText("Copy projects into workspace"); mCopyCheckBox.addSelectionListener(this); Composite group = mWorkingSetGroup.createControl(container); group.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1)); updateColumnWidths(); } private void updateColumnWidths() { Rectangle r = mTable.getClientArea(); int availableWidth = r.width; // Add all available size to the first column for (int i = 1; i < mTable.getColumnCount(); i++) { TableColumn column = mTable.getColumn(i); availableWidth -= column.getWidth(); } if (availableWidth > 100) { mTable.getColumn(0).setWidth(availableWidth); } } @Override public void setVisible(boolean visible) { super.setVisible(visible); validatePage(); } private void refresh() { File root = new File(mDir.getText().trim()); mProjectPaths = searchForProjects(root); mCheckboxTableViewer.refresh(); mCheckboxTableViewer.setAllChecked(true); List<ImportedProject> selected = new ArrayList<ImportedProject>(); List<ImportedProject> disabled = new ArrayList<ImportedProject>(); for (ImportedProject project : mProjectPaths) { String projectName = project.getProjectName(); boolean invalid = false; for (IProject existingProject : mExistingProjects) { if (projectName.equals(existingProject.getName())) { invalid = true; break; } } if (invalid) { disabled.add(project); } else { selected.add(project); } } mValues.importProjects = selected; mCheckboxTableViewer.setGrayedElements(disabled.toArray()); mCheckboxTableViewer.setCheckedElements(selected.toArray()); mCheckboxTableViewer.refresh(); mCheckboxTableViewer.getTable().setFocus(); validatePage(); } private List<ImportedProject> searchForProjects(File dir) { List<ImportedProject> projects = new ArrayList<ImportedProject>(); addProjects(dir, projects, dir.getPath().length() + 1); return projects; } /** Finds all project directories under the given directory */ private void addProjects(File dir, List<ImportedProject> projects, int prefixLength) { if (dir.isDirectory()) { if (LintUtils.isProjectDir(dir)) { String relative = dir.getPath(); if (relative.length() > prefixLength) { relative = relative.substring(prefixLength); } projects.add(new ImportedProject(dir, relative)); } File[] children = dir.listFiles(); if (children != null) { for (File child : children) { addProjects(child, projects, prefixLength); } } } } private void validatePage() { IStatus status = null; // Validate project name -- unless we're creating a sample, in which case // the user will get a chance to pick the name on the Sample page if (mProjectPaths == null || mProjectPaths.isEmpty()) { status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Select a directory to search for existing Android projects"); } else if (mValues.importProjects == null || mValues.importProjects.isEmpty()) { status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "Select at least one project"); } else { for (ImportedProject project : mValues.importProjects) { if (mCheckboxTableViewer.getGrayed(project)) { status = new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format("Cannot import %1$s because the project name is in use", project.getProjectName())); break; } else { status = ProjectNamePage.validateProjectName(project.getProjectName()); if (status != null && !status.isOK()) { // Need to insert project name to make it clear which project name // is in violation if (mValues.importProjects.size() > 1) { String message = String.format("%1$s: %2$s", project.getProjectName(), status.getMessage()); status = new Status(status.getSeverity(), AdtPlugin.PLUGIN_ID, message); } break; } else { status = null; // Don't leave non null status with isOK() == true } } } } // -- update UI & enable finish if there's no error setPageComplete(status == null || status.getSeverity() != IStatus.ERROR); if (status != null) { setMessage(status.getMessage(), status.getSeverity() == IStatus.ERROR ? IMessageProvider.ERROR : IMessageProvider.WARNING); } else { setErrorMessage(null); setMessage(null); } } /** * Returns the working sets to which the new project should be added. * * @return the selected working sets to which the new project should be added */ private IWorkingSet[] getWorkingSets() { return mWorkingSetGroup.getSelectedWorkingSets(); } /** * Sets the working sets to which the new project should be added. * * @param workingSets the initial selected working sets */ private void setWorkingSets(IWorkingSet[] workingSets) { assert workingSets != null; mWorkingSetGroup.setWorkingSets(workingSets); } @Override public IWizardPage getNextPage() { // Sync working set data to the value object, since the WorkingSetGroup // doesn't let us add listeners to do this lazily mValues.workingSets = getWorkingSets(); return super.getNextPage(); } // ---- Implements SelectionListener ---- @Override public void widgetSelected(SelectionEvent e) { Object source = e.getSource(); if (source == mBrowseButton) { // Choose directory DirectoryDialog dialog = new DirectoryDialog(getShell(), SWT.OPEN); String path = mDir.getText().trim(); if (path.length() > 0) { dialog.setFilterPath(path); } String file = dialog.open(); if (file != null) { mDir.setText(file); refresh(); } } else if (source == mSelectAllButton) { mCheckboxTableViewer.setAllChecked(true); mValues.importProjects = mProjectPaths; } else if (source == mDeselectAllButton) { mCheckboxTableViewer.setAllChecked(false); mValues.importProjects = Collections.emptyList(); } else if (source == mRefreshButton || source == mDir) { refresh(); } else if (source == mCopyCheckBox) { mValues.copyIntoWorkspace = mCopyCheckBox.getSelection(); } validatePage(); } @Override public void widgetDefaultSelected(SelectionEvent e) { } // ---- KeyListener ---- @Override public void keyPressed(KeyEvent e) { if (e.getSource() == mDir) { if (e.keyCode == SWT.CR) { refresh(); } } } @Override public void keyReleased(KeyEvent e) { } // ---- TraverseListener ---- @Override public void keyTraversed(TraverseEvent e) { // Prevent Return from running through the wizard; return is handled by // key listener to refresh project list instead if (SWT.TRAVERSE_RETURN == e.detail) { e.doit = false; } } // ---- Implements IStructuredContentProvider ---- @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public Object[] getElements(Object inputElement) { return mProjectPaths != null ? mProjectPaths.toArray() : new Object[0]; } // ---- Implements ICheckStateListener ---- @Override public void checkStateChanged(CheckStateChangedEvent event) { // Try to disable other elements that conflict with this Object[] checked = mCheckboxTableViewer.getCheckedElements(); List<ImportedProject> selected = new ArrayList<ImportedProject>(checked.length); for (Object o : checked) { if (!mCheckboxTableViewer.getGrayed(o)) { selected.add((ImportedProject) o); } } mValues.importProjects = selected; validatePage(); mCheckboxTableViewer.update(event.getElement(), null); } // ---- Implements ControlListener ---- @Override public void controlMoved(ControlEvent e) { } @Override public void controlResized(ControlEvent e) { updateColumnWidths(); } private final class ProjectCellLabelProvider extends CellLabelProvider { @Override public void update(ViewerCell cell) { Object element = cell.getElement(); int index = cell.getColumnIndex(); ImportedProject project = (ImportedProject) element; Display display = mTable.getDisplay(); Color fg; if (mCheckboxTableViewer.getGrayed(element)) { fg = display.getSystemColor(SWT.COLOR_DARK_GRAY); } else { fg = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND); } cell.setForeground(fg); cell.setBackground(display.getSystemColor(SWT.COLOR_LIST_BACKGROUND)); switch (index) { case DIR_COLUMN: { // Directory name cell.setText(project.getRelativePath()); return; } case NAME_COLUMN: { // New name cell.setText(project.getProjectName()); return; } default: assert false : index; } cell.setText(""); } } /** Editing support for the project name column */ private class ProjectNameEditingSupport extends EditingSupport { private ProjectNameEditingSupport(ColumnViewer viewer) { super(viewer); } @Override protected void setValue(Object element, Object value) { ImportedProject project = (ImportedProject) element; project.setProjectName(value.toString()); mCheckboxTableViewer.update(element, null); validatePage(); } @Override protected Object getValue(Object element) { ImportedProject project = (ImportedProject) element; return project.getProjectName(); } @Override protected CellEditor getCellEditor(Object element) { return new TextCellEditor(mTable); } @Override protected boolean canEdit(Object element) { return true; } } }