1/*
2 * Copyright (C) 2008 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
17package com.android.ide.eclipse.adt.internal.ui;
18
19import com.android.SdkConstants;
20import com.android.ide.common.resources.LocaleManager;
21import com.android.ide.common.resources.configuration.CountryCodeQualifier;
22import com.android.ide.common.resources.configuration.DensityQualifier;
23import com.android.ide.common.resources.configuration.FolderConfiguration;
24import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
25import com.android.ide.common.resources.configuration.LanguageQualifier;
26import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
27import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
28import com.android.ide.common.resources.configuration.NavigationStateQualifier;
29import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
30import com.android.ide.common.resources.configuration.NightModeQualifier;
31import com.android.ide.common.resources.configuration.RegionQualifier;
32import com.android.ide.common.resources.configuration.ResourceQualifier;
33import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
34import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
35import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
36import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
37import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
38import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
39import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
40import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
41import com.android.ide.common.resources.configuration.TouchScreenQualifier;
42import com.android.ide.common.resources.configuration.UiModeQualifier;
43import com.android.ide.common.resources.configuration.VersionQualifier;
44import com.android.ide.eclipse.adt.internal.resources.ResourceHelper;
45import com.android.resources.Density;
46import com.android.resources.Keyboard;
47import com.android.resources.KeyboardState;
48import com.android.resources.LayoutDirection;
49import com.android.resources.Navigation;
50import com.android.resources.NavigationState;
51import com.android.resources.NightMode;
52import com.android.resources.ResourceEnum;
53import com.android.resources.ScreenOrientation;
54import com.android.resources.ScreenRatio;
55import com.android.resources.ScreenSize;
56import com.android.resources.TouchScreen;
57import com.android.resources.UiMode;
58
59import org.eclipse.jface.viewers.ILabelProviderListener;
60import org.eclipse.jface.viewers.ISelection;
61import org.eclipse.jface.viewers.ISelectionChangedListener;
62import org.eclipse.jface.viewers.IStructuredContentProvider;
63import org.eclipse.jface.viewers.IStructuredSelection;
64import org.eclipse.jface.viewers.ITableLabelProvider;
65import org.eclipse.jface.viewers.SelectionChangedEvent;
66import org.eclipse.jface.viewers.StructuredSelection;
67import org.eclipse.jface.viewers.TableViewer;
68import org.eclipse.jface.viewers.Viewer;
69import org.eclipse.swt.SWT;
70import org.eclipse.swt.custom.StackLayout;
71import org.eclipse.swt.events.ControlAdapter;
72import org.eclipse.swt.events.ControlEvent;
73import org.eclipse.swt.events.FocusAdapter;
74import org.eclipse.swt.events.FocusEvent;
75import org.eclipse.swt.events.ModifyEvent;
76import org.eclipse.swt.events.ModifyListener;
77import org.eclipse.swt.events.SelectionAdapter;
78import org.eclipse.swt.events.SelectionEvent;
79import org.eclipse.swt.events.SelectionListener;
80import org.eclipse.swt.events.VerifyEvent;
81import org.eclipse.swt.events.VerifyListener;
82import org.eclipse.swt.graphics.Image;
83import org.eclipse.swt.graphics.Rectangle;
84import org.eclipse.swt.layout.GridData;
85import org.eclipse.swt.layout.GridLayout;
86import org.eclipse.swt.widgets.Button;
87import org.eclipse.swt.widgets.Combo;
88import org.eclipse.swt.widgets.Composite;
89import org.eclipse.swt.widgets.Label;
90import org.eclipse.swt.widgets.Table;
91import org.eclipse.swt.widgets.TableColumn;
92import org.eclipse.swt.widgets.Text;
93
94import java.util.ArrayList;
95import java.util.Arrays;
96import java.util.HashMap;
97import java.util.Locale;
98import java.util.Set;
99
100/**
101 * Custom UI widget to let user build a Folder configuration.
102 * <p/>
103 * To use this, instantiate somewhere in the UI and then:
104 * <ul>
105 * <li>Use {@link #setConfiguration(String)} or {@link #setConfiguration(FolderConfiguration)}.
106 * <li>Retrieve the configuration using {@link #getConfiguration(FolderConfiguration)}.
107 * </ul>
108 */
109public class ConfigurationSelector extends Composite {
110
111    public static final int WIDTH_HINT = 600;
112    public static final int HEIGHT_HINT = 250;
113
114    private Runnable mOnChangeListener;
115
116    private TableViewer mFullTableViewer;
117    private TableViewer mSelectionTableViewer;
118    private Button mAddButton;
119    private Button mRemoveButton;
120    private StackLayout mStackLayout;
121
122    private boolean mOnRefresh = false;
123
124    private final FolderConfiguration mBaseConfiguration = new FolderConfiguration();
125    private final FolderConfiguration mSelectedConfiguration = new FolderConfiguration();
126
127    private final HashMap<Class<? extends ResourceQualifier>, QualifierEditBase> mUiMap =
128        new HashMap<Class<? extends ResourceQualifier>, QualifierEditBase>();
129    private final SelectorMode mMode;
130    private Composite mQualifierEditParent;
131    private IQualifierFilter mQualifierFilter;
132
133    /**
134     * Basic of {@link VerifyListener} to only accept digits.
135     */
136    private static class DigitVerifier implements VerifyListener {
137        @Override
138        public void verifyText(VerifyEvent e) {
139            // check for digit only.
140            for (int i = 0 ; i < e.text.length(); i++) {
141                char letter = e.text.charAt(i);
142                if (letter < '0' || letter > '9') {
143                    e.doit = false;
144                    return;
145                }
146            }
147        }
148    }
149
150    /**
151     * Implementation of {@link VerifyListener} for Country Code qualifiers.
152     */
153    public static class MobileCodeVerifier extends DigitVerifier {
154        @Override
155        public void verifyText(VerifyEvent e) {
156            super.verifyText(e);
157
158            // basic tests passed?
159            if (e.doit) {
160                // check the max 3 digits.
161                if (e.text.length() - e.end + e.start +
162                        ((Text)e.getSource()).getText().length() > 3) {
163                    e.doit = false;
164                }
165            }
166        }
167    }
168
169    /**
170     * Implementation of {@link VerifyListener} for the Language and Region qualifiers.
171     */
172    public static class LanguageRegionVerifier implements VerifyListener {
173        @Override
174        public void verifyText(VerifyEvent e) {
175            // check for length
176            if (e.text.length() - e.end + e.start + ((Combo)e.getSource()).getText().length() > 2) {
177                e.doit = false;
178                return;
179            }
180
181            // check for lower case only.
182            for (int i = 0 ; i < e.text.length(); i++) {
183                char letter = e.text.charAt(i);
184                if ((letter < 'a' || letter > 'z') && (letter < 'A' || letter > 'Z')) {
185                    e.doit = false;
186                    return;
187                }
188            }
189        }
190    }
191
192    /**
193     * Implementation of {@link VerifyListener} for the Density qualifier.
194     */
195    public static class DensityVerifier extends DigitVerifier { }
196
197    /**
198     * Implementation of {@link VerifyListener} for the Screen Dimension qualifier.
199     */
200    public static class DimensionVerifier extends DigitVerifier { }
201
202    /**
203     * Enum for the state of the configuration being created.
204     */
205    public enum ConfigurationState {
206        OK, INVALID_CONFIG, REGION_WITHOUT_LANGUAGE;
207    }
208
209    /**
210     * Behavior mode for the Selector.
211     *
212     * @see #DEFAULT
213     * @see #DEVICE_ONLY
214     * @see #CONFIG_ONLY
215     */
216    public enum SelectorMode {
217        /** the default mode */
218        DEFAULT,
219        /** mode forcing the qualifier values to be valid on a device.
220         * For instance {@link Density#NODPI} is a valid qualifier for a resource configuration but
221         * this is not valid on a device */
222        DEVICE_ONLY,
223        /** mode where only the specific config can be edited. The user can only select
224         * which non-empty qualifier to select. */
225        CONFIG_ONLY;
226    }
227
228    /**
229     * A filter for {@link ResourceQualifier}.
230     * @see ConfigurationSelector#setQualifierFilter(IQualifierFilter)
231     */
232    public interface IQualifierFilter {
233        /**
234         * Returns true of the qualifier is accepted.
235         */
236        boolean accept(ResourceQualifier qualifier);
237    }
238
239    /**
240     * Creates the selector.
241     * <p/>
242     * The {@link SelectorMode} changes the behavior of the selector depending on what is being
243     * edited (a device config, a resource config, a given configuration).
244     *
245     * @param parent the composite parent.
246     * @param mode the mode for the selector.
247     */
248    public ConfigurationSelector(Composite parent, SelectorMode mode) {
249        super(parent, SWT.NONE);
250
251        mMode  = mode;
252        mBaseConfiguration.createDefault();
253
254        GridLayout gl = new GridLayout(4, false);
255        gl.marginWidth = gl.marginHeight = 0;
256        setLayout(gl);
257
258        // first column is the first table
259        final Table fullTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
260        fullTable.setLayoutData(new GridData(GridData.FILL_BOTH));
261        fullTable.setHeaderVisible(true);
262        fullTable.setLinesVisible(true);
263
264        // create the column
265        final TableColumn fullTableColumn = new TableColumn(fullTable, SWT.LEFT);
266        // set the header
267        fullTableColumn.setText("Available Qualifiers");
268
269        fullTable.addControlListener(new ControlAdapter() {
270            @Override
271            public void controlResized(ControlEvent e) {
272                Rectangle r = fullTable.getClientArea();
273                fullTableColumn.setWidth(r.width);
274            }
275        });
276
277        mFullTableViewer = new TableViewer(fullTable);
278        mFullTableViewer.setContentProvider(new QualifierContentProvider());
279        // the label provider must return the value of the label only if the mode is
280        // CONFIG_ONLY
281        mFullTableViewer.setLabelProvider(new QualifierLabelProvider(
282                mMode == SelectorMode.CONFIG_ONLY));
283        mFullTableViewer.setInput(mBaseConfiguration);
284        mFullTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
285            @Override
286            public void selectionChanged(SelectionChangedEvent event) {
287                ISelection selection = event.getSelection();
288                if (selection instanceof IStructuredSelection) {
289                    IStructuredSelection structSelection = (IStructuredSelection)selection;
290                    Object first = structSelection.getFirstElement();
291
292                    if (first instanceof ResourceQualifier) {
293                        mAddButton.setEnabled(true);
294                        return;
295                    }
296                }
297
298                mAddButton.setEnabled(false);
299            }
300        });
301
302        // 2nd column is the left/right arrow button
303        Composite buttonComposite = new Composite(this, SWT.NONE);
304        gl = new GridLayout(1, false);
305        gl.marginWidth = gl.marginHeight = 0;
306        buttonComposite.setLayout(gl);
307        buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
308
309        new Composite(buttonComposite, SWT.NONE);
310        mAddButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
311        mAddButton.setText("->");
312        mAddButton.setEnabled(false);
313        mAddButton.addSelectionListener(new SelectionAdapter() {
314            @Override
315            public void widgetSelected(SelectionEvent e) {
316                IStructuredSelection selection =
317                    (IStructuredSelection)mFullTableViewer.getSelection();
318
319                Object first = selection.getFirstElement();
320                if (first instanceof ResourceQualifier) {
321                    ResourceQualifier qualifier = (ResourceQualifier)first;
322
323                    mBaseConfiguration.removeQualifier(qualifier);
324                    mSelectedConfiguration.addQualifier(qualifier);
325
326                    mFullTableViewer.refresh();
327                    mSelectionTableViewer.refresh();
328                    mSelectionTableViewer.setSelection(new StructuredSelection(qualifier), true);
329
330                    onChange(false /* keepSelection */);
331                }
332            }
333        });
334
335        mRemoveButton = new Button(buttonComposite, SWT.BORDER | SWT.PUSH);
336        mRemoveButton.setText("<-");
337        mRemoveButton.setEnabled(false);
338        mRemoveButton.addSelectionListener(new SelectionAdapter() {
339            @Override
340            public void widgetSelected(SelectionEvent e) {
341                IStructuredSelection selection =
342                    (IStructuredSelection)mSelectionTableViewer.getSelection();
343
344                Object first = selection.getFirstElement();
345                if (first instanceof ResourceQualifier) {
346                    ResourceQualifier qualifier = (ResourceQualifier)first;
347
348                    mSelectedConfiguration.removeQualifier(qualifier);
349                    mBaseConfiguration.addQualifier(qualifier);
350
351                    mFullTableViewer.refresh();
352                    mSelectionTableViewer.refresh();
353
354                    onChange(false /* keepSelection */);
355                }
356            }
357        });
358
359        // 3rd column is the selected config table
360        final Table selectionTable = new Table(this, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
361        selectionTable.setLayoutData(new GridData(GridData.FILL_BOTH));
362        selectionTable.setHeaderVisible(true);
363        selectionTable.setLinesVisible(true);
364
365        // create the column
366        final TableColumn selectionTableColumn = new TableColumn(selectionTable, SWT.LEFT);
367        // set the header
368        selectionTableColumn.setText("Chosen Qualifiers");
369
370        selectionTable.addControlListener(new ControlAdapter() {
371            @Override
372            public void controlResized(ControlEvent e) {
373                Rectangle r = selectionTable.getClientArea();
374                selectionTableColumn.setWidth(r.width);
375            }
376        });
377        mSelectionTableViewer = new TableViewer(selectionTable);
378        mSelectionTableViewer.setContentProvider(new QualifierContentProvider());
379        // always show the qualifier value in this case.
380        mSelectionTableViewer.setLabelProvider(new QualifierLabelProvider(
381                true /* showQualifierValue */));
382        mSelectionTableViewer.setInput(mSelectedConfiguration);
383        mSelectionTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
384            @Override
385            public void selectionChanged(SelectionChangedEvent event) {
386                // ignore selection changes during resfreshes in some cases.
387                if (mOnRefresh) {
388                    return;
389                }
390
391                ISelection selection = event.getSelection();
392                if (selection instanceof IStructuredSelection) {
393                    IStructuredSelection structSelection = (IStructuredSelection)selection;
394
395                    if (structSelection.isEmpty() == false) {
396                        Object first = structSelection.getFirstElement();
397
398                        if (first instanceof ResourceQualifier) {
399                            mRemoveButton.setEnabled(true);
400
401                            if (mMode != SelectorMode.CONFIG_ONLY) {
402                                QualifierEditBase composite = mUiMap.get(first.getClass());
403
404                                if (composite != null) {
405                                    composite.setQualifier((ResourceQualifier)first);
406                                }
407
408                                mStackLayout.topControl = composite;
409                                mQualifierEditParent.layout();
410                            }
411
412                            return;
413                        }
414                    } else {
415                        if (mMode != SelectorMode.CONFIG_ONLY) {
416                            mStackLayout.topControl = null;
417                            mQualifierEditParent.layout();
418                        }
419                    }
420                }
421
422                mRemoveButton.setEnabled(false);
423            }
424        });
425
426        if (mMode != SelectorMode.CONFIG_ONLY) {
427            // 4th column is the detail of the selected qualifier
428            mQualifierEditParent = new Composite(this, SWT.NONE);
429            mQualifierEditParent.setLayout(mStackLayout = new StackLayout());
430            mQualifierEditParent.setLayoutData(new GridData(GridData.FILL_VERTICAL));
431
432            // create the UI for all the qualifiers, and associate them to the
433            // ResourceQualifer class.
434            mUiMap.put(CountryCodeQualifier.class, new MCCEdit(mQualifierEditParent));
435            mUiMap.put(NetworkCodeQualifier.class, new MNCEdit(mQualifierEditParent));
436            mUiMap.put(LanguageQualifier.class, new LanguageEdit(mQualifierEditParent));
437            mUiMap.put(RegionQualifier.class, new RegionEdit(mQualifierEditParent));
438            mUiMap.put(LayoutDirectionQualifier.class,
439                    new LayoutDirectionEdit(mQualifierEditParent));
440            mUiMap.put(SmallestScreenWidthQualifier.class,
441                    new SmallestScreenWidthEdit(mQualifierEditParent));
442            mUiMap.put(ScreenWidthQualifier.class, new ScreenWidthEdit(mQualifierEditParent));
443            mUiMap.put(ScreenHeightQualifier.class, new ScreenHeightEdit(mQualifierEditParent));
444            mUiMap.put(ScreenSizeQualifier.class, new ScreenSizeEdit(mQualifierEditParent));
445            mUiMap.put(ScreenRatioQualifier.class, new ScreenRatioEdit(mQualifierEditParent));
446            mUiMap.put(ScreenOrientationQualifier.class, new OrientationEdit(mQualifierEditParent));
447            mUiMap.put(UiModeQualifier.class, new UiModeEdit(mQualifierEditParent));
448            mUiMap.put(NightModeQualifier.class, new NightModeEdit(mQualifierEditParent));
449            mUiMap.put(DensityQualifier.class, new DensityEdit(mQualifierEditParent));
450            mUiMap.put(TouchScreenQualifier.class, new TouchEdit(mQualifierEditParent));
451            mUiMap.put(KeyboardStateQualifier.class, new KeyboardEdit(mQualifierEditParent));
452            mUiMap.put(TextInputMethodQualifier.class, new TextInputEdit(mQualifierEditParent));
453            mUiMap.put(NavigationStateQualifier.class,
454                    new NavigationStateEdit(mQualifierEditParent));
455            mUiMap.put(NavigationMethodQualifier.class, new NavigationEdit(mQualifierEditParent));
456            mUiMap.put(ScreenDimensionQualifier.class,
457                    new ScreenDimensionEdit(mQualifierEditParent));
458            mUiMap.put(VersionQualifier.class, new VersionEdit(mQualifierEditParent));
459        }
460    }
461
462    /**
463     * Sets a {@link IQualifierFilter}. If non null, this will restrict the qualifiers that
464     * can be chosen.
465     * @param filter the filter to set.
466     */
467    public void setQualifierFilter(IQualifierFilter filter) {
468        mQualifierFilter = filter;
469    }
470
471    /**
472     * Sets a listener to be notified when the configuration changes.
473     * @param listener A {@link Runnable} whose <code>run()</code> method is called when the
474     * configuration is changed. The method is called from the UI thread.
475     */
476    public void setOnChangeListener(Runnable listener) {
477        mOnChangeListener = listener;
478    }
479
480    /**
481     * Initialize the UI with a given {@link FolderConfiguration}. This must
482     * be called from the UI thread.
483     * @param config The configuration.
484     */
485    public void setConfiguration(FolderConfiguration config) {
486
487        if (mMode != SelectorMode.CONFIG_ONLY) {
488            mSelectedConfiguration.set(config, true /*nonFakeValuesOnly*/);
489
490            // create the base config, which is the default config minus the qualifiers
491            // in SelectedConfiguration
492            mBaseConfiguration.substract(mSelectedConfiguration);
493        } else {
494            // set the base config to the edited config.
495            // reset the config to be empty
496            mBaseConfiguration.reset();
497            mBaseConfiguration.set(config, true /*nonFakeValuesOnly*/);
498        }
499
500        mSelectionTableViewer.refresh();
501        mFullTableViewer.refresh();
502    }
503
504    /**
505     * Initialize the UI with the configuration represented by a resource folder name.
506     * This must be called from the UI thread.
507     *
508     * @param folderSegments the segments of the folder name,
509     *                       split using {@link FolderConfiguration#QUALIFIER_SEP}.
510     * @return true if success, or false if the folder name is not a valid name.
511     */
512    public boolean setConfiguration(String[] folderSegments) {
513        FolderConfiguration config = FolderConfiguration.getConfig(folderSegments);
514
515        if (config == null) {
516            return false;
517        }
518
519        setConfiguration(config);
520
521        return true;
522    }
523
524    /**
525     * Initialize the UI with the configuration represented by a resource folder name.
526     * This must be called from the UI thread.
527     * @param folderName the name of the folder.
528     * @return true if success, or false if the folder name is not a valid name.
529     */
530    public boolean setConfiguration(String folderName) {
531        // split the name of the folder in segments.
532        String[] folderSegments = folderName.split(SdkConstants.RES_QUALIFIER_SEP);
533
534        return setConfiguration(folderSegments);
535    }
536
537    /**
538     * Gets the configuration as setup by the widget.
539     * @param config the {@link FolderConfiguration} object to be filled with the information
540     * from the UI.
541     */
542    public void getConfiguration(FolderConfiguration config) {
543        config.set(mSelectedConfiguration);
544    }
545
546    /**
547     * Returns the state of the configuration being edited/created.
548     */
549    public ConfigurationState getState() {
550        if (mSelectedConfiguration.getInvalidQualifier() != null) {
551            return ConfigurationState.INVALID_CONFIG;
552        }
553
554        if (mSelectedConfiguration.checkRegion() == false) {
555            return ConfigurationState.REGION_WITHOUT_LANGUAGE;
556        }
557
558        return ConfigurationState.OK;
559    }
560
561    /**
562     * Returns the first invalid qualifier of the configuration being edited/created,
563     * or <code>null<code> if they are all valid (or if none exists).
564     * <p/>If {@link #getState()} return {@link ConfigurationState#INVALID_CONFIG} then this will
565     * not return <code>null</code>.
566     */
567    public ResourceQualifier getInvalidQualifier() {
568        return mSelectedConfiguration.getInvalidQualifier();
569    }
570
571    /**
572     * Handle changes in the configuration.
573     * @param keepSelection if <code>true</code> attemps to avoid triggering selection change in
574     * {@link #mSelectedConfiguration}.
575     */
576    private void onChange(boolean keepSelection) {
577        ISelection selection = null;
578        if (keepSelection) {
579            mOnRefresh = true;
580            selection = mSelectionTableViewer.getSelection();
581        }
582
583        mSelectionTableViewer.refresh(true);
584
585        if (keepSelection) {
586            mSelectionTableViewer.setSelection(selection);
587            mOnRefresh = false;
588        }
589
590        if (mOnChangeListener != null) {
591            mOnChangeListener.run();
592        }
593    }
594
595    private void fillCombo(Combo combo, ResourceEnum[] resEnums) {
596        for (ResourceEnum resEnum : resEnums) {
597            // only add the enum if:
598            // not in device mode OR (device mode is true and) it's a valid device value.
599            // Also, always ignore fake values.
600            if ((mMode == SelectorMode.DEFAULT || resEnum.isValidValueForDevice()) &&
601                    resEnum.isFakeValue() == false) {
602                combo.add(resEnum.getShortDisplayValue());
603            }
604        }
605    }
606
607    /**
608     * Content provider around a {@link FolderConfiguration}.
609     */
610    private class QualifierContentProvider implements IStructuredContentProvider {
611
612        private FolderConfiguration mInput;
613
614        public QualifierContentProvider() {
615        }
616
617        @Override
618        public void dispose() {
619            // pass
620        }
621
622        @Override
623        public Object[] getElements(Object inputElement) {
624            // default easy case
625            if (mQualifierFilter == null) {
626                return mInput.getQualifiers();
627            }
628
629            // in this case we have to compute the list
630            ArrayList<ResourceQualifier> list = new ArrayList<ResourceQualifier>();
631            for (ResourceQualifier qual : mInput.getQualifiers()) {
632                if (mQualifierFilter.accept(qual)) {
633                    list.add(qual);
634                }
635            }
636
637            return list.toArray();
638        }
639
640        @Override
641        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
642            mInput = null;
643            if (newInput instanceof FolderConfiguration) {
644                mInput = (FolderConfiguration)newInput;
645            }
646        }
647    }
648
649    /**
650     * Label provider for {@link ResourceQualifier} objects.
651     */
652    private static class QualifierLabelProvider implements ITableLabelProvider {
653
654        private final boolean mShowQualifierValue;
655
656        public QualifierLabelProvider(boolean showQualifierValue) {
657            mShowQualifierValue = showQualifierValue;
658        }
659
660        @Override
661        public String getColumnText(Object element, int columnIndex) {
662            // only one column, so we can ignore columnIndex
663            if (element instanceof ResourceQualifier) {
664                if (mShowQualifierValue) {
665                    String value = ((ResourceQualifier)element).getShortDisplayValue();
666                    if (value.length() == 0) {
667                        return String.format("%1$s (?)",
668                                ((ResourceQualifier)element).getShortName());
669                    } else {
670                        return value;
671                    }
672
673                } else {
674                    return ((ResourceQualifier)element).getShortName();
675                }
676            }
677
678            return null;
679        }
680
681        @Override
682        public Image getColumnImage(Object element, int columnIndex) {
683            // only one column, so we can ignore columnIndex
684            if (element instanceof ResourceQualifier) {
685                return ResourceHelper.getIcon(((ResourceQualifier)element).getClass());
686            }
687
688            return null;
689        }
690
691        @Override
692        public void addListener(ILabelProviderListener listener) {
693            // pass
694        }
695
696        @Override
697        public void dispose() {
698            // pass
699        }
700
701        @Override
702        public boolean isLabelProperty(Object element, String property) {
703            // pass
704            return false;
705        }
706
707        @Override
708        public void removeListener(ILabelProviderListener listener) {
709            // pass
710        }
711    }
712
713    /**
714     * Base class for Edit widget for {@link ResourceQualifier}.
715     */
716    private abstract static class QualifierEditBase extends Composite {
717
718        public QualifierEditBase(Composite parent, String title) {
719            super(parent, SWT.NONE);
720            setLayout(new GridLayout(1, false));
721
722            new Label(this, SWT.NONE).setText(title);
723        }
724
725        public abstract void setQualifier(ResourceQualifier qualifier);
726    }
727
728    /**
729     * Edit widget for {@link CountryCodeQualifier}.
730     */
731    private class MCCEdit extends QualifierEditBase {
732
733        private Text mText;
734
735        public MCCEdit(Composite parent) {
736            super(parent, CountryCodeQualifier.NAME);
737
738            mText = new Text(this, SWT.BORDER);
739            mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
740            mText.addVerifyListener(new MobileCodeVerifier());
741            mText.addModifyListener(new ModifyListener() {
742                @Override
743                public void modifyText(ModifyEvent e) {
744                    onTextChange();
745                }
746            });
747
748            mText.addFocusListener(new FocusAdapter() {
749                @Override
750                public void focusLost(FocusEvent e) {
751                    onTextChange();
752                }
753            });
754
755            new Label(this, SWT.NONE).setText("(3 digit code)");
756        }
757
758        private void onTextChange() {
759            String value = mText.getText();
760
761            if (value.length() == 0) {
762                // empty string, means a qualifier with no value.
763                // Since the qualifier classes are immutable, and we don't want to
764                // remove the qualifier from the configuration, we create a new default one.
765                mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
766            } else {
767                try {
768                    CountryCodeQualifier qualifier = CountryCodeQualifier.getQualifier(
769                            CountryCodeQualifier.getFolderSegment(Integer.parseInt(value)));
770                    if (qualifier != null) {
771                        mSelectedConfiguration.setCountryCodeQualifier(qualifier);
772                    } else {
773                        // Failure! Looks like the value is wrong
774                        // (for instance not exactly 3 digits).
775                        mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
776                    }
777                } catch (NumberFormatException nfe) {
778                    // Looks like the code is not a number. This should not happen since the text
779                    // field has a VerifyListener that prevents it.
780                    mSelectedConfiguration.setCountryCodeQualifier(new CountryCodeQualifier());
781                }
782            }
783
784            // notify of change
785            onChange(true /* keepSelection */);
786        }
787
788        @Override
789        public void setQualifier(ResourceQualifier qualifier) {
790            CountryCodeQualifier q = (CountryCodeQualifier)qualifier;
791
792            mText.setText(Integer.toString(q.getCode()));
793        }
794    }
795
796    /**
797     * Edit widget for {@link NetworkCodeQualifier}.
798     */
799    private class MNCEdit extends QualifierEditBase {
800        private Text mText;
801
802        public MNCEdit(Composite parent) {
803            super(parent, NetworkCodeQualifier.NAME);
804
805            mText = new Text(this, SWT.BORDER);
806            mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
807            mText.addVerifyListener(new MobileCodeVerifier());
808            mText.addModifyListener(new ModifyListener() {
809                @Override
810                public void modifyText(ModifyEvent e) {
811                    onTextChange();
812                }
813            });
814            mText.addFocusListener(new FocusAdapter() {
815                @Override
816                public void focusLost(FocusEvent e) {
817                    onTextChange();
818                }
819            });
820
821            new Label(this, SWT.NONE).setText("(1-3 digit code)");
822        }
823
824        private void onTextChange() {
825            String value = mText.getText();
826
827            if (value.length() == 0) {
828                // empty string, means a qualifier with no value.
829                // Since the qualifier classes are immutable, and we don't want to
830                // remove the qualifier from the configuration, we create a new default one.
831                mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
832            } else {
833                try {
834                    NetworkCodeQualifier qualifier = NetworkCodeQualifier.getQualifier(
835                            NetworkCodeQualifier.getFolderSegment(Integer.parseInt(value)));
836                    if (qualifier != null) {
837                        mSelectedConfiguration.setNetworkCodeQualifier(qualifier);
838                    } else {
839                        // Failure! Looks like the value is wrong
840                        // (for instance not exactly 3 digits).
841                        mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
842                    }
843                } catch (NumberFormatException nfe) {
844                    // Looks like the code is not a number. This should not happen since the text
845                    // field has a VerifyListener that prevents it.
846                    mSelectedConfiguration.setNetworkCodeQualifier(new NetworkCodeQualifier());
847                }
848            }
849
850            // notify of change
851            onChange(true /* keepSelection */);
852        }
853
854        @Override
855        public void setQualifier(ResourceQualifier qualifier) {
856            NetworkCodeQualifier q = (NetworkCodeQualifier)qualifier;
857
858            mText.setText(Integer.toString(q.getCode()));
859        }
860    }
861
862    /**
863     * Edit widget for {@link LanguageQualifier}.
864     */
865    private class LanguageEdit extends QualifierEditBase {
866        private Combo mLanguage;
867        private Label mName;
868
869        public LanguageEdit(Composite parent) {
870            super(parent, LanguageQualifier.NAME);
871
872            mLanguage = new Combo(this, SWT.DROP_DOWN);
873            Set<String> codes = LocaleManager.getLanguageCodes();
874            String[] items = codes.toArray(new String[codes.size()]);
875            Arrays.sort(items);
876            mLanguage.setItems(items);
877
878            mLanguage.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
879            mLanguage.addVerifyListener(new LanguageRegionVerifier());
880            mLanguage.addSelectionListener(new SelectionListener() {
881                @Override
882                public void widgetDefaultSelected(SelectionEvent e) {
883                    onLanguageChange();
884                }
885                @Override
886                public void widgetSelected(SelectionEvent e) {
887                    onLanguageChange();
888                }
889            });
890            mLanguage.addModifyListener(new ModifyListener() {
891                @Override
892                public void modifyText(ModifyEvent e) {
893                    onLanguageChange();
894                }
895            });
896
897            new Label(this, SWT.NONE).setText("(2 letter code)");
898
899            mName = new Label(this, SWT.NONE);
900            mName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
901
902        }
903
904        private void onLanguageChange() {
905            // update the current config
906            String value = mLanguage.getText();
907
908            String newName = "";
909            if (value.length() == 2) {
910                String name = LocaleManager.getLanguageName(value.toLowerCase(Locale.US));
911                if (name != null) {
912                    newName = name;
913                }
914            }
915            mName.setText(newName);
916
917            if (value.length() == 0) {
918                // empty string, means no qualifier.
919                // Since the qualifier classes are immutable, and we don't want to
920                // remove the qualifier from the configuration, we create a new default one.
921                mSelectedConfiguration.setLanguageQualifier(new LanguageQualifier());
922            } else {
923                LanguageQualifier qualifier = null;
924                String segment = LanguageQualifier.getFolderSegment(value);
925                if (segment != null) {
926                    qualifier = LanguageQualifier.getQualifier(segment);
927                }
928
929                if (qualifier != null) {
930                    mSelectedConfiguration.setLanguageQualifier(qualifier);
931                } else {
932                    // Failure! Looks like the value is wrong (for instance a one letter string).
933                    mSelectedConfiguration.setLanguageQualifier(new LanguageQualifier());
934                }
935            }
936
937            // notify of change
938            onChange(true /* keepSelection */);
939        }
940
941        @Override
942        public void setQualifier(ResourceQualifier qualifier) {
943            LanguageQualifier q = (LanguageQualifier)qualifier;
944
945            String value = q.getValue();
946            if (value != null) {
947                mLanguage.setText(value);
948            }
949        }
950    }
951
952    /**
953     * Edit widget for {@link RegionQualifier}.
954     */
955    private class RegionEdit extends QualifierEditBase {
956        private Combo mRegion;
957
958        public RegionEdit(Composite parent) {
959            super(parent, RegionQualifier.NAME);
960
961            mRegion = new Combo(this, SWT.DROP_DOWN);
962            mRegion.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
963            mRegion.addVerifyListener(new LanguageRegionVerifier());
964            mRegion.addSelectionListener(new SelectionListener() {
965                @Override
966                public void widgetDefaultSelected(SelectionEvent e) {
967                    onRegionChange();
968                }
969                @Override
970                public void widgetSelected(SelectionEvent e) {
971                    onRegionChange();
972                }
973            });
974            mRegion.addModifyListener(new ModifyListener() {
975                @Override
976                public void modifyText(ModifyEvent e) {
977                    onRegionChange();
978                }
979            });
980
981            new Label(this, SWT.NONE).setText("(2 letter code)");
982        }
983
984        private void onRegionChange() {
985            // update the current config
986            String value = mRegion.getText();
987
988            if (value.length() == 0) {
989                // empty string, means no qualifier.
990                // Since the qualifier classes are immutable, and we don't want to
991                // remove the qualifier from the configuration, we create a new default one.
992                mSelectedConfiguration.setRegionQualifier(new RegionQualifier());
993            } else {
994                RegionQualifier qualifier = null;
995                String segment = RegionQualifier.getFolderSegment(value);
996                if (segment != null) {
997                    qualifier = RegionQualifier.getQualifier(segment);
998                }
999
1000                if (qualifier != null) {
1001                    mSelectedConfiguration.setRegionQualifier(qualifier);
1002                } else {
1003                    // Failure! Looks like the value is wrong (for instance a one letter string).
1004                    mSelectedConfiguration.setRegionQualifier(new RegionQualifier());
1005                }
1006            }
1007
1008            // notify of change
1009            onChange(true /* keepSelection */);
1010        }
1011
1012        @Override
1013        public void setQualifier(ResourceQualifier qualifier) {
1014            RegionQualifier q = (RegionQualifier)qualifier;
1015
1016            String value = q.getValue();
1017            if (value != null) {
1018                mRegion.setText(q.getValue());
1019            }
1020        }
1021    }
1022
1023    /**
1024     * Edit widget for {@link LayoutDirectionQualifier}.
1025     */
1026    private class LayoutDirectionEdit extends QualifierEditBase {
1027
1028        private Combo mDirection;
1029
1030        public LayoutDirectionEdit(Composite parent) {
1031            super(parent, LayoutDirectionQualifier.NAME);
1032
1033            mDirection = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1034            fillCombo(mDirection, LayoutDirection.values());
1035
1036            mDirection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1037            mDirection.addSelectionListener(new SelectionListener() {
1038                @Override
1039                public void widgetDefaultSelected(SelectionEvent e) {
1040                    onDirectionChange();
1041                }
1042                @Override
1043                public void widgetSelected(SelectionEvent e) {
1044                    onDirectionChange();
1045                }
1046            });
1047        }
1048
1049        protected void onDirectionChange() {
1050            // update the current config
1051            int index = mDirection.getSelectionIndex();
1052
1053            if (index != -1) {
1054                mSelectedConfiguration.setLayoutDirectionQualifier(new LayoutDirectionQualifier(
1055                        LayoutDirection.getByIndex(index)));
1056            } else {
1057                // empty selection, means no qualifier.
1058                // Since the qualifier classes are immutable, and we don't want to
1059                // remove the qualifier from the configuration, we create a new default one.
1060                mSelectedConfiguration.setLayoutDirectionQualifier(
1061                        new LayoutDirectionQualifier());
1062            }
1063
1064            // notify of change
1065            onChange(true /* keepSelection */);
1066        }
1067
1068        @Override
1069        public void setQualifier(ResourceQualifier qualifier) {
1070            LayoutDirectionQualifier q = (LayoutDirectionQualifier)qualifier;
1071
1072            LayoutDirection value = q.getValue();
1073            if (value == null) {
1074                mDirection.clearSelection();
1075            } else {
1076                mDirection.select(LayoutDirection.getIndex(value));
1077            }
1078        }
1079    }
1080
1081
1082    /**
1083     * Edit widget for {@link SmallestScreenWidthQualifier}.
1084     */
1085    private class SmallestScreenWidthEdit extends QualifierEditBase {
1086
1087        private Text mSize;
1088
1089        public SmallestScreenWidthEdit(Composite parent) {
1090            super(parent, SmallestScreenWidthQualifier.NAME);
1091
1092            ModifyListener modifyListener = new ModifyListener() {
1093                @Override
1094                public void modifyText(ModifyEvent e) {
1095                    onSizeChange();
1096                }
1097            };
1098
1099            FocusAdapter focusListener = new FocusAdapter() {
1100                @Override
1101                public void focusLost(FocusEvent e) {
1102                    onSizeChange();
1103                }
1104            };
1105
1106            mSize = new Text(this, SWT.BORDER);
1107            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1108            mSize.addVerifyListener(new DimensionVerifier());
1109            mSize.addModifyListener(modifyListener);
1110            mSize.addFocusListener(focusListener);
1111        }
1112
1113        private void onSizeChange() {
1114            // update the current config
1115            String size = mSize.getText();
1116
1117            if (size.length() == 0) {
1118                // if one of the strings is empty, reset to no qualifier.
1119                // Since the qualifier classes are immutable, and we don't want to
1120                // remove the qualifier from the configuration, we create a new default one.
1121                mSelectedConfiguration.setSmallestScreenWidthQualifier(
1122                        new SmallestScreenWidthQualifier());
1123            } else {
1124                SmallestScreenWidthQualifier qualifier = SmallestScreenWidthQualifier.getQualifier(
1125                        size);
1126
1127                if (qualifier != null) {
1128                    mSelectedConfiguration.setSmallestScreenWidthQualifier(qualifier);
1129                } else {
1130                    // Failure! Looks like the value is wrong, reset the qualifier
1131                    // Since the qualifier classes are immutable, and we don't want to
1132                    // remove the qualifier from the configuration, we create a new default one.
1133                    mSelectedConfiguration.setSmallestScreenWidthQualifier(
1134                            new SmallestScreenWidthQualifier());
1135                }
1136            }
1137
1138            // notify of change
1139            onChange(true /* keepSelection */);
1140        }
1141
1142        @Override
1143        public void setQualifier(ResourceQualifier qualifier) {
1144            SmallestScreenWidthQualifier q = (SmallestScreenWidthQualifier)qualifier;
1145
1146            mSize.setText(Integer.toString(q.getValue()));
1147        }
1148    }
1149
1150    /**
1151     * Edit widget for {@link ScreenWidthQualifier}.
1152     */
1153    private class ScreenWidthEdit extends QualifierEditBase {
1154
1155        private Text mSize;
1156
1157        public ScreenWidthEdit(Composite parent) {
1158            super(parent, ScreenWidthQualifier.NAME);
1159
1160            ModifyListener modifyListener = new ModifyListener() {
1161                @Override
1162                public void modifyText(ModifyEvent e) {
1163                    onSizeChange();
1164                }
1165            };
1166
1167            FocusAdapter focusListener = new FocusAdapter() {
1168                @Override
1169                public void focusLost(FocusEvent e) {
1170                    onSizeChange();
1171                }
1172            };
1173
1174            mSize = new Text(this, SWT.BORDER);
1175            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1176            mSize.addVerifyListener(new DimensionVerifier());
1177            mSize.addModifyListener(modifyListener);
1178            mSize.addFocusListener(focusListener);
1179        }
1180
1181        private void onSizeChange() {
1182            // update the current config
1183            String size = mSize.getText();
1184
1185            if (size.length() == 0) {
1186                // if one of the strings is empty, reset to no qualifier.
1187                // Since the qualifier classes are immutable, and we don't want to
1188                // remove the qualifier from the configuration, we create a new default one.
1189                mSelectedConfiguration.setScreenWidthQualifier(new ScreenWidthQualifier());
1190            } else {
1191                ScreenWidthQualifier qualifier = ScreenWidthQualifier.getQualifier(size);
1192
1193                if (qualifier != null) {
1194                    mSelectedConfiguration.setScreenWidthQualifier(qualifier);
1195                } else {
1196                    // Failure! Looks like the value is wrong, reset the qualifier
1197                    // Since the qualifier classes are immutable, and we don't want to
1198                    // remove the qualifier from the configuration, we create a new default one.
1199                    mSelectedConfiguration.setScreenWidthQualifier(
1200                            new ScreenWidthQualifier());
1201                }
1202            }
1203
1204            // notify of change
1205            onChange(true /* keepSelection */);
1206        }
1207
1208        @Override
1209        public void setQualifier(ResourceQualifier qualifier) {
1210            ScreenWidthQualifier q = (ScreenWidthQualifier)qualifier;
1211
1212            mSize.setText(Integer.toString(q.getValue()));
1213        }
1214    }
1215
1216    /**
1217     * Edit widget for {@link ScreenHeightQualifier}.
1218     */
1219    private class ScreenHeightEdit extends QualifierEditBase {
1220
1221        private Text mSize;
1222
1223        public ScreenHeightEdit(Composite parent) {
1224            super(parent, ScreenHeightQualifier.NAME);
1225
1226            ModifyListener modifyListener = new ModifyListener() {
1227                @Override
1228                public void modifyText(ModifyEvent e) {
1229                    onSizeChange();
1230                }
1231            };
1232
1233            FocusAdapter focusListener = new FocusAdapter() {
1234                @Override
1235                public void focusLost(FocusEvent e) {
1236                    onSizeChange();
1237                }
1238            };
1239
1240            mSize = new Text(this, SWT.BORDER);
1241            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1242            mSize.addVerifyListener(new DimensionVerifier());
1243            mSize.addModifyListener(modifyListener);
1244            mSize.addFocusListener(focusListener);
1245        }
1246
1247        private void onSizeChange() {
1248            // update the current config
1249            String size = mSize.getText();
1250
1251            if (size.length() == 0) {
1252                // if one of the strings is empty, reset to no qualifier.
1253                // Since the qualifier classes are immutable, and we don't want to
1254                // remove the qualifier from the configuration, we create a new default one.
1255                mSelectedConfiguration.setScreenHeightQualifier(new ScreenHeightQualifier());
1256            } else {
1257                ScreenHeightQualifier qualifier = ScreenHeightQualifier.getQualifier(size);
1258
1259                if (qualifier != null) {
1260                    mSelectedConfiguration.setScreenHeightQualifier(qualifier);
1261                } else {
1262                    // Failure! Looks like the value is wrong, reset the qualifier
1263                    // Since the qualifier classes are immutable, and we don't want to
1264                    // remove the qualifier from the configuration, we create a new default one.
1265                    mSelectedConfiguration.setScreenHeightQualifier(
1266                            new ScreenHeightQualifier());
1267                }
1268            }
1269
1270            // notify of change
1271            onChange(true /* keepSelection */);
1272        }
1273
1274        @Override
1275        public void setQualifier(ResourceQualifier qualifier) {
1276            ScreenHeightQualifier q = (ScreenHeightQualifier)qualifier;
1277
1278            mSize.setText(Integer.toString(q.getValue()));
1279        }
1280    }
1281
1282
1283    /**
1284     * Edit widget for {@link ScreenSizeQualifier}.
1285     */
1286    private class ScreenSizeEdit extends QualifierEditBase {
1287
1288        private Combo mSize;
1289
1290        public ScreenSizeEdit(Composite parent) {
1291            super(parent, ScreenSizeQualifier.NAME);
1292
1293            mSize = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1294            fillCombo(mSize, ScreenSize.values());
1295
1296            mSize.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1297            mSize.addSelectionListener(new SelectionListener() {
1298                @Override
1299                public void widgetDefaultSelected(SelectionEvent e) {
1300                    onScreenSizeChange();
1301                }
1302                @Override
1303                public void widgetSelected(SelectionEvent e) {
1304                    onScreenSizeChange();
1305                }
1306            });
1307        }
1308
1309        protected void onScreenSizeChange() {
1310            // update the current config
1311            int index = mSize.getSelectionIndex();
1312
1313            if (index != -1) {
1314                mSelectedConfiguration.setScreenSizeQualifier(new ScreenSizeQualifier(
1315                        ScreenSize.getByIndex(index)));
1316            } else {
1317                // empty selection, means no qualifier.
1318                // Since the qualifier classes are immutable, and we don't want to
1319                // remove the qualifier from the configuration, we create a new default one.
1320                mSelectedConfiguration.setScreenSizeQualifier(
1321                        new ScreenSizeQualifier());
1322            }
1323
1324            // notify of change
1325            onChange(true /* keepSelection */);
1326        }
1327
1328        @Override
1329        public void setQualifier(ResourceQualifier qualifier) {
1330            ScreenSizeQualifier q = (ScreenSizeQualifier)qualifier;
1331
1332            ScreenSize value = q.getValue();
1333            if (value == null) {
1334                mSize.clearSelection();
1335            } else {
1336                mSize.select(ScreenSize.getIndex(value));
1337            }
1338        }
1339    }
1340
1341    /**
1342     * Edit widget for {@link ScreenRatioQualifier}.
1343     */
1344    private class ScreenRatioEdit extends QualifierEditBase {
1345
1346        private Combo mRatio;
1347
1348        public ScreenRatioEdit(Composite parent) {
1349            super(parent, ScreenRatioQualifier.NAME);
1350
1351            mRatio = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1352            fillCombo(mRatio, ScreenRatio.values());
1353
1354            mRatio.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1355            mRatio.addSelectionListener(new SelectionListener() {
1356                @Override
1357                public void widgetDefaultSelected(SelectionEvent e) {
1358                    onScreenRatioChange();
1359                }
1360                @Override
1361                public void widgetSelected(SelectionEvent e) {
1362                    onScreenRatioChange();
1363                }
1364            });
1365        }
1366
1367        protected void onScreenRatioChange() {
1368            // update the current config
1369            int index = mRatio.getSelectionIndex();
1370
1371            if (index != -1) {
1372                mSelectedConfiguration.setScreenRatioQualifier(new ScreenRatioQualifier(
1373                        ScreenRatio.getByIndex(index)));
1374            } else {
1375                // empty selection, means no qualifier.
1376                // Since the qualifier classes are immutable, and we don't want to
1377                // remove the qualifier from the configuration, we create a new default one.
1378                mSelectedConfiguration.setScreenRatioQualifier(
1379                        new ScreenRatioQualifier());
1380            }
1381
1382            // notify of change
1383            onChange(true /* keepSelection */);
1384        }
1385
1386        @Override
1387        public void setQualifier(ResourceQualifier qualifier) {
1388            ScreenRatioQualifier q = (ScreenRatioQualifier)qualifier;
1389
1390            ScreenRatio value = q.getValue();
1391            if (value == null) {
1392                mRatio.clearSelection();
1393            } else {
1394                mRatio.select(ScreenRatio.getIndex(value));
1395            }
1396        }
1397    }
1398
1399    /**
1400     * Edit widget for {@link ScreenOrientationQualifier}.
1401     */
1402    private class OrientationEdit extends QualifierEditBase {
1403
1404        private Combo mOrientation;
1405
1406        public OrientationEdit(Composite parent) {
1407            super(parent, ScreenOrientationQualifier.NAME);
1408
1409            mOrientation = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1410            fillCombo(mOrientation, ScreenOrientation.values());
1411
1412            mOrientation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1413            mOrientation.addSelectionListener(new SelectionListener() {
1414                @Override
1415                public void widgetDefaultSelected(SelectionEvent e) {
1416                    onOrientationChange();
1417                }
1418                @Override
1419                public void widgetSelected(SelectionEvent e) {
1420                    onOrientationChange();
1421                }
1422            });
1423        }
1424
1425        protected void onOrientationChange() {
1426            // update the current config
1427            int index = mOrientation.getSelectionIndex();
1428
1429            if (index != -1) {
1430                mSelectedConfiguration.setScreenOrientationQualifier(new ScreenOrientationQualifier(
1431                    ScreenOrientation.getByIndex(index)));
1432            } else {
1433                // empty selection, means no qualifier.
1434                // Since the qualifier classes are immutable, and we don't want to
1435                // remove the qualifier from the configuration, we create a new default one.
1436                mSelectedConfiguration.setScreenOrientationQualifier(
1437                        new ScreenOrientationQualifier());
1438            }
1439
1440            // notify of change
1441            onChange(true /* keepSelection */);
1442        }
1443
1444        @Override
1445        public void setQualifier(ResourceQualifier qualifier) {
1446            ScreenOrientationQualifier q = (ScreenOrientationQualifier)qualifier;
1447
1448            ScreenOrientation value = q.getValue();
1449            if (value == null) {
1450                mOrientation.clearSelection();
1451            } else {
1452                mOrientation.select(ScreenOrientation.getIndex(value));
1453            }
1454        }
1455    }
1456
1457    /**
1458     * Edit widget for {@link DockModeQualifier}.
1459     */
1460    private class UiModeEdit extends QualifierEditBase {
1461
1462        private Combo mUiMode;
1463
1464        public UiModeEdit(Composite parent) {
1465            super(parent, UiModeQualifier.NAME);
1466
1467            mUiMode = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1468            fillCombo(mUiMode, UiMode.values());
1469
1470            mUiMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1471            mUiMode.addSelectionListener(new SelectionListener() {
1472                @Override
1473                public void widgetDefaultSelected(SelectionEvent e) {
1474                    onDockModeChange();
1475                }
1476                @Override
1477                public void widgetSelected(SelectionEvent e) {
1478                    onDockModeChange();
1479                }
1480            });
1481        }
1482
1483        protected void onDockModeChange() {
1484            // update the current config
1485            int index = mUiMode.getSelectionIndex();
1486
1487            if (index != -1) {
1488                mSelectedConfiguration.setUiModeQualifier(
1489                        new UiModeQualifier(UiMode.getByIndex(index)));
1490            } else {
1491                // empty selection, means no qualifier.
1492                // Since the qualifier classes are immutable, and we don't want to
1493                // remove the qualifier from the configuration, we create a new default one.
1494                mSelectedConfiguration.setUiModeQualifier(new UiModeQualifier());
1495            }
1496
1497            // notify of change
1498            onChange(true /* keepSelection */);
1499        }
1500
1501        @Override
1502        public void setQualifier(ResourceQualifier qualifier) {
1503            UiModeQualifier q = (UiModeQualifier)qualifier;
1504
1505            UiMode value = q.getValue();
1506            if (value == null) {
1507                mUiMode.clearSelection();
1508            } else {
1509                mUiMode.select(UiMode.getIndex(value));
1510            }
1511        }
1512    }
1513
1514    /**
1515     * Edit widget for {@link NightModeQualifier}.
1516     */
1517    private class NightModeEdit extends QualifierEditBase {
1518
1519        private Combo mNightMode;
1520
1521        public NightModeEdit(Composite parent) {
1522            super(parent, NightModeQualifier.NAME);
1523
1524            mNightMode = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1525            fillCombo(mNightMode, NightMode.values());
1526
1527            mNightMode.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1528            mNightMode.addSelectionListener(new SelectionListener() {
1529                @Override
1530                public void widgetDefaultSelected(SelectionEvent e) {
1531                    onNightModeChange();
1532                }
1533                @Override
1534                public void widgetSelected(SelectionEvent e) {
1535                    onNightModeChange();
1536                }
1537            });
1538        }
1539
1540        protected void onNightModeChange() {
1541            // update the current config
1542            int index = mNightMode.getSelectionIndex();
1543
1544            if (index != -1) {
1545                mSelectedConfiguration.setNightModeQualifier(
1546                        new NightModeQualifier(NightMode.getByIndex(index)));
1547            } else {
1548                // empty selection, means no qualifier.
1549                // Since the qualifier classes are immutable, and we don't want to
1550                // remove the qualifier from the configuration, we create a new default one.
1551                mSelectedConfiguration.setNightModeQualifier(new NightModeQualifier());
1552            }
1553
1554            // notify of change
1555            onChange(true /* keepSelection */);
1556        }
1557
1558        @Override
1559        public void setQualifier(ResourceQualifier qualifier) {
1560            NightModeQualifier q = (NightModeQualifier)qualifier;
1561
1562            NightMode value = q.getValue();
1563            if (value == null) {
1564                mNightMode.clearSelection();
1565            } else {
1566                mNightMode.select(NightMode.getIndex(value));
1567            }
1568        }
1569    }
1570
1571
1572    /**
1573     * Edit widget for {@link DensityQualifier}.
1574     */
1575    private class DensityEdit extends QualifierEditBase {
1576        private Combo mDensity;
1577
1578        public DensityEdit(Composite parent) {
1579            super(parent, DensityQualifier.NAME);
1580
1581            mDensity = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1582            fillCombo(mDensity, Density.values());
1583
1584            mDensity.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1585            mDensity.addSelectionListener(new SelectionListener() {
1586                @Override
1587                public void widgetDefaultSelected(SelectionEvent e) {
1588                    onDensityChange();
1589                }
1590                @Override
1591                public void widgetSelected(SelectionEvent e) {
1592                    onDensityChange();
1593                }
1594            });
1595        }
1596
1597        private void onDensityChange() {
1598            // update the current config
1599            int index = mDensity.getSelectionIndex();
1600
1601            if (index != -1) {
1602                mSelectedConfiguration.setDensityQualifier(new DensityQualifier(
1603                    Density.getByIndex(index)));
1604            } else {
1605                // empty selection, means no qualifier.
1606                // Since the qualifier classes are immutable, and we don't want to
1607                // remove the qualifier from the configuration, we create a new default one.
1608                mSelectedConfiguration.setDensityQualifier(
1609                        new DensityQualifier());
1610            }
1611
1612            // notify of change
1613            onChange(true /* keepSelection */);
1614        }
1615
1616        @Override
1617        public void setQualifier(ResourceQualifier qualifier) {
1618            DensityQualifier q = (DensityQualifier)qualifier;
1619
1620            Density value = q.getValue();
1621            if (value == null) {
1622                mDensity.clearSelection();
1623            } else {
1624                mDensity.select(Density.getIndex(value));
1625            }
1626        }
1627    }
1628
1629    /**
1630     * Edit widget for {@link TouchScreenQualifier}.
1631     */
1632    private class TouchEdit extends QualifierEditBase {
1633
1634        private Combo mTouchScreen;
1635
1636        public TouchEdit(Composite parent) {
1637            super(parent, TouchScreenQualifier.NAME);
1638
1639            mTouchScreen = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1640            fillCombo(mTouchScreen, TouchScreen.values());
1641
1642            mTouchScreen.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1643            mTouchScreen.addSelectionListener(new SelectionListener() {
1644                @Override
1645                public void widgetDefaultSelected(SelectionEvent e) {
1646                    onTouchChange();
1647                }
1648                @Override
1649                public void widgetSelected(SelectionEvent e) {
1650                    onTouchChange();
1651                }
1652            });
1653        }
1654
1655        protected void onTouchChange() {
1656            // update the current config
1657            int index = mTouchScreen.getSelectionIndex();
1658
1659            if (index != -1) {
1660                mSelectedConfiguration.setTouchTypeQualifier(new TouchScreenQualifier(
1661                        TouchScreen.getByIndex(index)));
1662            } else {
1663                // empty selection, means no qualifier.
1664                // Since the qualifier classes are immutable, and we don't want to
1665                // remove the qualifier from the configuration, we create a new default one.
1666                mSelectedConfiguration.setTouchTypeQualifier(new TouchScreenQualifier());
1667            }
1668
1669            // notify of change
1670            onChange(true /* keepSelection */);
1671        }
1672
1673        @Override
1674        public void setQualifier(ResourceQualifier qualifier) {
1675            TouchScreenQualifier q = (TouchScreenQualifier)qualifier;
1676
1677            TouchScreen value = q.getValue();
1678            if (value == null) {
1679                mTouchScreen.clearSelection();
1680            } else {
1681                mTouchScreen.select(TouchScreen.getIndex(value));
1682            }
1683        }
1684    }
1685
1686    /**
1687     * Edit widget for {@link KeyboardStateQualifier}.
1688     */
1689    private class KeyboardEdit extends QualifierEditBase {
1690
1691        private Combo mKeyboardState;
1692
1693        public KeyboardEdit(Composite parent) {
1694            super(parent, KeyboardStateQualifier.NAME);
1695
1696            mKeyboardState = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1697            fillCombo(mKeyboardState, KeyboardState.values());
1698
1699            mKeyboardState.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1700            mKeyboardState.addSelectionListener(new SelectionListener() {
1701                @Override
1702                public void widgetDefaultSelected(SelectionEvent e) {
1703                    onKeyboardChange();
1704                }
1705                @Override
1706                public void widgetSelected(SelectionEvent e) {
1707                    onKeyboardChange();
1708                }
1709            });
1710        }
1711
1712        protected void onKeyboardChange() {
1713            // update the current config
1714            int index = mKeyboardState.getSelectionIndex();
1715
1716            if (index != -1) {
1717                mSelectedConfiguration.setKeyboardStateQualifier(new KeyboardStateQualifier(
1718                        KeyboardState.getByIndex(index)));
1719            } else {
1720                // empty selection, means no qualifier.
1721                // Since the qualifier classes are immutable, and we don't want to
1722                // remove the qualifier from the configuration, we create a new default one.
1723                mSelectedConfiguration.setKeyboardStateQualifier(
1724                        new KeyboardStateQualifier());
1725            }
1726
1727            // notify of change
1728            onChange(true /* keepSelection */);
1729        }
1730
1731        @Override
1732        public void setQualifier(ResourceQualifier qualifier) {
1733            KeyboardStateQualifier q = (KeyboardStateQualifier)qualifier;
1734
1735            KeyboardState value = q.getValue();
1736            if (value == null) {
1737                mKeyboardState.clearSelection();
1738            } else {
1739                mKeyboardState.select(KeyboardState.getIndex(value));
1740            }
1741        }
1742    }
1743
1744    /**
1745     * Edit widget for {@link TextInputMethodQualifier}.
1746     */
1747    private class TextInputEdit extends QualifierEditBase {
1748
1749        private Combo mTextInput;
1750
1751        public TextInputEdit(Composite parent) {
1752            super(parent, TextInputMethodQualifier.NAME);
1753
1754            mTextInput = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1755            fillCombo(mTextInput, Keyboard.values());
1756
1757            mTextInput.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1758            mTextInput.addSelectionListener(new SelectionListener() {
1759                @Override
1760                public void widgetDefaultSelected(SelectionEvent e) {
1761                    onTextInputChange();
1762                }
1763                @Override
1764                public void widgetSelected(SelectionEvent e) {
1765                    onTextInputChange();
1766                }
1767            });
1768        }
1769
1770        protected void onTextInputChange() {
1771            // update the current config
1772            int index = mTextInput.getSelectionIndex();
1773
1774            if (index != -1) {
1775                mSelectedConfiguration.setTextInputMethodQualifier(new TextInputMethodQualifier(
1776                        Keyboard.getByIndex(index)));
1777            } else {
1778                // empty selection, means no qualifier.
1779                // Since the qualifier classes are immutable, and we don't want to
1780                // remove the qualifier from the configuration, we create a new default one.
1781                mSelectedConfiguration.setTextInputMethodQualifier(
1782                        new TextInputMethodQualifier());
1783            }
1784
1785            // notify of change
1786            onChange(true /* keepSelection */);
1787        }
1788
1789        @Override
1790        public void setQualifier(ResourceQualifier qualifier) {
1791            TextInputMethodQualifier q = (TextInputMethodQualifier)qualifier;
1792
1793            Keyboard value = q.getValue();
1794            if (value == null) {
1795                mTextInput.clearSelection();
1796            } else {
1797                mTextInput.select(Keyboard.getIndex(value));
1798            }
1799        }
1800    }
1801
1802    /**
1803     * Edit widget for {@link NavigationStateQualifier}.
1804     */
1805    private class NavigationStateEdit extends QualifierEditBase {
1806
1807        private Combo mNavigationState;
1808
1809        public NavigationStateEdit(Composite parent) {
1810            super(parent, NavigationStateQualifier.NAME);
1811
1812            mNavigationState = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1813            fillCombo(mNavigationState, NavigationState.values());
1814
1815            mNavigationState.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1816            mNavigationState.addSelectionListener(new SelectionListener() {
1817                @Override
1818                public void widgetDefaultSelected(SelectionEvent e) {
1819                    onNavigationChange();
1820                }
1821                @Override
1822                public void widgetSelected(SelectionEvent e) {
1823                    onNavigationChange();
1824                }
1825            });
1826        }
1827
1828        protected void onNavigationChange() {
1829            // update the current config
1830            int index = mNavigationState.getSelectionIndex();
1831
1832            if (index != -1) {
1833                mSelectedConfiguration.setNavigationStateQualifier(
1834                        new NavigationStateQualifier(NavigationState.getByIndex(index)));
1835            } else {
1836                // empty selection, means no qualifier.
1837                // Since the qualifier classes are immutable, and we don't want to
1838                // remove the qualifier from the configuration, we create a new default one.
1839                mSelectedConfiguration.setNavigationStateQualifier(new NavigationStateQualifier());
1840            }
1841
1842            // notify of change
1843            onChange(true /* keepSelection */);
1844        }
1845
1846        @Override
1847        public void setQualifier(ResourceQualifier qualifier) {
1848            NavigationStateQualifier q = (NavigationStateQualifier)qualifier;
1849
1850            NavigationState value = q.getValue();
1851            if (value == null) {
1852                mNavigationState.clearSelection();
1853            } else {
1854                mNavigationState.select(NavigationState.getIndex(value));
1855            }
1856        }
1857    }
1858
1859
1860    /**
1861     * Edit widget for {@link NavigationMethodQualifier}.
1862     */
1863    private class NavigationEdit extends QualifierEditBase {
1864
1865        private Combo mNavigation;
1866
1867        public NavigationEdit(Composite parent) {
1868            super(parent, NavigationMethodQualifier.NAME);
1869
1870            mNavigation = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
1871            fillCombo(mNavigation, Navigation.values());
1872
1873            mNavigation.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1874            mNavigation.addSelectionListener(new SelectionListener() {
1875                @Override
1876                public void widgetDefaultSelected(SelectionEvent e) {
1877                    onNavigationChange();
1878                }
1879                @Override
1880                public void widgetSelected(SelectionEvent e) {
1881                    onNavigationChange();
1882                }
1883            });
1884        }
1885
1886        protected void onNavigationChange() {
1887            // update the current config
1888            int index = mNavigation.getSelectionIndex();
1889
1890            if (index != -1) {
1891                mSelectedConfiguration.setNavigationMethodQualifier(new NavigationMethodQualifier(
1892                        Navigation.getByIndex(index)));
1893            } else {
1894                // empty selection, means no qualifier.
1895                // Since the qualifier classes are immutable, and we don't want to
1896                // remove the qualifier from the configuration, we create a new default one.
1897                mSelectedConfiguration.setNavigationMethodQualifier(
1898                        new NavigationMethodQualifier());
1899            }
1900
1901            // notify of change
1902            onChange(true /* keepSelection */);
1903        }
1904
1905        @Override
1906        public void setQualifier(ResourceQualifier qualifier) {
1907            NavigationMethodQualifier q = (NavigationMethodQualifier)qualifier;
1908
1909            Navigation value = q.getValue();
1910            if (value == null) {
1911                mNavigation.clearSelection();
1912            } else {
1913                mNavigation.select(Navigation.getIndex(value));
1914            }
1915        }
1916    }
1917
1918    /**
1919     * Edit widget for {@link ScreenDimensionQualifier}.
1920     */
1921    private class ScreenDimensionEdit extends QualifierEditBase {
1922
1923        private Text mSize1;
1924        private Text mSize2;
1925
1926        public ScreenDimensionEdit(Composite parent) {
1927            super(parent, ScreenDimensionQualifier.NAME);
1928
1929            ModifyListener modifyListener = new ModifyListener() {
1930                @Override
1931                public void modifyText(ModifyEvent e) {
1932                    onSizeChange();
1933                }
1934            };
1935
1936            FocusAdapter focusListener = new FocusAdapter() {
1937                @Override
1938                public void focusLost(FocusEvent e) {
1939                    onSizeChange();
1940                }
1941            };
1942
1943            mSize1 = new Text(this, SWT.BORDER);
1944            mSize1.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1945            mSize1.addVerifyListener(new DimensionVerifier());
1946            mSize1.addModifyListener(modifyListener);
1947            mSize1.addFocusListener(focusListener);
1948
1949            mSize2 = new Text(this, SWT.BORDER);
1950            mSize2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1951            mSize2.addVerifyListener(new DimensionVerifier());
1952            mSize2.addModifyListener(modifyListener);
1953            mSize2.addFocusListener(focusListener);
1954        }
1955
1956        private void onSizeChange() {
1957            // update the current config
1958            String size1 = mSize1.getText();
1959            String size2 = mSize2.getText();
1960
1961            if (size1.length() == 0 || size2.length() == 0) {
1962                // if one of the strings is empty, reset to no qualifier.
1963                // Since the qualifier classes are immutable, and we don't want to
1964                // remove the qualifier from the configuration, we create a new default one.
1965                mSelectedConfiguration.setScreenDimensionQualifier(new ScreenDimensionQualifier());
1966            } else {
1967                ScreenDimensionQualifier qualifier = ScreenDimensionQualifier.getQualifier(size1,
1968                        size2);
1969
1970                if (qualifier != null) {
1971                    mSelectedConfiguration.setScreenDimensionQualifier(qualifier);
1972                } else {
1973                    // Failure! Looks like the value is wrong, reset the qualifier
1974                    // Since the qualifier classes are immutable, and we don't want to
1975                    // remove the qualifier from the configuration, we create a new default one.
1976                    mSelectedConfiguration.setScreenDimensionQualifier(
1977                            new ScreenDimensionQualifier());
1978                }
1979            }
1980
1981            // notify of change
1982            onChange(true /* keepSelection */);
1983        }
1984
1985        @Override
1986        public void setQualifier(ResourceQualifier qualifier) {
1987            ScreenDimensionQualifier q = (ScreenDimensionQualifier)qualifier;
1988
1989            mSize1.setText(Integer.toString(q.getValue1()));
1990            mSize2.setText(Integer.toString(q.getValue2()));
1991        }
1992    }
1993
1994    /**
1995     * Edit widget for {@link VersionQualifier}.
1996     */
1997    private class VersionEdit extends QualifierEditBase {
1998        private Text mText;
1999
2000        public VersionEdit(Composite parent) {
2001            super(parent, VersionQualifier.NAME);
2002
2003            mText = new Text(this, SWT.BORDER);
2004            mText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
2005            mText.addVerifyListener(new MobileCodeVerifier());
2006            mText.addModifyListener(new ModifyListener() {
2007                @Override
2008                public void modifyText(ModifyEvent e) {
2009                    onVersionChange();
2010                }
2011            });
2012            mText.addFocusListener(new FocusAdapter() {
2013                @Override
2014                public void focusLost(FocusEvent e) {
2015                    onVersionChange();
2016                }
2017            });
2018
2019            new Label(this, SWT.NONE).setText("(Platform API level)");
2020        }
2021
2022        private void onVersionChange() {
2023            String value = mText.getText();
2024
2025            if (value.length() == 0) {
2026                // empty string, means a qualifier with no value.
2027                // Since the qualifier classes are immutable, and we don't want to
2028                // remove the qualifier from the configuration, we create a new default one.
2029                mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
2030            } else {
2031                try {
2032                    VersionQualifier qualifier = VersionQualifier.getQualifier(
2033                            VersionQualifier.getFolderSegment(Integer.parseInt(value)));
2034                    if (qualifier != null) {
2035                        mSelectedConfiguration.setVersionQualifier(qualifier);
2036                    } else {
2037                        // Failure! Looks like the value is wrong
2038                        mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
2039                    }
2040                } catch (NumberFormatException nfe) {
2041                    // Looks like the code is not a number. This should not happen since the text
2042                    // field has a VerifyListener that prevents it.
2043                    mSelectedConfiguration.setVersionQualifier(new VersionQualifier());
2044                }
2045            }
2046
2047            // notify of change
2048            onChange(true /* keepSelection */);
2049        }
2050
2051        @Override
2052        public void setQualifier(ResourceQualifier qualifier) {
2053            VersionQualifier q = (VersionQualifier)qualifier;
2054
2055            mText.setText(Integer.toString(q.getVersion()));
2056        }
2057    }
2058
2059}
2060