Home | History | Annotate | Download | only in uimodel
      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.uimodel;
     18 
     19 import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
     20 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
     21 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_PREFIX;
     22 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
     23 import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
     24 import static com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors.ATTR_COLOR;
     25 
     26 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
     27 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
     28 
     29 import org.eclipse.swt.widgets.Composite;
     30 import org.eclipse.ui.forms.IManagedForm;
     31 import org.w3c.dom.Node;
     32 
     33 import java.util.Comparator;
     34 
     35 /**
     36  * Represents an XML attribute that can be modified by the XML editor's user interface.
     37  * <p/>
     38  * The characteristics of an {@link UiAttributeNode} are declared by a
     39  * corresponding {@link AttributeDescriptor}.
     40  * <p/>
     41  * This is an abstract class. Derived classes must implement the creation of the UI
     42  * and manage its synchronization with the XML.
     43  */
     44 public abstract class UiAttributeNode implements Comparable<UiAttributeNode> {
     45 
     46     private AttributeDescriptor mDescriptor;
     47     private UiElementNode mUiParent;
     48     private boolean mIsDirty;
     49     private boolean mHasError;
     50 
     51     /** Creates a new {@link UiAttributeNode} linked to a specific {@link AttributeDescriptor}
     52      * and the corresponding runtime {@link UiElementNode} parent. */
     53     public UiAttributeNode(AttributeDescriptor attributeDescriptor, UiElementNode uiParent) {
     54         mDescriptor = attributeDescriptor;
     55         mUiParent = uiParent;
     56     }
     57 
     58     /** Returns the {@link AttributeDescriptor} specific to this UI attribute node */
     59     public final AttributeDescriptor getDescriptor() {
     60         return mDescriptor;
     61     }
     62 
     63     /** Returns the {@link UiElementNode} that owns this {@link UiAttributeNode} */
     64     public final UiElementNode getUiParent() {
     65         return mUiParent;
     66     }
     67 
     68     /** Returns the current value of the node. */
     69     public abstract String getCurrentValue();
     70 
     71     /**
     72      * @return True if the attribute has been changed since it was last loaded
     73      *         from the XML model.
     74      */
     75     public final boolean isDirty() {
     76         return mIsDirty;
     77     }
     78 
     79     /**
     80      * Sets whether the attribute is dirty and also notifies the editor some part's dirty
     81      * flag as changed.
     82      * <p/>
     83      * Subclasses should set the to true as a result of user interaction with the widgets in
     84      * the section and then should set to false when the commit() method completed.
     85      *
     86      * @param isDirty the new value to set the dirty-flag to
     87      */
     88     public void setDirty(boolean isDirty) {
     89         boolean wasDirty = mIsDirty;
     90         mIsDirty = isDirty;
     91         // TODO: for unknown attributes, getParent() != null && getParent().getEditor() != null
     92         if (wasDirty != isDirty) {
     93             AndroidXmlEditor editor = getUiParent().getEditor();
     94             if (editor != null) {
     95                 editor.editorDirtyStateChanged();
     96             }
     97         }
     98     }
     99 
    100     /**
    101      * Sets the error flag value.
    102      * @param errorFlag the error flag
    103      */
    104     public final void setHasError(boolean errorFlag) {
    105         mHasError = errorFlag;
    106     }
    107 
    108     /**
    109      * Returns whether this node has errors.
    110      */
    111     public final boolean hasError() {
    112         return mHasError;
    113     }
    114 
    115     /**
    116      * Called once by the parent user interface to creates the necessary
    117      * user interface to edit this attribute.
    118      * <p/>
    119      * This method can be called more than once in the life cycle of an UI node,
    120      * typically when the UI is part of a master-detail tree, as pages are swapped.
    121      *
    122      * @param parent The composite where to create the user interface.
    123      * @param managedForm The managed form owning this part.
    124      */
    125     public abstract void createUiControl(Composite parent, IManagedForm managedForm);
    126 
    127     /**
    128      * Used to get a list of all possible values for this UI attribute.
    129      * <p/>
    130      * This is used, among other things, by the XML Content Assists to complete values
    131      * for an attribute.
    132      * <p/>
    133      * Implementations that do not have any known values should return null.
    134      *
    135      * @param prefix An optional prefix string, which is whatever the user has already started
    136      *   typing. Can be null or an empty string. The implementation can use this to filter choices
    137      *   and only return strings that match this prefix. A lazy or default implementation can
    138      *   simply ignore this and return everything.
    139      * @return A list of possible completion values, and empty array or null.
    140      */
    141     public abstract String[] getPossibleValues(String prefix);
    142 
    143     /**
    144      * Called when the XML is being loaded or has changed to
    145      * update the value held by this user interface attribute node.
    146      * <p/>
    147      * The XML Node <em>may</em> be null, which denotes that the attribute is not
    148      * specified in the XML model. In general, this means the "default" value of the
    149      * attribute should be used.
    150      * <p/>
    151      * The caller doesn't really know if attributes have changed,
    152      * so it will call this to refresh the attribute anyway. It's up to the
    153      * UI implementation to minimize refreshes.
    154      *
    155      * @param node the node to read the value from
    156      */
    157     public abstract void updateValue(Node node);
    158 
    159     /**
    160      * Called by the user interface when the editor is saved or its state changed
    161      * and the modified attributes must be committed (i.e. written) to the XML model.
    162      * <p/>
    163      * Important behaviors:
    164      * <ul>
    165      * <li>The caller *must* have called IStructuredModel.aboutToChangeModel before.
    166      *     The implemented methods must assume it is safe to modify the XML model.
    167      * <li>On success, the implementation *must* call setDirty(false).
    168      * <li>On failure, the implementation can fail with an exception, which
    169      *     is trapped and logged by the caller, or do nothing, whichever is more
    170      *     appropriate.
    171      * </ul>
    172      */
    173     public abstract void commit();
    174 
    175     // ---- Implements Comparable ----
    176     public int compareTo(UiAttributeNode o) {
    177         return compareAttributes(mDescriptor.getXmlLocalName(), o.mDescriptor.getXmlLocalName());
    178     }
    179 
    180     /**
    181      * Returns {@link Comparator} values for ordering attributes in the following
    182      * order:
    183      * <ul>
    184      *   <li> id
    185      *   <li> style
    186      *   <li> layout_width
    187      *   <li> layout_height
    188      *   <li> other layout params, sorted alphabetically
    189      *   <li> other attributes, sorted alphabetically
    190      * </ul>
    191      *
    192      * @param name1 the first attribute name to compare
    193      * @param name2 the second attribute name to compare
    194      * @return a negative number if name1 should be ordered before name2
    195      */
    196     public static int compareAttributes(String name1, String name2) {
    197       int priority1 = getAttributePriority(name1);
    198       int priority2 = getAttributePriority(name2);
    199       if (priority1 != priority2) {
    200           return priority1 - priority2;
    201       }
    202 
    203       // Sort remaining attributes alphabetically
    204       return name1.compareTo(name2);
    205     }
    206 
    207     /** Returns a sorting priority for the given attribute name */
    208     private static int getAttributePriority(String name) {
    209         if (ATTR_ID.equals(name)) {
    210             return 10;
    211         }
    212 
    213         if (ATTR_STYLE.equals(name)) {
    214             return 20;
    215         }
    216 
    217         if (name.startsWith(ATTR_LAYOUT_PREFIX)) {
    218             // Width and height are special cased because we (a) want width and height
    219             // before the other layout attributes, and (b) we want width to sort before height
    220             // even though it comes after it alphabetically.
    221             if (name.equals(ATTR_LAYOUT_WIDTH)) {
    222                 return 30;
    223             }
    224             if (name.equals(ATTR_LAYOUT_HEIGHT)) {
    225                 return 40;
    226             }
    227 
    228             return 50;
    229         }
    230 
    231         // "color" sorts to the end
    232         if (ATTR_COLOR.equals(name)) {
    233             return 100;
    234         }
    235 
    236         return 60;
    237     }
    238 }
    239