Home | History | Annotate | Download | only in newxmlfile
      1 /*
      2  * Copyright (C) 2011 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.newxmlfile;
     17 
     18 import com.android.SdkConstants;
     19 import com.android.ide.common.resources.configuration.ResourceQualifier;
     20 import com.android.ide.eclipse.adt.AdtConstants;
     21 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
     22 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
     23 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
     24 import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileCreationPage.TypeInfo;
     25 import com.android.ide.eclipse.adt.internal.wizards.newxmlfile.NewXmlFileWizard.Values;
     26 
     27 import org.eclipse.core.resources.IFile;
     28 import org.eclipse.jface.dialogs.IMessageProvider;
     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.ModifyEvent;
     33 import org.eclipse.swt.events.ModifyListener;
     34 import org.eclipse.swt.layout.GridData;
     35 import org.eclipse.swt.layout.GridLayout;
     36 import org.eclipse.swt.widgets.Composite;
     37 import org.eclipse.swt.widgets.Label;
     38 import org.eclipse.swt.widgets.Text;
     39 
     40 /**
     41  * Second page of the {@link NewXmlFileWizard}.
     42  * <p>
     43  * This page is used for choosing the current configuration or specific resource
     44  * folder.
     45  */
     46 public class ChooseConfigurationPage extends WizardPage {
     47     private Values mValues;
     48     private Text mWsFolderPathTextField;
     49     private ConfigurationSelector mConfigSelector;
     50     private boolean mInternalWsFolderPathUpdate;
     51     private boolean mInternalConfigSelectorUpdate;
     52 
     53     /** Absolute destination folder root, e.g. "/res/" */
     54     static final String RES_FOLDER_ABS = AdtConstants.WS_RESOURCES + AdtConstants.WS_SEP;
     55     /** Relative destination folder root, e.g. "res/" */
     56     static final String RES_FOLDER_REL = SdkConstants.FD_RESOURCES + AdtConstants.WS_SEP;
     57 
     58     /**
     59      * Create the wizard.
     60      *
     61      * @param values value object holding current wizard state
     62      */
     63     public ChooseConfigurationPage(NewXmlFileWizard.Values values) {
     64         super("chooseConfig");
     65         mValues = values;
     66         setTitle("Choose Configuration Folder");
     67     }
     68 
     69     @Override
     70     public void setVisible(boolean visible) {
     71         super.setVisible(visible);
     72         if (visible) {
     73             if (mValues.folderPath != null) {
     74                 mWsFolderPathTextField.setText(mValues.folderPath);
     75             }
     76         }
     77     }
     78 
     79     @Override
     80     public void createControl(Composite parent) {
     81         // This UI is maintained with WindowBuilder.
     82 
     83         Composite composite = new Composite(parent, SWT.NULL);
     84         composite.setLayout(new GridLayout(2, false /* makeColumnsEqualWidth */));
     85         composite.setLayoutData(new GridData(GridData.FILL_BOTH));
     86 
     87         // label before configuration selector
     88         Label label = new Label(composite, SWT.NONE);
     89         label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
     90         label.setText("Optional: Choose a specific configuration to limit the XML to:");
     91 
     92         // configuration selector
     93         mConfigSelector = new ConfigurationSelector(composite, SelectorMode.DEFAULT);
     94         GridData gd = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
     95         gd.verticalAlignment = SWT.FILL;
     96         gd.horizontalAlignment = SWT.FILL;
     97         gd.horizontalSpan = 2;
     98         gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
     99         mConfigSelector.setLayoutData(gd);
    100         mConfigSelector.setOnChangeListener(new ConfigurationChangeListener());
    101 
    102         // Folder name: [text]
    103         String tooltip = "The folder where the file will be generated, relative to the project.";
    104 
    105         Label separator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
    106         GridData gdSeparator = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1);
    107         gdSeparator.heightHint = 10;
    108         separator.setLayoutData(gdSeparator);
    109         Label folderLabel = new Label(composite, SWT.NONE);
    110         folderLabel.setText("Folder:");
    111         folderLabel.setToolTipText(tooltip);
    112 
    113         mWsFolderPathTextField = new Text(composite, SWT.BORDER);
    114         mWsFolderPathTextField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    115         mWsFolderPathTextField.setToolTipText(tooltip);
    116         mWsFolderPathTextField.addModifyListener(new ModifyListener() {
    117             @Override
    118             public void modifyText(ModifyEvent e) {
    119                 onWsFolderPathUpdated();
    120             }
    121         });
    122 
    123         setControl(composite);
    124 
    125         mConfigSelector.setConfiguration(mValues.configuration);
    126     }
    127 
    128     /**
    129      * Callback called when the Folder text field is changed, either programmatically
    130      * or by the user.
    131      */
    132     private void onWsFolderPathUpdated() {
    133         if (mInternalWsFolderPathUpdate) {
    134             return;
    135         }
    136 
    137         String wsFolderPath = mWsFolderPathTextField.getText();
    138 
    139         // This is a custom path, we need to sanitize it.
    140         // First it should start with "/res/". Then we need to make sure there are no
    141         // relative paths, things like "../" or "./" or even "//".
    142         wsFolderPath = wsFolderPath.replaceAll("/+\\.\\./+|/+\\./+|//+|\\\\+|^/+", "/");  //$NON-NLS-1$ //$NON-NLS-2$
    143         wsFolderPath = wsFolderPath.replaceAll("^\\.\\./+|^\\./+", "");                   //$NON-NLS-1$ //$NON-NLS-2$
    144         wsFolderPath = wsFolderPath.replaceAll("/+\\.\\.$|/+\\.$|/+$", "");               //$NON-NLS-1$ //$NON-NLS-2$
    145 
    146         // We get "res/foo" from selections relative to the project when we want a "/res/foo" path.
    147         if (wsFolderPath.startsWith(RES_FOLDER_REL)) {
    148             wsFolderPath = RES_FOLDER_ABS + wsFolderPath.substring(RES_FOLDER_REL.length());
    149 
    150             mInternalWsFolderPathUpdate = true;
    151             mWsFolderPathTextField.setText(wsFolderPath);
    152             mInternalWsFolderPathUpdate = false;
    153         }
    154 
    155         mValues.folderPath = wsFolderPath;
    156 
    157         if (wsFolderPath.startsWith(RES_FOLDER_ABS)) {
    158             wsFolderPath = wsFolderPath.substring(RES_FOLDER_ABS.length());
    159 
    160             int pos = wsFolderPath.indexOf(AdtConstants.WS_SEP_CHAR);
    161             if (pos >= 0) {
    162                 wsFolderPath = wsFolderPath.substring(0, pos);
    163             }
    164 
    165             String[] folderSegments = wsFolderPath.split(SdkConstants.RES_QUALIFIER_SEP);
    166 
    167             if (folderSegments.length > 0) {
    168                 String folderName = folderSegments[0];
    169 
    170                 // update config selector
    171                 mInternalConfigSelectorUpdate = true;
    172                 mConfigSelector.setConfiguration(folderSegments);
    173                 mInternalConfigSelectorUpdate = false;
    174 
    175                 IWizardPage previous = ((NewXmlFileWizard) getWizard()).getPreviousPage(this);
    176                 if (previous instanceof NewXmlFileCreationPage) {
    177                     NewXmlFileCreationPage p = (NewXmlFileCreationPage) previous;
    178                     p.selectTypeFromFolder(folderName);
    179                 }
    180             }
    181         }
    182 
    183         validatePage();
    184     }
    185 
    186     /**
    187      * Callback called when the configuration has changed in the {@link ConfigurationSelector}.
    188      */
    189     private class ConfigurationChangeListener implements Runnable {
    190         @Override
    191         public void run() {
    192             if (mInternalConfigSelectorUpdate) {
    193                 return;
    194             }
    195 
    196             resetFolderPath(true /*validate*/);
    197         }
    198     }
    199 
    200     /**
    201      * Reset the current Folder path based on the UI selection
    202      * @param validate if true, force a call to {@link #validatePage()}.
    203      */
    204     private void resetFolderPath(boolean validate) {
    205         TypeInfo type = mValues.type;
    206         if (type != null) {
    207             mConfigSelector.getConfiguration(mValues.configuration);
    208             StringBuilder sb = new StringBuilder(RES_FOLDER_ABS);
    209             sb.append(mValues.configuration.getFolderName(type.getResFolderType()));
    210 
    211             mInternalWsFolderPathUpdate = true;
    212             String newPath = sb.toString();
    213             mValues.folderPath = newPath;
    214             mWsFolderPathTextField.setText(newPath);
    215             mInternalWsFolderPathUpdate = false;
    216 
    217             if (validate) {
    218                 validatePage();
    219             }
    220         }
    221     }
    222 
    223     /**
    224      * Returns the destination folder path relative to the project or an empty string.
    225      *
    226      * @return the currently edited folder
    227      */
    228     public String getWsFolderPath() {
    229         return mWsFolderPathTextField == null ? "" : mWsFolderPathTextField.getText(); //$NON-NLS-1$
    230     }
    231 
    232     /**
    233      * Validates the fields, displays errors and warnings.
    234      * Enables the finish button if there are no errors.
    235      */
    236     private void validatePage() {
    237         String error = null;
    238         String warning = null;
    239 
    240         // -- validate folder configuration
    241         if (error == null) {
    242             ConfigurationState state = mConfigSelector.getState();
    243             if (state == ConfigurationState.INVALID_CONFIG) {
    244                 ResourceQualifier qual = mConfigSelector.getInvalidQualifier();
    245                 if (qual != null) {
    246                     error =
    247                       String.format("The qualifier '%1$s' is invalid in the folder configuration.",
    248                             qual.getName());
    249                 }
    250             } else if (state == ConfigurationState.REGION_WITHOUT_LANGUAGE) {
    251                 error = "The Region qualifier requires the Language qualifier.";
    252             }
    253         }
    254 
    255         // -- validate generated path
    256         if (error == null) {
    257             String wsFolderPath = getWsFolderPath();
    258             if (!wsFolderPath.startsWith(RES_FOLDER_ABS)) {
    259                 error = String.format("Target folder must start with %1$s.", RES_FOLDER_ABS);
    260             }
    261         }
    262 
    263         // -- validate destination file doesn't exist
    264         if (error == null) {
    265             IFile file = mValues.getDestinationFile();
    266             if (file != null && file.exists()) {
    267                 warning = "The destination file already exists";
    268             }
    269         }
    270 
    271         // -- update UI & enable finish if there's no error
    272         setPageComplete(error == null);
    273         if (error != null) {
    274             setMessage(error, IMessageProvider.ERROR);
    275         } else if (warning != null) {
    276             setMessage(warning, IMessageProvider.WARNING);
    277         } else {
    278             setErrorMessage(null);
    279             setMessage(null);
    280         }
    281     }
    282 }
    283