Home | History | Annotate | Download | only in ui
      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 
     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      */
     71     public ManifestEditor getEditor() {
     72         return mEditor;
     73     }
     74 
     75     /**
     76      * Returns the {@link UiElementNode} associated with this part.
     77      */
     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      */
     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
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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
    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