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