1 /* 2 * Copyright (C) 2012 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 package com.android.ide.eclipse.adt.internal.editors.layout.properties; 17 18 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_MARGIN; 19 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX; 20 21 import com.android.annotations.NonNull; 22 import com.android.annotations.Nullable; 23 import com.android.ide.common.api.IAttributeInfo; 24 import com.android.ide.common.api.IAttributeInfo.Format; 25 import com.android.ide.eclipse.adt.AdtPlugin; 26 import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; 27 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 28 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; 29 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; 30 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ViewHierarchy; 31 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; 32 33 import org.eclipse.jface.fieldassist.IContentProposal; 34 import org.eclipse.jface.fieldassist.IContentProposalProvider; 35 import org.eclipse.jface.viewers.ILabelProvider; 36 import org.eclipse.jface.viewers.LabelProvider; 37 import org.eclipse.swt.graphics.Image; 38 import org.eclipse.ui.views.properties.IPropertyDescriptor; 39 import org.eclipse.wb.internal.core.model.property.Property; 40 import org.eclipse.wb.internal.core.model.property.editor.PropertyEditor; 41 import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipProvider; 42 import org.eclipse.wb.internal.core.model.property.table.PropertyTooltipTextProvider; 43 import org.w3c.dom.Attr; 44 import org.w3c.dom.Element; 45 46 import java.util.EnumSet; 47 import java.util.Map; 48 49 /** 50 * An Android XML property 51 */ 52 class XmlProperty extends Property { 53 private PropertyFactory mFactory; 54 final AttributeDescriptor mDescriptor; 55 private UiViewElementNode mNode; 56 private Property mParent; 57 58 XmlProperty( 59 @NonNull PropertyEditor editor, 60 @NonNull PropertyFactory factory, 61 @NonNull UiViewElementNode node, 62 @NonNull AttributeDescriptor descriptor) { 63 super(editor); 64 mFactory = factory; 65 mNode = node; 66 mDescriptor = descriptor; 67 } 68 69 public PropertyFactory getFactory() { 70 return mFactory; 71 } 72 73 public UiViewElementNode getNode() { 74 return mNode; 75 } 76 77 public AttributeDescriptor getDescriptor() { 78 return mDescriptor; 79 } 80 81 @Override 82 public String getName() { 83 return mDescriptor.getXmlLocalName(); 84 } 85 86 @Override 87 public String getTitle() { 88 String name = mDescriptor.getXmlLocalName(); 89 int nameLength = name.length(); 90 91 if (name.startsWith(ATTR_LAYOUT_PREFIX)) { 92 if (name.startsWith(ATTR_LAYOUT_MARGIN) 93 && nameLength > ATTR_LAYOUT_MARGIN.length()) { 94 name = name.substring(ATTR_LAYOUT_MARGIN.length()); 95 } else { 96 name = name.substring(ATTR_LAYOUT_PREFIX.length()); 97 } 98 } 99 100 // Capitalize 101 name = DescriptorsUtils.capitalize(name); 102 103 // If we're nested within a complex property, say "Line Spacing", don't 104 // include "Line Spacing " as a prefix for each property here 105 if (mParent != null) { 106 String parentTitle = mParent.getTitle(); 107 if (name.startsWith(parentTitle)) { 108 int parentTitleLength = parentTitle.length(); 109 if (parentTitleLength < nameLength) { 110 if (nameLength > parentTitleLength && 111 Character.isWhitespace(name.charAt(parentTitleLength))) { 112 parentTitleLength++; 113 } 114 name = name.substring(parentTitleLength); 115 } 116 } 117 } 118 119 return name; 120 } 121 122 @Override 123 public <T> T getAdapter(Class<T> adapter) { 124 // tooltip 125 if (adapter == PropertyTooltipProvider.class) { 126 return adapter.cast(new PropertyTooltipTextProvider() { 127 @Override 128 protected String getText(Property p) throws Exception { 129 if (mDescriptor instanceof IPropertyDescriptor) { 130 IPropertyDescriptor d = (IPropertyDescriptor) mDescriptor; 131 return d.getDescription(); 132 } 133 134 return null; 135 } 136 }); 137 } else if (adapter == IContentProposalProvider.class) { 138 IAttributeInfo info = mDescriptor.getAttributeInfo(); 139 if (info != null) { 140 EnumSet<Format> formats = info.getFormats(); 141 if (formats.contains(Format.FLAG)) { 142 return adapter.cast(new FlagValueCompleter(this, info.getFlagValues())); 143 } else if (formats.contains(Format.ENUM)) { 144 return adapter.cast(new FlagValueCompleter(this, info.getEnumValues())); 145 } 146 } 147 // Fallback: complete values on resource values 148 return adapter.cast(new ResourceValueCompleter(this)); 149 } else if (adapter == ILabelProvider.class) { 150 return adapter.cast(new LabelProvider() { 151 @Override 152 public Image getImage(Object element) { 153 return AdtPlugin.getAndroidLogo(); 154 } 155 156 @Override 157 public String getText(Object element) { 158 return ((IContentProposal) element).getLabel(); 159 } 160 }); 161 } 162 return super.getAdapter(adapter); 163 } 164 165 @Override 166 public boolean isModified() throws Exception { 167 Object s = null; 168 try { 169 Element element = (Element) mNode.getXmlNode(); 170 String name = mDescriptor.getXmlLocalName(); 171 String uri = mDescriptor.getNamespaceUri(); 172 if (uri != null) { 173 return element.hasAttributeNS(uri, name); 174 } else { 175 return element.hasAttribute(name); 176 } 177 } catch (Exception e) { 178 // pass 179 } 180 return s != null && s.toString().length() > 0; 181 } 182 183 public String getStringValue() { 184 Element element = (Element) mNode.getXmlNode(); 185 String name = mDescriptor.getXmlLocalName(); 186 String uri = mDescriptor.getNamespaceUri(); 187 Attr attr; 188 if (uri != null) { 189 attr = element.getAttributeNodeNS(uri, name); 190 } else { 191 attr = element.getAttributeNode(name); 192 } 193 if (attr != null) { 194 return attr.getValue(); 195 } 196 197 Object viewObject = getFactory().getCurrentViewObject(); 198 if (viewObject != null) { 199 ViewHierarchy views = getGraphicalEditor().getCanvasControl().getViewHierarchy(); 200 Map<String, String> defaultProperties = views.getDefaultProperties(viewObject); 201 if (defaultProperties != null) { 202 return defaultProperties.get(name); 203 } 204 } 205 206 return null; 207 } 208 209 @Override 210 public Object getValue() throws Exception { 211 return getStringValue(); 212 } 213 214 @Override 215 public void setValue(Object value) throws Exception { 216 CommonXmlEditor editor = getXmlEditor(); 217 if (editor == null) { 218 return; 219 } 220 final String attribute = mDescriptor.getXmlLocalName(); 221 final String xmlValue = value != null && value != UNKNOWN_VALUE ? value.toString() : null; 222 editor.wrapUndoEditXmlModel( 223 String.format("Set \"%1$s\" to \"%2$s\"", attribute, xmlValue), 224 new Runnable() { 225 @Override 226 public void run() { 227 mNode.setAttributeValue(attribute, 228 mDescriptor.getNamespaceUri(), xmlValue, true /*override*/); 229 mNode.commitDirtyAttributesToXml(); 230 } 231 }); 232 } 233 234 @Override 235 public Property getComposite(Property[] properties) { 236 return XmlPropertyComposite.create(properties); 237 } 238 239 @Nullable 240 GraphicalEditorPart getGraphicalEditor() { 241 return mFactory.getGraphicalEditor(); 242 } 243 244 @Nullable CommonXmlEditor getXmlEditor() { 245 GraphicalEditorPart graphicalEditor = getGraphicalEditor(); 246 if (graphicalEditor != null) { 247 return graphicalEditor.getEditorDelegate().getEditor(); 248 } 249 250 return null; 251 } 252 253 public Property getParent() { 254 return mParent; 255 } 256 257 public void setParent(Property parent) { 258 mParent = parent; 259 } 260 261 @Override 262 public String toString() { 263 return getName() + ":" + getPriority(); 264 } 265 } 266