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.templates;
17 
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.internal.wizards.newproject.WorkingSetGroup;
21 import com.android.ide.eclipse.adt.internal.wizards.newproject.WorkingSetHelper;
22 
23 import org.eclipse.core.runtime.IPath;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Platform;
26 import org.eclipse.core.runtime.Status;
27 import org.eclipse.jface.dialogs.IMessageProvider;
28 import org.eclipse.jface.viewers.IStructuredSelection;
29 import org.eclipse.jface.wizard.IWizardPage;
30 import org.eclipse.jface.wizard.WizardPage;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.events.FocusEvent;
33 import org.eclipse.swt.events.FocusListener;
34 import org.eclipse.swt.events.ModifyEvent;
35 import org.eclipse.swt.events.ModifyListener;
36 import org.eclipse.swt.events.SelectionEvent;
37 import org.eclipse.swt.events.SelectionListener;
38 import org.eclipse.swt.layout.GridData;
39 import org.eclipse.swt.layout.GridLayout;
40 import org.eclipse.swt.widgets.Button;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.DirectoryDialog;
43 import org.eclipse.swt.widgets.Label;
44 import org.eclipse.swt.widgets.Shell;
45 import org.eclipse.swt.widgets.Text;
46 import org.eclipse.ui.IWorkbenchPart;
47 import org.eclipse.ui.IWorkingSet;
48 
49 import java.io.File;
50 
51 /**
52  * Second wizard page in the "New Project From Template" wizard
53  */
54 public class ProjectContentsPage extends WizardPage
55         implements ModifyListener, SelectionListener, FocusListener {
56 
57     private final NewProjectWizardState mValues;
58 
59     private boolean mIgnore;
60     private Button mCustomIconToggle;
61     private Button mLibraryToggle;
62 
63     private Button mUseDefaultLocationToggle;
64     private Label mLocationLabel;
65     private Text mLocationText;
66     private Button mChooseLocationButton;
67     private static String sLastProjectLocation = System.getProperty("user.home"); //$NON-NLS-1$
68     private Button mCreateActivityToggle;
69     private WorkingSetGroup mWorkingSetGroup;
70 
ProjectContentsPage(NewProjectWizardState values)71     ProjectContentsPage(NewProjectWizardState values) {
72         super("newAndroidApp"); //$NON-NLS-1$
73         mValues = values;
74         setTitle("New Android Application");
75         setDescription("Configure Project");
76 
77         mWorkingSetGroup = new WorkingSetGroup();
78         setWorkingSets(new IWorkingSet[0]);
79     }
80 
81     @Override
createControl(Composite parent)82     public void createControl(Composite parent) {
83         Composite container = new Composite(parent, SWT.NULL);
84         setControl(container);
85         GridLayout gl_container = new GridLayout(4, false);
86         gl_container.horizontalSpacing = 10;
87         container.setLayout(gl_container);
88 
89         mCustomIconToggle = new Button(container, SWT.CHECK);
90         mCustomIconToggle.setSelection(true);
91         mCustomIconToggle.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
92         mCustomIconToggle.setText("Create custom launcher icon");
93         mCustomIconToggle.setSelection(mValues.createIcon);
94         mCustomIconToggle.addSelectionListener(this);
95 
96         mCreateActivityToggle = new Button(container, SWT.CHECK);
97         mCreateActivityToggle.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false,
98                 4, 1));
99         mCreateActivityToggle.setText("Create activity");
100         mCreateActivityToggle.setSelection(mValues.createActivity);
101         mCreateActivityToggle.addSelectionListener(this);
102 
103         new Label(container, SWT.NONE).setLayoutData(
104                 new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
105 
106         mLibraryToggle = new Button(container, SWT.CHECK);
107         mLibraryToggle.setSelection(true);
108         mLibraryToggle.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
109         mLibraryToggle.setText("Mark this project as a library");
110         mLibraryToggle.setSelection(mValues.isLibrary);
111         mLibraryToggle.addSelectionListener(this);
112 
113         // Blank line
114         new Label(container, SWT.NONE).setLayoutData(
115                 new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
116 
117         mUseDefaultLocationToggle = new Button(container, SWT.CHECK);
118         mUseDefaultLocationToggle.setLayoutData(
119                 new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
120         mUseDefaultLocationToggle.setText("Create Project in Workspace");
121         mUseDefaultLocationToggle.addSelectionListener(this);
122 
123         mLocationLabel = new Label(container, SWT.NONE);
124         mLocationLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
125         mLocationLabel.setText("Location:");
126 
127         mLocationText = new Text(container, SWT.BORDER);
128         mLocationText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
129         mLocationText.addModifyListener(this);
130 
131         mChooseLocationButton = new Button(container, SWT.NONE);
132         mChooseLocationButton.setText("Browse...");
133         mChooseLocationButton.addSelectionListener(this);
134         mChooseLocationButton.setEnabled(false);
135         setUseCustomLocation(!mValues.useDefaultLocation);
136 
137         new Label(container, SWT.NONE).setLayoutData(
138                 new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1));
139 
140         Composite group = mWorkingSetGroup.createControl(container);
141         group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 4, 1));
142     }
143 
144     @Override
setVisible(boolean visible)145     public void setVisible(boolean visible) {
146         super.setVisible(visible);
147 
148         if (visible) {
149             try {
150                 mIgnore = true;
151                 mUseDefaultLocationToggle.setSelection(mValues.useDefaultLocation);
152                 mLocationText.setText(mValues.projectLocation);
153             } finally {
154                 mIgnore = false;
155             }
156         }
157 
158         validatePage();
159     }
160 
setUseCustomLocation(boolean en)161     private void setUseCustomLocation(boolean en) {
162         mValues.useDefaultLocation = !en;
163         mUseDefaultLocationToggle.setSelection(!en);
164         if (!en) {
165             updateProjectLocation(mValues.projectName);
166         }
167 
168         mLocationLabel.setEnabled(en);
169         mLocationText.setEnabled(en);
170         mChooseLocationButton.setEnabled(en);
171     }
172 
init(IStructuredSelection selection, IWorkbenchPart activePart)173     void init(IStructuredSelection selection, IWorkbenchPart activePart) {
174         setWorkingSets(WorkingSetHelper.getSelectedWorkingSet(selection, activePart));
175     }
176 
177     /**
178      * Returns the working sets to which the new project should be added.
179      *
180      * @return the selected working sets to which the new project should be added
181      */
getWorkingSets()182     private IWorkingSet[] getWorkingSets() {
183         return mWorkingSetGroup.getSelectedWorkingSets();
184     }
185 
186     /**
187      * Sets the working sets to which the new project should be added.
188      *
189      * @param workingSets the initial selected working sets
190      */
setWorkingSets(IWorkingSet[] workingSets)191     private void setWorkingSets(IWorkingSet[] workingSets) {
192         assert workingSets != null;
193         mWorkingSetGroup.setWorkingSets(workingSets);
194     }
195 
196     @Override
getNextPage()197     public IWizardPage getNextPage() {
198         // Sync working set data to the value object, since the WorkingSetGroup
199         // doesn't let us add listeners to do this lazily
200         mValues.workingSets = getWorkingSets();
201 
202         return super.getNextPage();
203     }
204 
205     // ---- Implements ModifyListener ----
206 
207     @Override
modifyText(ModifyEvent e)208     public void modifyText(ModifyEvent e) {
209         if (mIgnore) {
210             return;
211         }
212 
213         Object source = e.getSource();
214         if (source == mLocationText) {
215             mValues.projectLocation = mLocationText.getText().trim();
216         }
217 
218         validatePage();
219     }
220 
221 
222     /** If the project should be created in the workspace, then update the project location
223      * based on the project name. */
updateProjectLocation(String projectName)224     private void updateProjectLocation(String projectName) {
225         if (projectName == null) {
226             projectName = "";
227         }
228 
229         boolean useDefaultLocation = mUseDefaultLocationToggle.getSelection();
230 
231         if (useDefaultLocation) {
232             IPath workspace = Platform.getLocation();
233             String projectLocation = workspace.append(projectName).toOSString();
234             mLocationText.setText(projectLocation);
235             mValues.projectLocation = projectLocation;
236         }
237     }
238 
239     // ---- Implements SelectionListener ----
240 
241     @Override
widgetSelected(SelectionEvent e)242     public void widgetSelected(SelectionEvent e) {
243         if (mIgnore) {
244             return;
245         }
246 
247         Object source = e.getSource();
248         if (source == mCustomIconToggle) {
249             mValues.createIcon = mCustomIconToggle.getSelection();
250         } else if (source == mLibraryToggle) {
251             mValues.isLibrary = mLibraryToggle.getSelection();
252         } else if (source == mCreateActivityToggle) {
253             mValues.createActivity = mCreateActivityToggle.getSelection();
254         } else if (source == mUseDefaultLocationToggle) {
255             boolean useDefault = mUseDefaultLocationToggle.getSelection();
256             setUseCustomLocation(!useDefault);
257         } else if (source == mChooseLocationButton) {
258             String dir = promptUserForLocation(getShell());
259             if (dir != null) {
260                 mLocationText.setText(dir);
261                 mValues.projectLocation = dir;
262             }
263         }
264 
265         validatePage();
266     }
267 
promptUserForLocation(Shell shell)268     private String promptUserForLocation(Shell shell) {
269         DirectoryDialog dd = new DirectoryDialog(getShell());
270         dd.setMessage("Select folder where project should be created");
271 
272         String curLocation = mLocationText.getText().trim();
273         if (!curLocation.isEmpty()) {
274             dd.setFilterPath(curLocation);
275         } else if (sLastProjectLocation != null) {
276             dd.setFilterPath(sLastProjectLocation);
277         }
278 
279         String dir = dd.open();
280         if (dir != null) {
281             sLastProjectLocation = dir;
282         }
283 
284         return dir;
285     }
286 
287     @Override
widgetDefaultSelected(SelectionEvent e)288     public void widgetDefaultSelected(SelectionEvent e) {
289     }
290 
291     // ---- Implements FocusListener ----
292 
293     @Override
focusGained(FocusEvent e)294     public void focusGained(FocusEvent e) {
295     }
296 
297     @Override
focusLost(FocusEvent e)298     public void focusLost(FocusEvent e) {
299     }
300 
301     // Validation
302 
validatePage()303     void validatePage() {
304         IStatus status = validateProjectLocation();
305 
306         setPageComplete(status == null || status.getSeverity() != IStatus.ERROR);
307         if (status != null) {
308             setMessage(status.getMessage(),
309                     status.getSeverity() == IStatus.ERROR
310                         ? IMessageProvider.ERROR : IMessageProvider.WARNING);
311         } else {
312             setErrorMessage(null);
313             setMessage(null);
314         }
315     }
316 
validateLocationInWorkspace(NewProjectWizardState values)317     static IStatus validateLocationInWorkspace(NewProjectWizardState values) {
318         if (values.useDefaultLocation) {
319             return null;
320         }
321 
322         // Validate location
323         if (values.projectName != null) {
324             File dest = Platform.getLocation().append(values.projectName).toFile();
325             if (dest.exists()) {
326                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, String.format(
327                    "There is already a file or directory named \"%1$s\" in the selected location.",
328                         values.projectName));
329             }
330         }
331 
332         return null;
333     }
334 
validateProjectLocation()335     private IStatus validateProjectLocation() {
336         if (mValues.useDefaultLocation) {
337             return validateLocationInWorkspace(mValues);
338         }
339 
340         String location = mLocationText.getText();
341         if (location.trim().isEmpty()) {
342             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
343                     "Provide a valid file system location where the project should be created.");
344         }
345 
346         File f = new File(location);
347         if (f.exists()) {
348             if (!f.isDirectory()) {
349                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
350                         String.format("'%s' is not a valid folder.", location));
351             }
352 
353             File[] children = f.listFiles();
354             if (children != null && children.length > 0) {
355                 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
356                         String.format("Folder '%s' is not empty.", location));
357             }
358         }
359 
360         // if the folder doesn't exist, then make sure that the parent
361         // exists and is a writable folder
362         File parent = f.getParentFile();
363         if (!parent.exists()) {
364             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
365                     String.format("Folder '%s' does not exist.", parent.getName()));
366         }
367 
368         if (!parent.isDirectory()) {
369             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
370                     String.format("'%s' is not a folder.", parent.getName()));
371         }
372 
373         if (!parent.canWrite()) {
374             return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID,
375                     String.format("'%s' is not writeable.", parent.getName()));
376         }
377 
378         return null;
379     }
380 }
381