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 
UiViewElementNode(ViewElementDescriptor elementDescriptor)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
getAttributeDescriptors()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 
getIcon()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
setUiParent(UiElementNode parent)204     protected void setUiParent(UiElementNode parent) {
205         super.setUiParent(parent);
206         mCachedAttributeDescriptors = null;
207     }
208 }
209