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             public void modifyText(ModifyEvent e) {
    156                 mDeviceName = deviceNameText.getText().trim();
    157                 validateOk();
    158             }
    159         });
    160 
    161 
    162         VerifyListener floatVerifier = new VerifyListener() {
    163             public void verifyText(VerifyEvent event) {
    164                 // combine the current content and the new text
    165                 String text = ((Text)event.widget).getText();
    166                 text = text.substring(0, event.start) + event.text + text.substring(event.end);
    167 
    168                 // now make sure it's a match for the regex
    169                 event.doit = FLOAT_PATTERN.matcher(text).matches();
    170             }
    171         };
    172 
    173         l = new Label(deviceGroup, SWT.None);
    174         l.setText("x dpi");
    175 
    176         final Text deviceXDpiText = new Text(deviceGroup, SWT.BORDER);
    177         deviceXDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    178         if (Float.isNaN(mXDpi) == false) {
    179             deviceXDpiText.setText(String.format("%.1f", mXDpi)); //$NON-NLS-1$
    180         }
    181         deviceXDpiText.addVerifyListener(floatVerifier);
    182         deviceXDpiText.addModifyListener(new ModifyListener() {
    183             public void modifyText(ModifyEvent e) {
    184                 String value = deviceXDpiText.getText();
    185                 if (value.length() == 0) {
    186                     mXDpi = Float.NaN;
    187                 } else {
    188                     try {
    189                         mXDpi = mDecimalFormat.parse(value).floatValue();
    190                     } catch (ParseException exception) {
    191                         mXDpi = Float.NaN;
    192                     }
    193                 }
    194             }
    195         });
    196 
    197         l = new Label(deviceGroup, SWT.None);
    198         l.setText("y dpi");
    199 
    200         final Text deviceYDpiText = new Text(deviceGroup, SWT.BORDER);
    201         deviceYDpiText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    202         if (Float.isNaN(mYDpi) == false) {
    203             deviceYDpiText.setText(String.format("%.1f", mYDpi)); //$NON-NLS-1$
    204         }
    205         deviceYDpiText.addVerifyListener(floatVerifier);
    206         deviceYDpiText.addModifyListener(new ModifyListener() {
    207             public void modifyText(ModifyEvent e) {
    208                 String value = deviceYDpiText.getText();
    209                 if (value.length() == 0) {
    210                     mYDpi = Float.NaN;
    211                 } else {
    212                     try {
    213                         mYDpi = mDecimalFormat.parse(value).floatValue();
    214                     } catch (ParseException exception) {
    215                         mYDpi = Float.NaN;
    216                     }
    217                 }
    218             }
    219         });
    220 
    221         Group configGroup = new Group(parent, SWT.NONE);
    222         configGroup.setText("Configuration");
    223         configGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
    224         configGroup.setLayout(new GridLayout(2, false));
    225 
    226         l = new Label(configGroup, SWT.None);
    227         l.setText("Name");
    228 
    229         final Text configNameText = new Text(configGroup, SWT.BORDER);
    230         configNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    231         if (mConfigName != null) {
    232             configNameText.setText(mConfigName);
    233         }
    234         configNameText.addModifyListener(new ModifyListener() {
    235             public void modifyText(ModifyEvent e) {
    236                 mConfigName = configNameText.getText().trim();
    237                 validateOk();
    238             }
    239         });
    240 
    241         mConfigSelector = new ConfigurationSelector(configGroup, SelectorMode.DEVICE_ONLY);
    242         // configure the selector to be in "device mode" and not accept language/region/version
    243         // since those are selected from a different combo
    244         // FIXME: add version combo.
    245         mConfigSelector.setQualifierFilter(new IQualifierFilter() {
    246             public boolean accept(ResourceQualifier qualifier) {
    247                 if (qualifier instanceof LanguageQualifier ||
    248                         qualifier instanceof RegionQualifier ||
    249                         qualifier instanceof UiModeQualifier ||
    250                         qualifier instanceof NightModeQualifier ||
    251                         qualifier instanceof VersionQualifier) {
    252                     return false;
    253                 }
    254 
    255                 return true;
    256             }
    257         });
    258         mConfigSelector.setConfiguration(mConfig);
    259         GridData gd;
    260         mConfigSelector.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
    261         gd.horizontalSpan = 2;
    262         gd.widthHint = ConfigurationSelector.WIDTH_HINT;
    263         gd.heightHint = ConfigurationSelector.HEIGHT_HINT;
    264 
    265         // add a listener to check on the validity of the FolderConfiguration as
    266         // they are built.
    267         mConfigSelector.setOnChangeListener(new Runnable() {
    268             public void run() {
    269                 if (mConfigSelector.getState() == ConfigurationState.OK) {
    270                     mConfigSelector.getConfiguration(mConfig);
    271                 }
    272 
    273                 validateOk();
    274             }
    275         });
    276 
    277         mStatusComposite = new Composite(parent, SWT.NONE);
    278         mStatusComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    279         GridLayout gl = new GridLayout(2, false);
    280         mStatusComposite.setLayout(gl);
    281         gl.marginHeight = gl.marginWidth = 0;
    282 
    283         mStatusImage = new Label(mStatusComposite, SWT.NONE);
    284         mStatusLabel = new Label(mStatusComposite, SWT.NONE);
    285         mStatusLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
    286         resetStatus();
    287     }
    288 
    289     @Override
    290     protected Control createContents(Composite parent) {
    291         Control c = super.createContents(parent);
    292         validateOk();
    293         return c;
    294     }
    295 
    296     /**
    297      * resets the status label to show the file that will be created.
    298      */
    299     private void resetStatus() {
    300         String displayString = Dialog.shortenText(
    301                 String.format("Config: %1$s", mConfig.toString()),
    302                 mStatusLabel);
    303         mStatusLabel.setText(displayString);
    304     }
    305 
    306     private void setError(String text) {
    307         String displayString = Dialog.shortenText(text, mStatusLabel);
    308         mStatusLabel.setText(displayString);
    309         mStatusImage.setImage(mError);
    310         getButton(IDialogConstants.OK_ID).setEnabled(false);
    311     }
    312 
    313     private void validateOk() {
    314         // check the device name
    315         if (mDeviceName == null || mDeviceName.length() == 0) {
    316             setError("Device name must not be empty");
    317             return;
    318         }
    319 
    320         // check the config name
    321         if (mConfigName == null || mConfigName.length() == 0) {
    322             setError("Configuration name must not be empty");
    323             return;
    324         }
    325 
    326         // and check the config itself
    327         ConfigurationState state = mConfigSelector.getState();
    328 
    329         switch (state) {
    330             case INVALID_CONFIG:
    331                 ResourceQualifier invalidQualifier = mConfigSelector.getInvalidQualifier();
    332                 setError(String.format(
    333                         "Invalid Configuration: %1$s has no filter set.",
    334                         invalidQualifier.getName()));
    335                 return;
    336             case REGION_WITHOUT_LANGUAGE:
    337                 setError("The Region qualifier requires the Language qualifier.");
    338                 return;
    339         }
    340 
    341         // no error
    342         mStatusImage.setImage(null);
    343         resetStatus();
    344         getButton(IDialogConstants.OK_ID).setEnabled(true);
    345 
    346         // need to relayout, because of the change in size in mErrorImage.
    347         mStatusComposite.layout();
    348     }
    349 }
    350