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