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