1 /* 2 * Copyright (C) 2007 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.SdkConstants; 20 import com.android.ide.eclipse.adt.AdtPlugin; 21 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; 22 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 23 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; 24 import com.android.ide.eclipse.adt.internal.editors.descriptors.ListAttributeDescriptor; 25 import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor; 26 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper; 27 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; 28 29 import org.eclipse.core.runtime.IStatus; 30 import org.eclipse.swt.SWT; 31 import org.eclipse.swt.events.DisposeEvent; 32 import org.eclipse.swt.events.DisposeListener; 33 import org.eclipse.swt.events.ModifyEvent; 34 import org.eclipse.swt.events.ModifyListener; 35 import org.eclipse.swt.events.SelectionAdapter; 36 import org.eclipse.swt.events.SelectionEvent; 37 import org.eclipse.swt.widgets.Combo; 38 import org.eclipse.swt.widgets.Composite; 39 import org.eclipse.swt.widgets.Label; 40 import org.eclipse.ui.forms.IManagedForm; 41 import org.eclipse.ui.forms.widgets.FormToolkit; 42 import org.eclipse.ui.forms.widgets.TableWrapData; 43 44 /** 45 * Represents an XML attribute which has possible built-in values, and can be modified by 46 * an editable Combo box. 47 * <p/> 48 * See {@link UiTextAttributeNode} for more information. 49 */ 50 public class UiListAttributeNode extends UiAbstractTextAttributeNode { 51 52 protected Combo mCombo; 53 54 public UiListAttributeNode(ListAttributeDescriptor attributeDescriptor, 55 UiElementNode uiParent) { 56 super(attributeDescriptor, uiParent); 57 } 58 59 /* (non-java doc) 60 * Creates a label widget and an associated text field. 61 * <p/> 62 * As most other parts of the android manifest editor, this assumes the 63 * parent uses a table layout with 2 columns. 64 */ 65 @Override 66 public final void createUiControl(final Composite parent, IManagedForm managedForm) { 67 FormToolkit toolkit = managedForm.getToolkit(); 68 TextAttributeDescriptor desc = (TextAttributeDescriptor) getDescriptor(); 69 70 Label label = toolkit.createLabel(parent, desc.getUiName()); 71 label.setLayoutData(new TableWrapData(TableWrapData.LEFT, TableWrapData.MIDDLE)); 72 SectionHelper.addControlTooltip(label, DescriptorsUtils.formatTooltip(desc.getTooltip())); 73 74 int style = SWT.DROP_DOWN; 75 mCombo = new Combo(parent, style); 76 TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.MIDDLE); 77 twd.maxWidth = 100; 78 mCombo.setLayoutData(twd); 79 80 fillCombo(); 81 82 setTextWidgetValue(getCurrentValue()); 83 84 mCombo.addModifyListener(new ModifyListener() { 85 /** 86 * Sent when the text is modified, whether by the user via manual 87 * input or programmatic input via setText(). 88 */ 89 @Override 90 public void modifyText(ModifyEvent e) { 91 onComboChange(); 92 } 93 }); 94 95 mCombo.addSelectionListener(new SelectionAdapter() { 96 /** Sent when the text is changed from a list selection. */ 97 @Override 98 public void widgetSelected(SelectionEvent e) { 99 onComboChange(); 100 } 101 }); 102 103 // Remove self-reference when the widget is disposed 104 mCombo.addDisposeListener(new DisposeListener() { 105 @Override 106 public void widgetDisposed(DisposeEvent e) { 107 mCombo = null; 108 } 109 }); 110 } 111 112 protected void fillCombo() { 113 String[] values = getPossibleValues(null); 114 115 if (values == null) { 116 AdtPlugin.log(IStatus.ERROR, 117 "FrameworkResourceManager did not provide values yet for %1$s", 118 getDescriptor().getXmlLocalName()); 119 } else { 120 for (String value : values) { 121 mCombo.add(value); 122 } 123 } 124 } 125 126 /** 127 * Get the list values, either from the initial values set in the attribute 128 * or by querying the framework resource parser. 129 * 130 * {@inheritDoc} 131 */ 132 @Override 133 public String[] getPossibleValues(String prefix) { 134 AttributeDescriptor descriptor = getDescriptor(); 135 UiElementNode uiParent = getUiParent(); 136 137 String attr_name = descriptor.getXmlLocalName(); 138 String element_name = uiParent.getDescriptor().getXmlName(); 139 140 // FrameworkResourceManager expects a specific prefix for the attribute. 141 String nsPrefix = ""; 142 if (SdkConstants.NS_RESOURCES.equals(descriptor.getNamespaceUri())) { 143 nsPrefix = SdkConstants.ANDROID_NS_NAME + ':'; 144 } else if (SdkConstants.XMLNS_URI.equals(descriptor.getNamespaceUri())) { 145 nsPrefix = SdkConstants.XMLNS_PREFIX; 146 } 147 attr_name = nsPrefix + attr_name; 148 149 String[] values = null; 150 151 if (descriptor instanceof ListAttributeDescriptor && 152 ((ListAttributeDescriptor) descriptor).getValues() != null) { 153 // Get enum values from the descriptor 154 values = ((ListAttributeDescriptor) descriptor).getValues(); 155 } 156 157 if (values == null) { 158 // or from the AndroidTargetData 159 UiElementNode uiNode = getUiParent(); 160 AndroidXmlEditor editor = uiNode.getEditor(); 161 AndroidTargetData data = editor.getTargetData(); 162 if (data != null) { 163 // get the great-grand-parent descriptor. 164 165 // the parent should always exist. 166 UiElementNode grandParentNode = uiParent.getUiParent(); 167 168 String greatGrandParentNodeName = null; 169 if (grandParentNode != null) { 170 UiElementNode greatGrandParentNode = grandParentNode.getUiParent(); 171 if (greatGrandParentNode != null) { 172 greatGrandParentNodeName = 173 greatGrandParentNode.getDescriptor().getXmlName(); 174 } 175 } 176 177 values = data.getAttributeValues(element_name, attr_name, greatGrandParentNodeName); 178 } 179 } 180 181 return values; 182 } 183 184 @Override 185 public String getTextWidgetValue() { 186 if (mCombo != null) { 187 return mCombo.getText(); 188 } 189 190 return null; 191 } 192 193 @Override 194 public final boolean isValid() { 195 return mCombo != null; 196 } 197 198 @Override 199 public void setTextWidgetValue(String value) { 200 if (mCombo != null) { 201 mCombo.setText(value); 202 } 203 } 204 205 /** 206 * Handles Combo change, either from text edit or from selection change. 207 * <p/> 208 * Simply mark the attribute as dirty if it really changed. 209 * The container SectionPart will collect these flag and manage them. 210 */ 211 private void onComboChange() { 212 if (!isInInternalTextModification() && 213 !isDirty() && 214 mCombo != null && 215 getCurrentValue() != null && 216 !mCombo.getText().equals(getCurrentValue())) { 217 setDirty(true); 218 } 219 } 220 } 221