2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.eclipse.org/org/documents/epl-v10.php
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.
17 package com.android.ide.eclipse.adt.internal.editors.uimodel;
19 import com.android.ide.eclipse.adt.internal.editors.AndroidEditor;
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;
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;
52 import java.util.ArrayList;
53 import java.util.HashSet;
57 * Represents an XML attribute that is defined by a set of flag values,
58 * i.e. enum names separated by pipe (|) characters.
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'".
65 * This class handles the "flag" case.
66 * The "enum" case is done using {@link UiListAttributeNode}.
68 public class UiFlagAttributeNode extends UiTextAttributeNode {
70 public UiFlagAttributeNode(FlagAttributeDescriptor attributeDescriptor,
71 UiElementNode uiParent) {
72 super(attributeDescriptor, uiParent);
76 * Creates a label widget and an associated text field.
78 * As most other parts of the android manifest editor, this assumes the
79 * parent uses a table layout with 2 columns.
82 public void createUiControl(Composite parent, IManagedForm managedForm) {
83 setManagedForm(managedForm);
84 FormToolkit toolkit = managedForm.getToolkit();
85 TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor();
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()));
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);
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);
108 selectButton.addSelectionListener(new SelectionAdapter() {
110 public void widgetSelected(SelectionEvent e) {
111 super.widgetSelected(e);
113 String currentText = getTextWidgetValue();
115 String result = showDialog(selectButton.getShell(), currentText);
117 if (result != null) {
118 setTextWidgetValue(result);
125 * Get the flag names, either from the initial names set in the attribute
126 * or by querying the framework resource parser.
131 public String[] getPossibleValues(String prefix) {
132 String attr_name = getDescriptor().getXmlLocalName();
133 String element_name = getUiParent().getDescriptor().getXmlName();
135 String[] values = null;
137 if (getDescriptor() instanceof FlagAttributeDescriptor &&
138 ((FlagAttributeDescriptor) getDescriptor()).getNames() != null) {
139 // Get enum values from the descriptor
140 values = ((FlagAttributeDescriptor) getDescriptor()).getNames();
143 if (values == null) {
144 // or from the AndroidTargetData
145 UiElementNode uiNode = getUiParent();
146 AndroidEditor editor = uiNode.getEditor();
147 AndroidTargetData data = editor.getTargetData();
149 values = data.getAttributeValues(element_name, attr_name);
157 * Shows a dialog letting the user choose a set of enum, and returns a string
158 * containing the result.
160 public String showDialog(Shell shell, String currentValue) {
161 FlagSelectionDialog dlg = new FlagSelectionDialog(
162 shell, currentValue.trim().split("\\s*\\|\\s*")); //$NON-NLS-1$
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("|"); //$NON-NLS-1$
176 return buf.toString();
184 * Displays a list of flag names with checkboxes.
186 private class FlagSelectionDialog extends SelectionStatusDialog {
188 private Set<String> mCurrentSet;
189 private Table mTable;
191 public FlagSelectionDialog(Shell parentShell, String[] currentNames) {
194 mCurrentSet = new HashSet<String>();
195 for (String name : currentNames) {
196 if (name.length() > 0) {
197 mCurrentSet.add(name);
201 int shellStyle = getShellStyle();
202 setShellStyle(shellStyle | SWT.MAX | SWT.RESIZE);
206 protected void computeResult() {
207 if (mTable != null) {
208 ArrayList<String> results = new ArrayList<String>();
210 for (TableItem item : mTable.getItems()) {
211 if (item.getChecked()) {
212 results.add((String)item.getData());
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());
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()));
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);
243 mTable.setHeaderVisible(false);
244 final TableColumn column = new TableColumn(mTable, SWT.NONE);
246 // List all the expected flag names and check those which are currently used
247 String[] names = getPossibleValues(null);
249 for (String name : names) {
250 TableItem item = new TableItem(mTable, SWT.NONE);
254 boolean hasName = mCurrentSet.contains(name);
255 item.setChecked(hasName);
257 mCurrentSet.remove(name);
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());
269 for (String name : mCurrentSet) {
270 TableItem item = new TableItem(mTable, SWT.NONE);
271 item.setText(String.format("%1$s (unknown flag)", name));
273 item.setChecked(true);
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() {
282 public void controlResized(ControlEvent e) {
283 Rectangle r = mTable.getClientArea();
284 column.setWidth(r.width);
288 mTable.addControlListener(listener);
289 listener.controlResized(null /* event not used */);
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 */
295 public void widgetDefaultSelected(SelectionEvent e) {
296 if (e.item instanceof TableItem) {
297 TableItem i = (TableItem) e.item;
298 i.setChecked(!i.getChecked());
300 super.widgetDefaultSelected(e);
304 Dialog.applyDialogFont(composite);
305 setHelpAvailable(false);