Home | History | Annotate | Download | only in configuration
      1 /*
      2  * Copyright (C) 2009 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 
     17 package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
     18 
     19 import com.android.ide.common.resources.configuration.FolderConfiguration;
     20 import com.android.ide.common.resources.configuration.LanguageQualifier;
     21 import com.android.ide.common.resources.configuration.NightModeQualifier;
     22 import com.android.ide.common.resources.configuration.RegionQualifier;
     23 import com.android.ide.common.resources.configuration.ResourceQualifier;
     24 import com.android.ide.common.resources.configuration.UiModeQualifier;
     25 import com.android.ide.common.resources.configuration.VersionQualifier;
     26 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
     27 import com.android.ide.eclipse.adt.internal.sdk.LayoutDevice;
     28 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector;
     29 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.ConfigurationState;
     30 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.IQualifierFilter;
     31 import com.android.ide.eclipse.adt.internal.ui.ConfigurationSelector.SelectorMode;
     32 import com.android.sdkuilib.ui.GridDialog;
     33 
     34 import org.eclipse.jface.dialogs.Dialog;
     35 import org.eclipse.jface.dialogs.IDialogConstants;
     36 import org.eclipse.swt.SWT;
     37 import org.eclipse.swt.events.ModifyEvent;
     38 import org.eclipse.swt.events.ModifyListener;
     39 import org.eclipse.swt.events.VerifyEvent;
     40 import org.eclipse.swt.events.VerifyListener;
     41 import org.eclipse.swt.graphics.Image;
     42 import org.eclipse.swt.layout.GridData;
     43 import org.eclipse.swt.layout.GridLayout;
     44 import org.eclipse.swt.widgets.Composite;
     45 import org.eclipse.swt.widgets.Control;
     46 import org.eclipse.swt.widgets.Group;
     47 import org.eclipse.swt.widgets.Label;
     48 import org.eclipse.swt.widgets.Shell;
     49 import org.eclipse.swt.widgets.Text;
     50 
     51 import java.text.DecimalFormat;
     52 import java.text.DecimalFormatSymbols;
     53 import java.text.ParseException;
     54 import java.util.regex.Pattern;
     55 
     56 /**
     57  * Dialog to edit both a {@link LayoutDevice}, and a {@link FolderConfiguration} at the same time.
     58  */
     59 public class ConfigEditDialog extends GridDialog {
     60 
     61     private static final Pattern FLOAT_PATTERN;
     62     static {
     63         // get the decimal separator char
     64         DecimalFormatSymbols dfs = new DecimalFormatSymbols();
     65 
     66         // make a pattern that's basically ##.# where .# is optional and where . can be . or ,
     67         // depending on the locale
     68         FLOAT_PATTERN = Pattern.compile(
     69             "\\d*(" +                                                       //$NON-NLS-1$
     70             Pattern.quote(new String(new char[] { dfs.getDecimalSeparator() })) +
     71             "\\d?)?");                                                      //$NON-NLS-1$
     72     }
     73 
     74     private final FolderConfiguration mConfig = new FolderConfiguration();
     75 
     76     private ConfigurationSelector mConfigSelector;
     77     private Composite mStatusComposite;
     78     private Label mStatusLabel;
     79     private Label mStatusImage;
     80 
     81     private Image mError;
     82 
     83     private String mDeviceName;
     84     private String mConfigName;
     85     private float mXDpi = Float.NaN;
     86     private float mYDpi = Float.NaN;
     87 
     88     private final DecimalFormat mDecimalFormat = new DecimalFormat();
     89 
     90 
     91     public ConfigEditDialog(Shell parentShell, FolderConfiguration config) {
     92         super(parentShell, 1, false);
     93         mConfig.set(config);
     94     }
     95 
     96     public void setDeviceName(String name) {
     97         mDeviceName = name;
     98     }
     99 
    100     public String getDeviceName() {
    101         return mDeviceName;
    102     }
    103 
    104     public void setXDpi(float xdpi) {
    105         mXDpi = xdpi;
    106     }
    107 
    108     public float getXDpi() {
    109         return mXDpi;
    110     }
    111 
    112     public void setYDpi(float ydpi) {
    113         mYDpi = ydpi;
    114     }
    115 
    116     public float getYDpi() {
    117         return mYDpi;
    118     }
    119 
    120     public void setConfigName(String name) {
    121         mConfigName = name;
    122     }
    123 
    124     public String getConfigName() {
    125         return mConfigName;
    126     }
    127 
    128     public void setConfig(FolderConfiguration config) {
    129         mConfig.set(config);
    130     }
    131 
    132     public void getConfig(FolderConfiguration config) {
    133         config.set(mConfig);
    134         config.updateScreenWidthAndHeight();
    135     }
    136 
    137     @Override
    138     public void createDialogContent(Composite parent) {
    139         mError = IconFactory.getInstance().getIcon("error"); //$NON-NLS-1$
    140 
    141         Group deviceGroup = new Group(parent, SWT.NONE);
    142         deviceGroup.setText("Device");
    143         deviceGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
    144         deviceGroup.setLayout(new GridLayout(2, false));
    145 
    146         Label l = new Label(deviceGroup, SWT.None);
    147         l.setText("Name");
    148 
    149         final Text deviceNameText = new Text(deviceGroup, SWT.BORDER);
    150         deviceNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    151         if (mDeviceName != null) {
    152             deviceNameText.setText(mDeviceName);
    153         }
    154         deviceNameText.addModifyListener(new ModifyListener() {
    155             @Override
    156             public void modifyText(ModifyEvent e) {
    157                 mDeviceName = deviceNameText.getText().trim();
    158                 validateOk();
    159             }
    160         });
    161 
    162 
    163         VerifyListener floatVerifier = new VerifyListener() {
    164             @Override
    165             public void verifyText(VerifyEvent event) {
    166                 // combine the current content and the new text
    167                 String text = ((Text)event.widget).getText();
    168                 text = text.substring(0, event.start) + event.text + text.substring(event.end);
    169 
    170                 // now make sure it's a match for the regex
    171                 event.doit = FLOAT_PATTERN.matcher(text).matches();
    172             }
    173         };
    174 
    175         l = new Label(deviceGroup, SWT.None);
    176         l.setText("x dpi");
    177 
    178         final Text deviceXDpiText = new Text(deviceGroup, SWT.BORDER);
    179         deviceXDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    180         if (Float.isNaN(mXDpi) == false) {
    181             deviceXDpiText.setText(String.format("%.1f", mXDpi)); //$NON-NLS-1$
    182         }
    183         deviceXDpiText.addVerifyListener(floatVerifier);
    184         deviceXDpiText.addModifyListener(new ModifyListener() {
    185             @Override
    186             public void modifyText(ModifyEvent e) {
    187                 String value = deviceXDpiText.getText();
    188                 if (value.length() == 0) {
    189                     mXDpi = Float.NaN;
    190                 } else {
    191                     try {
    192                         mXDpi = mDecimalFormat.parse(value).floatValue();
    193                     } catch (ParseException exception) {
    194                         mXDpi = Float.NaN;
    195                     }
    196                 }
    197             }
    198         });
    199 
    200         l = new Label(deviceGroup, SWT.None);
    201         l.setText("y dpi");
    202 
    203         final Text deviceYDpiText = new Text(deviceGroup, SWT.BORDER);
    204         deviceYDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    205         if (Float.isNaN(mYDpi) == false) {
    206             deviceYDpiText.setText(String.format("%.1f", mYDpi)); //$NON-NLS-1$
    207         }
    208         deviceYDpiText.addVerifyListener(floatVerifier);
    209         deviceYDpiText.addModifyListener(new ModifyListener() {
    210             @Override
    211             public void modifyText(ModifyEvent e) {
    212                 String value = deviceYDpiText.getText();
    213                 if (value.length() == 0) {
    214                     mYDpi = Float.NaN;
    215                 } else {
    216                     try {
    217                         mYDpi = mDecimalFormat.parse(value).floatValue();
    218                     } catch (ParseException exception) {
    219                         mYDpi = Float.NaN;
    220                     }
    221                 }
    222             }
    223         });
    224 
    225         Group configGroup = new Group(parent, SWT.NONE);
    226         configGroup.setText("Configuration");
    227         configGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
    228         configGroup.setLayout(new GridLayout(2, false));
    229 
    230         l = new Label(configGroup, SWT.None);
    231         l.setText("Name");
    232 
    233         final Text configNameText = new Text(configGroup, SWT.BORDER);
    234         configNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    235         if (mConfigName != null) {
    236             configNameText.setText(mConfigName);
    237         }
    238         configNameText.addModifyListener(new ModifyListener() {
    239             @Override
    240             public void modifyText(ModifyEvent e) {
    241                 mConfigName = configNameText.getText().trim();
    242                 validateOk();
    243             }
    244         });
    245 
    246         mConfigSelector = new ConfigurationSelector(configGroup, SelectorMode.DEVICE_ONLY);
    247         // configure the selector to be in "device mode" and not accept language/region/version
    248         // since those are selected from a different combo
    249         // FIXME: add version combo.
    250         mConfigSelector.setQualifierFilter(new IQualifierFilter() {
    251             @Override
    252             public boolean accept(ResourceQualifier qualifier) {
    253                 if (qualifier instanceof LanguageQualifier ||
    254                         qualifier instanceof RegionQualifier ||
    255                         qualifier instanceof UiModeQualifier ||
    256                         qualifier instanceof NightModeQualifier ||
    257                         qualifier instanceof VersionQualifier) {
    258                     return false;
    259                 }
    260 
    261                 return true;
    262             }
    263         });
    264         mConfigSelector.setConfiguration(mConfig);
    265         GridData gd;
    266         mConfigSelector.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    267         gd.horizontalSpan = 2;
    268         gd.widthHint = ConfigurationSelector.WIDTH_HINT;
    269         gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
    270 
    271         // add a listener to check on the validity of the FolderConfiguration as
    272         // they are built.
    273         mConfigSelector.setOnChangeListener(new Runnable() {
    274             @Override
    275             public void run() {
    276                 if (mConfigSelector.getState() == ConfigurationState.OK) {
    277                     mConfigSelector.getConfiguration(mConfig);
    278                 }
    279 
    280                 validateOk();
    281             }
    282         });
    283 
    284         mStatusComposite = new Composite(parent, SWT.NONE);
    285         mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    286         GridLayout gl = new GridLayout(2, false);
    287         mStatusComposite.setLayout(gl);
    288         gl.marginHeight = gl.marginWidth = 0;
    289 
    290         mStatusImage = new Label(mStatusComposite, SWT.NONE);
    291         mStatusLabel = new Label(mStatusComposite, SWT.NONE);
    292         mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    293         resetStatus();
    294     }
    295 
    296     @Override
    297     protected Control createContents(Composite parent) {
    298         Control c = super.createContents(parent);
    299         validateOk();
    300         return c;
    301     }
    302 
    303     /**
    304      * resets the status label to show the file that will be created.
    305      */
    306     private void resetStatus() {
    307         String displayString = Dialog.shortenText(
    308                 String.format("Config: %1$s", mConfig.toString()),
    309                 mStatusLabel);
    310         mStatusLabel.setText(displayString);
    311     }
    312 
    313     private void setError(String text) {
    314         String displayString = Dialog.shortenText(text, mStatusLabel);
    315         mStatusLabel.setText(displayString);
    316         mStatusImage.setImage(mError);
    317         getButton(IDialogConstants.OK_ID).setEnabled(false);
    318     }
    319 
    320     private void validateOk() {
    321         // check the device name
    322         if (mDeviceName == null || mDeviceName.length() == 0) {
    323             setError("Device name must not be empty");
    324             return;
    325         }
    326 
    327         // check the config name
    328         if (mConfigName == null || mConfigName.length() == 0) {
    329             setError("Configuration name must not be empty");
    330             return;
    331         }
    332 
    333         // and check the config itself
    334         ConfigurationState state = mConfigSelector.getState();
    335 
    336         switch (state) {
    337             case INVALID_CONFIG:
    338                 ResourceQualifier invalidQualifier = mConfigSelector.getInvalidQualifier();
    339                 setError(String.format(
    340                         "Invalid Configuration: %1$s has no filter set.",
    341                         invalidQualifier.getName()));
    342                 return;
    343             case REGION_WITHOUT_LANGUAGE:
    344                 setError("The Region qualifier requires the Language qualifier.");
    345                 return;
    346         }
    347 
    348         // no error
    349         mStatusImage.setImage(null);
    350         resetStatus();
    351         getButton(IDialogConstants.OK_ID).setEnabled(true);
    352 
    353         // need to relayout, because of the change in size in mErrorImage.
    354         mStatusComposite.layout();
    355     }
    356 }
    357