FileDocCategorySizeDatePackage
UiElementDetail.javaAPI DocAndroid 1.5 API19074Wed May 06 22:41:10 BST 2009com.android.ide.eclipse.editors.ui.tree

UiElementDetail

public class UiElementDetail extends Object implements org.eclipse.ui.forms.IDetailsPage
Details page for the {@link UiElementNode} nodes in the tree view.

See IDetailsBase for more details.

Fields Summary
private com.android.ide.eclipse.editors.ui.SectionHelper.ManifestSectionPart
mMasterPart
The master-detail part, composed of a main tree and an auxiliary detail part
private org.eclipse.ui.forms.widgets.Section
mMasterSection
private com.android.ide.eclipse.editors.uimodel.UiElementNode
mCurrentUiElementNode
private org.eclipse.swt.widgets.Composite
mCurrentTable
private boolean
mIsDirty
private org.eclipse.ui.forms.IManagedForm
mManagedForm
private final UiTreeBlock
mTree
Constructors Summary
public UiElementDetail(UiTreeBlock tree)

        mTree = tree;
        mMasterPart = mTree.getMasterPart();
        mManagedForm = mMasterPart.getManagedForm();
    
Methods Summary
public voidcommit(boolean onSave)

        
        IStructuredModel model = mTree.getEditor().getModelForEdit();
        try {
            // Notify the model we're about to change data...
            model.aboutToChangeModel();

            if (mCurrentUiElementNode != null) {
                mCurrentUiElementNode.commit();
            }
            
            // Finally reset the dirty flag if everything was saved properly
            mIsDirty = false;
        } catch (Exception e) {
            AdtPlugin.log(e, "Detail node failed to commit XML attribute!"); //$NON-NLS-1$
        } finally {
            // Notify the model we're done modifying it. This must *always* be executed.
            model.changedModel();
            model.releaseFromEdit();
        }
    
public voidcreateContents(org.eclipse.swt.widgets.Composite parent)

        mMasterSection = createMasterSection(parent);
    
private org.eclipse.ui.forms.widgets.SectioncreateMasterSection(org.eclipse.swt.widgets.Composite parent)
Creates a TableWrapLayout in the DetailsPage, which in turns contains a Section. All the UI should be created in a layout which parent is the mSection itself. The hierarchy is:
DetailPage
+ TableWrapLayout
+ Section (with title/description && fill_grab horizontal)
+ TableWrapLayout [*]
+ Labels/Forms/etc... [*]
Both items marked with [*] are created by the derived classes to fit their needs.

param
parent Parent of the mSection (from createContents)
return
The new Section

        TableWrapLayout layout = new TableWrapLayout();
        layout.topMargin = 0;
        parent.setLayout(layout);

        FormToolkit toolkit = mManagedForm.getToolkit();
        Section section = toolkit.createSection(parent, Section.TITLE_BAR);
        section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB, TableWrapData.TOP));
        return section;
    
private org.eclipse.swt.widgets.CompositecreateSubSectionTable(org.eclipse.ui.forms.widgets.FormToolkit toolkit, org.eclipse.swt.widgets.Composite masterTable, java.lang.String title)
Create a sub Section and its embedding wrapper table with 2 columns.

return
The table, child of a new section.

        
        // The Section composite seems to ignore colspan when assigned a TableWrapData so
        // if the parent is a table with more than one column an extra table with one column
        // is inserted to respect colspan.
        int parentNumCol = ((TableWrapLayout) masterTable.getLayout()).numColumns;
        if (parentNumCol > 1) {
            masterTable = SectionHelper.createTableLayout(masterTable, toolkit, 1);
            TableWrapData twd = new TableWrapData(TableWrapData.FILL_GRAB);
            twd.maxWidth = AndroidEditor.TEXT_WIDTH_HINT;
            twd.colspan = parentNumCol;
            masterTable.setLayoutData(twd);
        }
        
        Composite table;
        Section section = toolkit.createSection(masterTable,
                Section.TITLE_BAR | Section.TWISTIE);

        // Add an expansion listener that will trigger a reflow on the parent
        // ScrolledPageBook (which is actually a SharedScrolledComposite). This will
        // recompute the correct size and adjust the scrollbar as needed.
        section.addExpansionListener(new IExpansionListener() {
            public void expansionStateChanged(ExpansionEvent e) {
                reflowMasterSection();
            }

            public void expansionStateChanging(ExpansionEvent e) {
                // pass
            }
        });

        section.setText(title);
        section.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB,
                                                TableWrapData.TOP));
        table = SectionHelper.createTableLayout(section, toolkit, 2 /* numColumns */);
        return table;
    
private voidcreateUiAttributeControls(org.eclipse.ui.forms.IManagedForm managedForm, com.android.ide.eclipse.editors.uimodel.UiElementNode ui_node)
Create the ui attribute controls to edit the attributes for the given ElementDescriptor.

