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