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.ui; 18 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; 21 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; 22 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestEditor; 23 import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper.ManifestSectionPart; 24 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; 25 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; 26 27 import org.eclipse.core.runtime.IStatus; 28 import org.eclipse.swt.widgets.Composite; 29 import org.eclipse.swt.widgets.Control; 30 import org.eclipse.ui.forms.IManagedForm; 31 import org.eclipse.ui.forms.widgets.FormToolkit; 32 import org.eclipse.ui.forms.widgets.Section; 33 34 /** 35 * Generic page's section part that displays all attributes of a given {@link UiElementNode}. 36 * <p/> 37 * This part is designed to be displayed in a page that has a table column layout. 38 * It is linked to a specific {@link UiElementNode} and automatically displays all of its 39 * attributes, manages its dirty state and commits the attributes when necessary. 40 * <p/> 41 * No derivation is needed unless the UI or workflow needs to be extended. 42 */ 43 public class UiElementPart extends ManifestSectionPart { 44 45 /** A reference to the container editor */ 46 private ManifestEditor mEditor; 47 /** The {@link UiElementNode} manipulated by this SectionPart. It can be null. */ 48 private UiElementNode mUiElementNode; 49 /** Table that contains all the attributes */ 50 private Composite mTable; 51 UiElementPart(Composite body, FormToolkit toolkit, ManifestEditor editor, UiElementNode uiElementNode, String sectionTitle, String sectionDescription, int extra_style)52 public UiElementPart(Composite body, FormToolkit toolkit, ManifestEditor editor, 53 UiElementNode uiElementNode, String sectionTitle, String sectionDescription, 54 int extra_style) { 55 super(body, toolkit, extra_style, sectionDescription != null); 56 mEditor = editor; 57 mUiElementNode = uiElementNode; 58 setupSection(sectionTitle, sectionDescription); 59 60 if (uiElementNode == null) { 61 // This is serious and should never happen. Instead of crashing, simply abort. 62 // There will be no UI, which will prevent further damage. 63 AdtPlugin.log(IStatus.ERROR, "Missing node to edit!"); //$NON-NLS-1$ 64 return; 65 } 66 } 67 68 /** 69 * Returns the Editor associated with this part. 70 */ getEditor()71 public ManifestEditor getEditor() { 72 return mEditor; 73 } 74 75 /** 76 * Returns the {@link UiElementNode} associated with this part. 77 */ getUiElementNode()78 public UiElementNode getUiElementNode() { 79 return mUiElementNode; 80 } 81 82 /** 83 * Changes the element node handled by this part. 84 * 85 * @param uiElementNode The new element node for the part. 86 */ setUiElementNode(UiElementNode uiElementNode)87 public void setUiElementNode(UiElementNode uiElementNode) { 88 mUiElementNode = uiElementNode; 89 } 90 91 /** 92 * Initializes the form part. 93 * <p/> 94 * This is called by the owning managed form as soon as the part is added to the form, 95 * which happens right after the part is actually created. 96 */ 97 @Override initialize(IManagedForm form)98 public void initialize(IManagedForm form) { 99 super.initialize(form); 100 createFormControls(form); 101 } 102 103 /** 104 * Setup the section that contains this part. 105 * <p/> 106 * This is called by the constructor to set the section's title and description 107 * with parameters given in the constructor. 108 * <br/> 109 * Derived class override this if needed, however in most cases the default 110 * implementation should be enough. 111 * 112 * @param sectionTitle The section part's title 113 * @param sectionDescription The section part's description 114 */ setupSection(String sectionTitle, String sectionDescription)115 protected void setupSection(String sectionTitle, String sectionDescription) { 116 Section section = getSection(); 117 section.setText(sectionTitle); 118 section.setDescription(sectionDescription); 119 } 120 121 /** 122 * Create the controls to edit the attributes for the given ElementDescriptor. 123 * <p/> 124 * This MUST not be called by the constructor. Instead it must be called from 125 * <code>initialize</code> (i.e. right after the form part is added to the managed form.) 126 * <p/> 127 * Derived classes can override this if necessary. 128 * 129 * @param managedForm The owner managed form 130 */ createFormControls(IManagedForm managedForm)131 protected void createFormControls(IManagedForm managedForm) { 132 setTable(createTableLayout(managedForm.getToolkit(), 2 /* numColumns */)); 133 134 createUiAttributes(managedForm); 135 } 136 137 /** 138 * Sets the table where the attribute UI needs to be created. 139 */ setTable(Composite table)140 protected void setTable(Composite table) { 141 mTable = table; 142 } 143 144 /** 145 * Returns the table where the attribute UI needs to be created. 146 */ getTable()147 protected Composite getTable() { 148 return mTable; 149 } 150 151 /** 152 * Add all the attribute UI widgets into the underlying table layout. 153 * 154 * @param managedForm The owner managed form 155 */ createUiAttributes(IManagedForm managedForm)156 protected void createUiAttributes(IManagedForm managedForm) { 157 Composite table = getTable(); 158 if (table == null || managedForm == null) { 159 return; 160 } 161 162 // Remove any old UI controls 163 for (Control c : table.getChildren()) { 164 c.dispose(); 165 } 166 167 fillTable(table, managedForm); 168 169 // Tell the section that the layout has changed. 170 layoutChanged(); 171 } 172 173 /** 174 * Actually fills the table. 175 * This is called by {@link #createUiAttributes(IManagedForm)} to populate the new 176 * table. The default implementation is to use 177 * {@link #insertUiAttributes(UiElementNode, Composite, IManagedForm)} to actually 178 * place the attributes of the default {@link UiElementNode} in the table. 179 * <p/> 180 * Derived classes can override this to add controls in the table before or after. 181 * 182 * @param table The table to fill. It must have 2 columns. 183 * @param managedForm The managed form for new controls. 184 */ fillTable(Composite table, IManagedForm managedForm)185 protected void fillTable(Composite table, IManagedForm managedForm) { 186 int inserted = insertUiAttributes(mUiElementNode, table, managedForm); 187 188 if (inserted == 0) { 189 createLabel(table, managedForm.getToolkit(), 190 "No attributes to display, waiting for SDK to finish loading...", 191 null /* tooltip */ ); 192 } 193 } 194 195 /** 196 * Insert the UI attributes of the given {@link UiElementNode} in the given table. 197 * 198 * @param uiNode The {@link UiElementNode} that contains the attributes to display. 199 * Must not be null. 200 * @param table The table to fill. It must have 2 columns. 201 * @param managedForm The managed form for new controls. 202 * @return The number of UI attributes inserted. It is >= 0. 203 */ insertUiAttributes(UiElementNode uiNode, Composite table, IManagedForm managedForm)204 protected int insertUiAttributes(UiElementNode uiNode, Composite table, IManagedForm managedForm) { 205 if (uiNode == null || table == null || managedForm == null) { 206 return 0; 207 } 208 209 // To iterate over all attributes, we use the {@link ElementDescriptor} instead 210 // of the {@link UiElementNode} because the attributes' order is guaranteed in the 211 // descriptor but not in the node itself. 212 AttributeDescriptor[] attr_desc_list = uiNode.getAttributeDescriptors(); 213 for (AttributeDescriptor attr_desc : attr_desc_list) { 214 if (attr_desc instanceof XmlnsAttributeDescriptor) { 215 // Do not show hidden attributes 216 continue; 217 } 218 219 UiAttributeNode ui_attr = uiNode.findUiAttribute(attr_desc); 220 if (ui_attr != null) { 221 ui_attr.createUiControl(table, managedForm); 222 } else { 223 // The XML has an extra attribute which wasn't declared in 224 // AndroidManifestDescriptors. This is not a problem, we just ignore it. 225 AdtPlugin.log(IStatus.WARNING, 226 "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$ 227 attr_desc.getXmlLocalName(), 228 uiNode.getDescriptor().getXmlName()); 229 } 230 } 231 return attr_desc_list.length; 232 } 233 234 /** 235 * Tests whether the part is dirty i.e. its widgets have state that is 236 * newer than the data in the model. 237 * <p/> 238 * This is done by iterating over all attributes and updating the super's 239 * internal dirty flag. Stop once at least one attribute is dirty. 240 * 241 * @return <code>true</code> if the part is dirty, <code>false</code> 242 * otherwise. 243 */ 244 @Override isDirty()245 public boolean isDirty() { 246 if (mUiElementNode != null && !super.isDirty()) { 247 for (UiAttributeNode ui_attr : mUiElementNode.getAllUiAttributes()) { 248 if (ui_attr.isDirty()) { 249 markDirty(); 250 break; 251 } 252 } 253 } 254 return super.isDirty(); 255 } 256 257 /** 258 * If part is displaying information loaded from a model, this method 259 * instructs it to commit the new (modified) data back into the model. 260 * 261 * @param onSave 262 * indicates if commit is called during 'save' operation or for 263 * some other reason (for example, if form is contained in a 264 * wizard or a multi-page editor and the user is about to leave 265 * the page). 266 */ 267 @Override commit(boolean onSave)268 public void commit(boolean onSave) { 269 if (mUiElementNode != null) { 270 mEditor.wrapEditXmlModel(new Runnable() { 271 @Override 272 public void run() { 273 for (UiAttributeNode ui_attr : mUiElementNode.getAllUiAttributes()) { 274 ui_attr.commit(); 275 } 276 } 277 }); 278 } 279 280 // We need to call super's commit after we synchronized the nodes to make sure we 281 // reset the dirty flag after all the side effects from committing have occurred. 282 super.commit(onSave); 283 } 284 } 285