This is called by the constructor. Derived classes can override this if necessary.

param
managedForm The managed form


        final ElementDescriptor elem_desc = ui_node.getDescriptor();
        mMasterSection.setText(String.format("Attributes for %1$s", ui_node.getShortDescription()));

        if (mCurrentUiElementNode != ui_node) {
            // Before changing the table, commit all dirty state.
            if (mIsDirty) {
                commit(false);
            }
            if (mCurrentTable != null) {
                mCurrentTable.dispose();
                mCurrentTable = null;
            }

            // To iterate over all attributes, we use the {@link ElementDescriptor} instead
            // of the {@link UiElementNode} because the attributes order is guaranteed in the
            // descriptor but not in the node itself.
            AttributeDescriptor[] attr_desc_list = ui_node.getAttributeDescriptors();

            // If the attribute list contains at least one SeparatorAttributeDescriptor,
            // sub-sections will be used. This needs to be known early as it influences the
            // creation of the master table.
            boolean useSubsections = false;
            for (AttributeDescriptor attr_desc : attr_desc_list) {
                if (attr_desc instanceof SeparatorAttributeDescriptor) {
                    // Sub-sections will be used. The default sections should no longer be
                    useSubsections = true;
                    break;
                }
            }

            FormToolkit toolkit = managedForm.getToolkit();
            Composite masterTable = SectionHelper.createTableLayout(mMasterSection,
                    toolkit, useSubsections ? 1 : 2 /* numColumns */);
            mCurrentTable = masterTable;

            mCurrentUiElementNode = ui_node;
                
            if (elem_desc.getTooltip() != null) {
                String tooltip;
                if (Sdk.getCurrent() != null &&
                        Sdk.getCurrent().getDocumentationBaseUrl() != null) {
                    tooltip = DescriptorsUtils.formatFormText(elem_desc.getTooltip(),
                            elem_desc,
                            Sdk.getCurrent().getDocumentationBaseUrl());
                } else {
                    tooltip = elem_desc.getTooltip();
                }

                try {
                    FormText text = SectionHelper.createFormText(masterTable, toolkit,
                            true /* isHtml */, tooltip, true /* setupLayoutData */);
                    text.addHyperlinkListener(mTree.getEditor().createHyperlinkListener());
                    Image icon = elem_desc.getIcon();
                    if (icon != null) {
                        text.setImage(DescriptorsUtils.IMAGE_KEY, icon);
                    }
                } catch(Exception e) {
                    // The FormText parser is really really basic and will fail as soon as the
                    // HTML javadoc is ever so slightly malformatted.
                    AdtPlugin.log(e,
                            "Malformed javadoc, rejected by FormText for node %1$s: '%2$s'", //$NON-NLS-1$
                            ui_node.getDescriptor().getXmlName(),
                            tooltip);
                    
                    // Fallback to a pure text tooltip, no fancy HTML
                    tooltip = DescriptorsUtils.formatTooltip(elem_desc.getTooltip());
                    Label label = SectionHelper.createLabel(masterTable, toolkit,
                            tooltip, tooltip);
                }
            }

            Composite table = useSubsections ? null : masterTable;
            
            for (AttributeDescriptor attr_desc : attr_desc_list) {
                if (attr_desc instanceof XmlnsAttributeDescriptor) {
                    // Do not show hidden attributes
                    continue;
                } else if (table == null || attr_desc instanceof SeparatorAttributeDescriptor) {
                    String title = null;
                    if (attr_desc instanceof SeparatorAttributeDescriptor) {
                        // xmlName is actually the label of the separator
                        title = attr_desc.getXmlLocalName();
                    } else {
                        title = String.format("Attributes from %1$s", elem_desc.getUiName());
                    }

                    table = createSubSectionTable(toolkit, masterTable, title);
                    if (attr_desc instanceof SeparatorAttributeDescriptor) {
                        continue;
                    }
                }

                UiAttributeNode ui_attr = ui_node.findUiAttribute(attr_desc);

                if (ui_attr != null) {
                    ui_attr.createUiControl(table, managedForm);
                    
                    if (ui_attr.getCurrentValue() != null &&
                            ui_attr.getCurrentValue().length() > 0) {
                        ((Section) table.getParent()).setExpanded(true);
                    }
                } else {
                    // The XML has an extra unknown attribute.
                    // This is not expected to happen so it is ignored.
                    AdtPlugin.log(IStatus.INFO,
                            "Attribute %1$s not declared in node %2$s, ignored.", //$NON-NLS-1$
                            attr_desc.getXmlLocalName(),
                            ui_node.getDescriptor().getXmlName());
                }
            }

            // Create a sub-section for the unknown attributes.
            // It is initially hidden till there are some attributes to show here.
            final Composite unknownTable = createSubSectionTable(toolkit, masterTable,
                    "Unknown XML Attributes");
            unknownTable.getParent().setVisible(false); // set section to not visible
            final HashSet<UiAttributeNode> reference = new HashSet<UiAttributeNode>();
            
            final IUiUpdateListener updateListener = new IUiUpdateListener() {
                public void uiElementNodeUpdated(UiElementNode ui_node, UiUpdateState state) {
                    if (state == UiUpdateState.ATTR_UPDATED) {
                        updateUnknownAttributesSection(ui_node, unknownTable, managedForm,
                                reference);
                    }
                }
            };
            ui_node.addUpdateListener(updateListener);
            
            // remove the listener when the UI is disposed
            unknownTable.addDisposeListener(new DisposeListener() {
                public void widgetDisposed(DisposeEvent e) {
                    ui_node.removeUpdateListener(updateListener);
                }
            });
            
            updateUnknownAttributesSection(ui_node, unknownTable, managedForm, reference);
            
            mMasterSection.getParent().pack(true /* changed */);
        }
    
