Home | History | Annotate | Download | only in uimodel
      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 
     17 package com.android.ide.eclipse.adt.internal.editors.uimodel;
     18 
     19 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     20 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
     21 import com.android.ide.eclipse.adt.internal.editors.descriptors.FlagAttributeDescriptor;
     22 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
     23 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
     24 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
     25 
     26 import org.eclipse.jface.dialogs.Dialog;
     27 import org.eclipse.jface.resource.FontDescriptor;
     28 import org.eclipse.jface.resource.JFaceResources;
     29 import org.eclipse.swt.SWT;
     30 import org.eclipse.swt.events.ControlAdapter;
     31 import org.eclipse.swt.events.ControlEvent;
     32 import org.eclipse.swt.events.SelectionAdapter;
     33 import org.eclipse.swt.events.SelectionEvent;
     34 import org.eclipse.swt.graphics.Font;
     35 import org.eclipse.swt.graphics.Rectangle;
     36 import org.eclipse.swt.layout.GridData;
     37 import org.eclipse.swt.layout.GridLayout;
     38 import org.eclipse.swt.widgets.Button;
     39 import org.eclipse.swt.widgets.Composite;
     40 import org.eclipse.swt.widgets.Control;
     41 import org.eclipse.swt.widgets.Label;
     42 import org.eclipse.swt.widgets.Shell;
     43 import org.eclipse.swt.widgets.Table;
     44 import org.eclipse.swt.widgets.TableColumn;
     45 import org.eclipse.swt.widgets.TableItem;
     46 import org.eclipse.swt.widgets.Text;
     47 import org.eclipse.ui.dialogs.SelectionStatusDialog;
     48 import org.eclipse.ui.forms.IManagedForm;
     49 import org.eclipse.ui.forms.widgets.FormToolkit;
     50 import org.eclipse.ui.forms.widgets.TableWrapData;
     51 
     52 import java.util.ArrayList;
     53 import java.util.HashSet;
     54 import java.util.Set;
     55 
     56 /**
     57  * Represents an XML attribute that is defined by a set of flag values,
     58  * i.e. enum names separated by pipe (|) characters.
     59  *
     60  * Note: in Android resources, a "flag" is a list of fixed values where one or
     61  * more values can be selected using an "or", e.g. "align='left|top'".
     62  * By contrast, an "enum" is a list of fixed values of which only one can be
     63  * selected at a given time, e.g. "gravity='right'".
     64  * <p/>
     65  * This class handles the "flag" case.
     66  * The "enum" case is done using {@link UiListAttributeNode}.
     67  */
     68 public class UiFlagAttributeNode extends UiTextAttributeNode {
     69 
     70     public UiFlagAttributeNode(FlagAttributeDescriptor attributeDescriptor,
     71             UiElementNode uiParent) {
     72         super(attributeDescriptor, uiParent);
     73     }
     74 
     75     /* (non-java doc)
     76      * Creates a label widget and an associated text field.
     77      * <p/>
     78      * As most other parts of the android manifest editor, this assumes the
     79      * parent uses a table layout with 2 columns.
     80      */
     81     @Override
     82     public void createUiControl(Composite parent, IManagedForm managedForm) {
     83         setManagedForm(managedForm);
     84         FormToolkit toolkit = managedForm.getToolkit();
     85         TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
     86 
     87         Label label = toolkit.createLabel(parent, desc.getUiName());
     88         label.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE));
     89         SectionHelper.addControlTooltip(label, DescriptorsUtils.formatTooltip(desc.getTooltip()));
     90 
     91         Composite composite = toolkit.createComposite(parent);
     92         composite.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE));
     93         GridLayout gl = new GridLayout(2, false);
     94         gl.marginHeight = gl.marginWidth = 0;
     95         composite.setLayout(gl);
     96         // Fixes missing text borders under GTK... also requires adding a 1-pixel margin
     97         // for the text field below
     98         toolkit.paintBordersFor(composite);
     99 
    100         final Text text = toolkit.createText(composite, getCurrentValue());
    101         GridData gd = new GridData(GridData.FILL_HORIZONTAL);
    102         gd.horizontalIndent = 1;  // Needed by the fixed composite borders under GTK
    103         text.setLayoutData(gd);
    104         final Button selectButton = toolkit.createButton(composite, "Select...", SWT.PUSH);
    105 
    106         setTextWidget(text);
    107 
    108         selectButton.addSelectionListener(new SelectionAdapter() {
    109             @Override
    110             public void widgetSelected(SelectionEvent e) {
    111                 super.widgetSelected(e);
    112 
    113                 String currentText = getTextWidgetValue();
    114 
    115                 String result = showDialog(selectButton.getShell(), currentText);
    116 
    117                 if (result != null) {
    118                     setTextWidgetValue(result);
    119                 }
    120             }
    121         });
    122     }
    123 
    124     /**
    125      * Get the flag names, either from the initial names set in the attribute
    126      * or by querying the framework resource parser.
    127      *
    128      * {@inheritDoc}
    129      */
    130     @Override
    131     public String[] getPossibleValues(String prefix) {
    132         String attr_name = getDescriptor().getXmlLocalName();
    133         String element_name = getUiParent().getDescriptor().getXmlName();
    134 
    135         String[] values = null;
    136 
    137         if (getDescriptor() instanceof FlagAttributeDescriptor &&
    138                 ((FlagAttributeDescriptor) getDescriptor()).getNames() != null) {
    139             // Get enum values from the descriptor
    140             values = ((FlagAttributeDescriptor) getDescriptor()).getNames();
    141         }
    142 
    143         if (values == null) {
    144             // or from the AndroidTargetData
    145             UiElementNode uiNode = getUiParent();
    146             AndroidXmlEditor editor = uiNode.getEditor();
    147             AndroidTargetData data = editor.getTargetData();
    148             if (data != null) {
    149                 values = data.getAttributeValues(element_name, attr_name);
    150             }
    151         }
    152 
    153         return values;
    154     }
    155 
    156     /**
    157      * Shows a dialog letting the user choose a set of enum, and returns a string
    158      * containing the result.
    159      */
    160     public String showDialog(Shell shell, String currentValue) {
    161         FlagSelectionDialog dlg = new FlagSelectionDialog(
    162                 shell, currentValue.trim().split("\\s*\\|\\s*")); //$NON-NLS-1$
    163         dlg.open();
    164         Object[] result = dlg.getResult();
    165         if (result != null) {
    166             StringBuilder buf = new StringBuilder();
    167             for (Object name : result) {
    168                 if (name instanceof String) {
    169                     if (buf.length() > 0) {
    170                         buf.append('|');
    171                     }
    172                     buf.append(name);
    173                 }
    174             }
    175 
    176             return buf.toString();
    177         }
    178 
    179         return null;
    180 
    181     }
    182 
    183     /**
    184      * Displays a list of flag names with checkboxes.
    185      */
    186     private class FlagSelectionDialog extends SelectionStatusDialog {
    187 
    188         private Set<String> mCurrentSet;
    189         private Table mTable;
    190 
    191         public FlagSelectionDialog(Shell parentShell, String[] currentNames) {
    192             super(parentShell);
    193 
    194             mCurrentSet = new HashSet<String>();
    195             for (String name : currentNames) {
    196                 if (name.length() > 0) {
    197                     mCurrentSet.add(name);
    198                 }
    199             }
    200 
    201             int shellStyle = getShellStyle();
    202             setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
    203         }
    204 
    205         @Override
    206         protected void computeResult() {
    207             if (mTable != null) {
    208                 ArrayList<String> results = new ArrayList<String>();
    209 
    210                 for (TableItem item : mTable.getItems()) {
    211                     if (item.getChecked()) {
    212                         results.add((String)item.getData());
    213                     }
    214                 }
    215 
    216                 setResult(results);
    217             }
    218         }
    219 
    220         @Override
    221         protected Control createDialogArea(Composite parent) {
    222             Composite composite= new Composite(parent, SWT.NONE);
    223             composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    224             composite.setLayout(new GridLayout(1, true));
    225             composite.setFont(parent.getFont());
    226 
    227             Label label = new Label(composite, SWT.NONE);
    228             label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    229             label.setText(String.format("Select the flag values for attribute %1$s:",
    230                     ((FlagAttributeDescriptor) getDescriptor()).getUiName()));
    231 
    232             mTable = new Table(composite, SWT.CHECK | SWT.BORDER);
    233             GridData data = new GridData();
    234             // The 60,18 hints are the ones used by AbstractElementListSelectionDialog
    235             data.widthHint = convertWidthInCharsToPixels(60);
    236             data.heightHint = convertHeightInCharsToPixels(18);
    237             data.grabExcessVerticalSpace = true;
    238             data.grabExcessHorizontalSpace = true;
    239             data.horizontalAlignment = GridData.FILL;
    240             data.verticalAlignment = GridData.FILL;
    241             mTable.setLayoutData(data);
    242 
    243             mTable.setHeaderVisible(false);
    244             final TableColumn column = new TableColumn(mTable, SWT.NONE);
    245 
    246             // List all the expected flag names and check those which are currently used
    247             String[] names = getPossibleValues(null);
    248             if (names != null) {
    249                 for (String name : names) {
    250                     TableItem item = new TableItem(mTable, SWT.NONE);
    251                     item.setText(name);
    252                     item.setData(name);
    253 
    254                     boolean hasName = mCurrentSet.contains(name);
    255                     item.setChecked(hasName);
    256                     if (hasName) {
    257                         mCurrentSet.remove(name);
    258                     }
    259                 }
    260             }
    261 
    262             // If there are unknown flag names currently used, display them at the end if the
    263             // table already checked.
    264             if (!mCurrentSet.isEmpty()) {
    265                 FontDescriptor fontDesc = JFaceResources.getDialogFontDescriptor();
    266                 fontDesc = fontDesc.withStyle(SWT.ITALIC);
    267                 Font font = fontDesc.createFont(JFaceResources.getDialogFont().getDevice());
    268 
    269                 for (String name : mCurrentSet) {
    270                     TableItem item = new TableItem(mTable, SWT.NONE);
    271                     item.setText(String.format("%1$s (unknown flag)", name));
    272                     item.setData(name);
    273                     item.setChecked(true);
    274                     item.setFont(font);
    275                 }
    276             }
    277 
    278             // Add a listener that will resize the column to the full width of the table
    279             // so that only one column appears in the table even if the dialog is resized.
    280             ControlAdapter listener = new ControlAdapter() {
    281                 @Override
    282                 public void controlResized(ControlEvent e) {
    283                     Rectangle r = mTable.getClientArea();
    284                     column.setWidth(r.width);
    285                 }
    286             };
    287 
    288             mTable.addControlListener(listener);
    289             listener.controlResized(null /* event not used */);
    290 
    291             // Add a selection listener that will check/uncheck items when they are double-clicked
    292             mTable.addSelectionListener(new SelectionAdapter() {
    293                 /** Default selection means double-click on "most" platforms */
    294                 @Override
    295                 public void widgetDefaultSelected(SelectionEvent e) {
    296                     if (e.item instanceof TableItem) {
    297                         TableItem i = (TableItem) e.item;
    298                         i.setChecked(!i.getChecked());
    299                     }
    300                     super.widgetDefaultSelected(e);
    301                 }
    302             });
    303 
    304             Dialog.applyDialogFont(composite);
    305             setHelpAvailable(false);
    306 
    307             return composite;
    308         }
    309     }
    310 }
    311