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.layout.uimodel; 18 19 import static com.android.SdkConstants.ANDROID_NS_NAME; 20 import static com.android.SdkConstants.ANDROID_URI; 21 import static com.android.SdkConstants.ATTR_CLASS; 22 import static com.android.SdkConstants.ATTR_ORIENTATION; 23 import static com.android.SdkConstants.FQCN_FRAME_LAYOUT; 24 import static com.android.SdkConstants.LINEAR_LAYOUT; 25 import static com.android.SdkConstants.VALUE_VERTICAL; 26 import static com.android.SdkConstants.VIEW_TAG; 27 28 import com.android.ide.eclipse.adt.AdtPlugin; 29 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; 30 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 31 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 32 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; 33 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; 34 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; 35 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; 36 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; 37 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; 38 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; 39 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; 40 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 41 import com.android.sdklib.IAndroidTarget; 42 43 import org.eclipse.core.resources.IMarker; 44 import org.eclipse.core.resources.IProject; 45 import org.eclipse.swt.graphics.Image; 46 import org.w3c.dom.Element; 47 import org.w3c.dom.Node; 48 49 /** 50 * Specialized version of {@link UiElementNode} for the {@link ViewElementDescriptor}s. 51 */ 52 public class UiViewElementNode extends UiElementNode { 53 54 /** An AttributeDescriptor array that depends on the current UiParent. */ 55 private AttributeDescriptor[] mCachedAttributeDescriptors; 56 57 public UiViewElementNode(ViewElementDescriptor elementDescriptor) { 58 super(elementDescriptor); 59 } 60 61 /** 62 * Returns an AttributeDescriptor array that depends on the current UiParent. 63 * <p/> 64 * The array merges both "direct" attributes with the descriptor layout attributes. 65 * The array instance is cached and cleared if the UiParent is changed. 66 */ 67 @Override 68 public AttributeDescriptor[] getAttributeDescriptors() { 69 if (!getDescriptor().syncAttributes()) { 70 mCachedAttributeDescriptors = null; 71 } 72 if (mCachedAttributeDescriptors != null) { 73 return mCachedAttributeDescriptors; 74 } 75 76 UiElementNode ui_parent = getUiParent(); 77 AttributeDescriptor[] direct_attrs = super.getAttributeDescriptors(); 78 mCachedAttributeDescriptors = direct_attrs; 79 80 // Compute layout attributes: These depend on the *parent* this widget is within 81 AttributeDescriptor[] layout_attrs = null; 82 boolean need_xmlns = false; 83 84 if (ui_parent instanceof UiDocumentNode) { 85 // Limitation: right now the layout behaves as if everything was 86 // owned by a FrameLayout. 87 // TODO replace by something user-configurable. 88 89 IProject project = getEditor().getProject(); 90 if (project != null) { 91 Sdk currentSdk = Sdk.getCurrent(); 92 if (currentSdk != null) { 93 IAndroidTarget target = currentSdk.getTarget(project); 94 if (target != null) { 95 AndroidTargetData data = currentSdk.getTargetData(target); 96 if (data != null) { 97 LayoutDescriptors descriptors = data.getLayoutDescriptors(); 98 ViewElementDescriptor desc = 99 descriptors.findDescriptorByClass(FQCN_FRAME_LAYOUT); 100 if (desc != null) { 101 layout_attrs = desc.getLayoutAttributes(); 102 need_xmlns = true; 103 } 104 } 105 } 106 } 107 } 108 } else if (ui_parent instanceof UiViewElementNode) { 109 layout_attrs = 110 ((ViewElementDescriptor) ui_parent.getDescriptor()).getLayoutAttributes(); 111 } 112 113 if (layout_attrs == null || layout_attrs.length == 0) { 114 return mCachedAttributeDescriptors; 115 } 116 117 mCachedAttributeDescriptors = 118 new AttributeDescriptor[direct_attrs.length + 119 layout_attrs.length + 120 (need_xmlns ? 1 : 0)]; 121 System.arraycopy(direct_attrs, 0, 122 mCachedAttributeDescriptors, 0, 123 direct_attrs.length); 124 System.arraycopy(layout_attrs, 0, 125 mCachedAttributeDescriptors, direct_attrs.length, 126 layout_attrs.length); 127 if (need_xmlns) { 128 AttributeDescriptor desc = new XmlnsAttributeDescriptor(ANDROID_NS_NAME, ANDROID_URI); 129 mCachedAttributeDescriptors[direct_attrs.length + layout_attrs.length] = desc; 130 } 131 132 return mCachedAttributeDescriptors; 133 } 134 135 public Image getIcon() { 136 ElementDescriptor desc = getDescriptor(); 137 if (desc != null) { 138 Image img = null; 139 // Special case for the common case of vertical linear layouts: 140 // show vertical linear icon (the default icon shows horizontal orientation) 141 String uiName = desc.getUiName(); 142 IconFactory icons = IconFactory.getInstance(); 143 if (uiName.equals(LINEAR_LAYOUT)) { 144 Element e = (Element) getXmlNode(); 145 if (VALUE_VERTICAL.equals(e.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION))) { 146 IconFactory factory = icons; 147 img = factory.getIcon("VerticalLinearLayout"); //$NON-NLS-1$ 148 } 149 } else if (uiName.equals(VIEW_TAG)) { 150 Node xmlNode = getXmlNode(); 151 if (xmlNode instanceof Element) { 152 String className = ((Element) xmlNode).getAttribute(ATTR_CLASS); 153 if (className != null && className.length() > 0) { 154 int index = className.lastIndexOf('.'); 155 if (index != -1) { 156 className = "customView"; //$NON-NLS-1$ 157 } 158 img = icons.getIcon(className); 159 } 160 } 161 162 if (img == null) { 163 // Can't have both view.png and View.png; issues on case sensitive vs 164 // case insensitive file systems 165 img = icons.getIcon("View"); //$NON-NLS-1$ 166 } 167 } 168 if (img == null) { 169 img = desc.getGenericIcon(); 170 } 171 172 if (img != null) { 173 AndroidXmlEditor editor = getEditor(); 174 if (editor != null) { 175 LayoutEditorDelegate delegate = LayoutEditorDelegate.fromEditor(editor); 176 if (delegate != null) { 177 IMarker marker = delegate.getIssueForNode(this); 178 if (marker != null) { 179 int severity = marker.getAttribute(IMarker.SEVERITY, 0); 180 if (severity == IMarker.SEVERITY_ERROR) { 181 return icons.addErrorIcon(img); 182 } else { 183 return icons.addWarningIcon(img); 184 } 185 } 186 } 187 } 188 189 return img; 190 } 191 192 return img; 193 } 194 195 return AdtPlugin.getAndroidLogo(); 196 } 197 198 /** 199 * Sets the parent of this UI node. 200 * <p/> 201 * Also removes the cached AttributeDescriptor array that depends on the current UiParent. 202 */ 203 @Override 204 protected void setUiParent(UiElementNode parent) { 205 super.setUiParent(parent); 206 mCachedAttributeDescriptors = null; 207 } 208 } 209