public voiddispose()

        // pass
    
public voidinitialize(org.eclipse.ui.forms.IManagedForm form)

        mManagedForm = form;
    
public booleanisDirty()

        if (mCurrentUiElementNode != null && mCurrentUiElementNode.isDirty()) {
            markDirty();
        }
        return mIsDirty;
    
public booleanisStale()

        // pass
        return false;
    
private voidmarkDirty()
Marks the part dirty. Called as a result of user interaction with the widgets in the section.

        if (!mIsDirty) {
            mIsDirty = true;
            mManagedForm.dirtyStateChanged();
        }
    
private voidreflowMasterSection()
Reflow the parent ScrolledPageBook (which is actually a SharedScrolledComposite). This will recompute the correct size and adjust the scrollbar as needed.

        for(Composite c = mMasterSection; c != null; c = c.getParent()) {
            if (c instanceof SharedScrolledComposite) {
                ((SharedScrolledComposite) c).reflow(true /* flushCache */);
                break;
            }
        }
    
public voidrefresh()
Called by the master part when the tree is refreshed after the framework resources have been reloaded.

        if (mCurrentTable != null) {
            mCurrentTable.dispose();
            mCurrentTable = null;
        }
        mCurrentUiElementNode = null;
        mMasterSection.getParent().pack(true /* changed */);
    
public voidselectionChanged(org.eclipse.ui.forms.IFormPart part, org.eclipse.jface.viewers.ISelection selection)

        if (part == mMasterPart &&
                !selection.isEmpty() &&
                selection instanceof ITreeSelection) {
            ITreeSelection tree_selection = (ITreeSelection) selection;

            Object first = tree_selection.getFirstElement();
            if (first instanceof UiElementNode) {
                UiElementNode ui_node = (UiElementNode) first;
                createUiAttributeControls(mManagedForm, ui_node);
            }
        }
    
public voidsetFocus()

        // pass
    
public booleansetFormInput(java.lang.Object input)

        // pass
        return false;
    
private voidupdateUnknownAttributesSection(com.android.ide.eclipse.editors.uimodel.UiElementNode ui_node, org.eclipse.swt.widgets.Composite unknownTable, org.eclipse.ui.forms.IManagedForm managedForm, java.util.HashSet reference)
Updates the unknown attributes section for the UI Node.

        Collection<UiAttributeNode> ui_attrs = ui_node.getUnknownUiAttributes();
        Section section = ((Section) unknownTable.getParent());
        boolean needs_reflow = false;

        // The table was created hidden, show it if there are unknown attributes now
        if (ui_attrs.size() > 0 && !section.isVisible()) {
            section.setVisible(true);
            needs_reflow = true;
        }

        // Compare the new attribute set with the old "reference" one
        boolean has_differences = ui_attrs.size() != reference.size();
        if (!has_differences) {
            for (UiAttributeNode ui_attr : ui_attrs) {
                if (!reference.contains(ui_attr)) {
                    has_differences = true;
                    break;
                }
            }
        }

        if (has_differences) {
            needs_reflow = true;
            reference.clear();
            
            // Remove all children of the table
            for (Control c : unknownTable.getChildren()) {
                c.dispose();
            }
    
            // Recreate all attributes UI
            for (UiAttributeNode ui_attr : ui_attrs) {
                reference.add(ui_attr);
                ui_attr.createUiControl(unknownTable, managedForm);
    
                if (ui_attr.getCurrentValue() != null && ui_attr.getCurrentValue().length() > 0) {
                    section.setExpanded(true);
                }
            }
        }
        
        if (needs_reflow) {
            reflowMasterSection();
        }