com.microsoft.tfs.client.common.ui.controls.teamexplorer.TeamExplorerBaseControl.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.client.common.ui.controls.teamexplorer.TeamExplorerBaseControl.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the repository root.

package com.microsoft.tfs.client.common.ui.controls.teamexplorer;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;

import com.microsoft.tfs.client.common.TFSCommonClientPlugin;
import com.microsoft.tfs.client.common.ui.Messages;
import com.microsoft.tfs.client.common.ui.TFSCommonUIClientPlugin;
import com.microsoft.tfs.client.common.ui.controls.generic.BaseControl;
import com.microsoft.tfs.client.common.ui.framework.helper.SWTUtil;
import com.microsoft.tfs.client.common.ui.framework.helper.UIHelpers;
import com.microsoft.tfs.client.common.ui.framework.image.ImageHelper;
import com.microsoft.tfs.client.common.ui.framework.layout.GridDataBuilder;
import com.microsoft.tfs.client.common.ui.teamexplorer.TeamExplorerContext;
import com.microsoft.tfs.client.common.ui.teamexplorer.TeamExplorerEvents;
import com.microsoft.tfs.client.common.ui.teamexplorer.TeamExplorerResizeEventArg;
import com.microsoft.tfs.client.common.ui.teamexplorer.internal.TeamExplorerConfig;
import com.microsoft.tfs.client.common.ui.teamexplorer.internal.TeamExplorerPageConfig;
import com.microsoft.tfs.client.common.ui.teamexplorer.internal.TeamExplorerSectionConfig;
import com.microsoft.tfs.client.common.ui.teamexplorer.pages.ITeamExplorerPage;
import com.microsoft.tfs.client.common.ui.teamexplorer.sections.ITeamExplorerSection;
import com.microsoft.tfs.client.common.ui.teamexplorer.sections.TeamExplorerSectionRegenerateListener;
import com.microsoft.tfs.core.clients.commonstructure.ProjectInfo;
import com.microsoft.tfs.core.clients.teamsettings.TeamConfiguration;
import com.microsoft.tfs.core.ws.runtime.exceptions.TransportRequestHandlerCanceledException;
import com.microsoft.tfs.util.tasks.CanceledException;

/**
 * Base class for @TeamExplorerControl and
 *
 * @TeamExplorerDockableControl for code reuse.
 */
public abstract class TeamExplorerBaseControl extends BaseControl {

    private static final Log log = LogFactory.getLog(TeamExplorerBaseControl.class);
    protected final TeamExplorerConfig configuration;
    protected final TeamExplorerContext context;
    protected final FormToolkit toolkit;
    protected final Form form;
    protected final ScrolledForm subForm;
    protected Composite pageComposite;

    // Property name for the Job property bag. Value is a
    // TeamExplorerNavigationItemConfig
    protected static final QualifiedName NAVITEM_CONFIG_DATA_NAME = new QualifiedName(null, "NavigationItemConfig"); //$NON-NLS-1$

    // Property name for the Job property bag. Value is an ITeamExplorerSection.
    protected static final QualifiedName SECTION_INSTANCE_PROPERY_NAME = new QualifiedName(null, "SectionInstance"); //$NON-NLS-1$

    // Property name for the Job property bag. Value is a FormToolkit section.
    protected static final QualifiedName SECTION_CONTROL_PROPERTY_NAME = new QualifiedName(null, "SectionControl"); //$NON-NLS-1$

    protected static final String SECTION_CONFIG_DATA_NAME = "com.microsoft.tfs.client.common.ui.controls.teamexplorer.sectionConfig"; //$NON-NLS-1$

    protected static final String SECTION_INSTANCE_DATA_NAME = "com.microsoft.tfs.client.common.ui.controls.teamexplorer.sectionInstance"; //$NON-NLS-1$

    protected final Map<String, Section> sectionMap = new HashMap<String, Section>();

    protected final SectionRegenerateListener sectionRegenerateListener = new SectionRegenerateListener();
    protected ITeamExplorerPage currentPage = null;

    // The map maintains all the TeamExplorer Sections, keeps the connection
    // between pages and sections
    protected final Map<String, ITeamExplorerSection> teamExplorerSectionMap = new HashMap<String, ITeamExplorerSection>();

    // The map maintains all the states of pages/sections, the key is the
    // page/section ID, the value is the real state object
    protected final Map<String, Object> stateMap = new HashMap<String, Object>();

    // The map maintains the expanded state of all the Team Explorer sections
    protected final Map<String, Boolean> sectionExpandStateMap = new HashMap<String, Boolean>();

    public TeamExplorerBaseControl(final TeamExplorerConfig configuration, final TeamExplorerContext context,
            final Composite parent, final int style) {
        super(parent, style);
        this.configuration = configuration;
        this.context = context;

        setLayout(new FillLayout());

        toolkit = new FormToolkit(this.getDisplay());
        form = toolkit.createForm(this);
        toolkit.decorateFormHeading(form);

        form.addListener(SWT.Resize, new Listener() {
            @Override
            public void handleEvent(final Event event) {
                handleHeadResize();
            }
        });

        final GridLayout gridLayout = new GridLayout();
        gridLayout.marginWidth = 0;
        gridLayout.marginHeight = 0;
        gridLayout.horizontalSpacing = 0;
        gridLayout.verticalSpacing = 0;
        form.getBody().setLayout(gridLayout);

        subForm = toolkit.createScrolledForm(form.getBody());
        subForm.getBody().setLayout(gridLayout);
        GridDataBuilder.newInstance().grab().fill().applyTo(subForm);
        subForm.addListener(SWT.Resize, new Listener() {
            @Override
            public void handleEvent(final Event event) {
                handleBodyResize();
            }
        });
    }

    @Override
    public boolean setFocus() {
        form.setFocus();
        return true;
    }

    protected void handleHeadResize() {
        form.layout(true, true);
    }

    protected void handleBodyResize() {
        int availableWidth = subForm.getSize().x;
        final ScrollBar verticalScrollBar = subForm.getVerticalBar();
        if (verticalScrollBar != null && verticalScrollBar.isVisible()) {
            availableWidth -= verticalScrollBar.getSize().x;
        }

        final TeamExplorerResizeEventArg arg = new TeamExplorerResizeEventArg(availableWidth);
        context.getEvents().notifyListener(TeamExplorerEvents.FORM_RESIZED, arg);
        subForm.layout(true, true);
        subForm.reflow(true);
    }

    protected void createPageContent(final Composite parent, final TeamExplorerPageConfig page,
            final TeamExplorerSectionConfig[] sections) {
        final Composite composite = createPageHeaderContent(parent, page);

        if (composite != null && !composite.isDisposed()) {
            GridDataBuilder.newInstance().hFill().hGrab().applyTo(composite);
        }

        if (!context.isConnectedToCollection()) {
            return;
        }

        if (sections != null) {
            for (int i = 0; i < sections.length; i++) {
                final TeamExplorerSectionConfig section = sections[i];

                Composite sectionComposite;

                try {
                    final ITeamExplorerSection sectionInstance = section.createInstance();
                    sectionInstance.addSectionRegenerateListener(sectionRegenerateListener);

                    final Section ec = createSectionContent(parent, section, sectionInstance);
                    ec.setText(sectionInstance.getTitle());

                    sectionMap.put(sectionInstance.getID(), ec);
                    teamExplorerSectionMap.put(sectionInstance.getID(), sectionInstance);
                    sectionComposite = ec;
                } catch (final Throwable t) {
                    sectionComposite = createExceptionComposite(parent, section.getTitle(), t);
                }

                final GridData gridData = new GridData();
                gridData.horizontalAlignment = SWT.FILL;
                gridData.grabExcessHorizontalSpace = true;

                if (i > 0) {
                    gridData.verticalIndent = 0;
                }

                if (i == sections.length - 1) {
                    gridData.verticalAlignment = SWT.FILL;
                    gridData.grabExcessVerticalSpace = true;
                }

                if (sectionComposite.getVisible() == false) {
                    gridData.exclude = true;
                }

                sectionComposite.setLayoutData(gridData);
            }
        }
    }

    protected Composite createPageHeaderContent(final Composite parent, final TeamExplorerPageConfig page) {
        Composite composite;
        try {
            final ITeamExplorerPage pageInstance = page.createInstance();
            pageInstance.initialize(null, context, stateMap.get(page.getID()));
            currentPage = pageInstance;
            composite = pageInstance.getPageContent(toolkit, parent, SWT.NONE, context);
        } catch (final Exception e) {
            composite = createExceptionComposite(parent, page.getTitle(), e);
        }

        return composite;
    }

    protected Section createSectionContent(final Composite parent, final TeamExplorerSectionConfig sectionConfig,
            final ITeamExplorerSection sectionInstance) {
        final Section ec = toolkit.createSection(parent,
                ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT);

        Composite sectionComposite;
        boolean shouldExpand = true;
        try {
            if (sectionExpandStateMap.get(sectionInstance.getID()) != null) {
                shouldExpand = sectionExpandStateMap.get(sectionInstance.getID());
            } else {
                shouldExpand = true;
            }

            if (sectionInstance.initializeInBackground(context)) {
                createBackgroundSectionLoadJob(ec, sectionConfig, sectionInstance);
                sectionComposite = createLoadingComposite(ec);
            } else {
                sectionInstance.initialize(null, context, stateMap.get(sectionInstance.getID()));
                sectionComposite = sectionInstance.getSectionContent(toolkit, ec, SWT.NONE, context);
            }
        } catch (final CanceledException e) {
            sectionComposite = createCancelledComposite(ec);
        } catch (final TransportRequestHandlerCanceledException e) {
            sectionComposite = createCancelledComposite(ec);
        } catch (final Exception e) {
            sectionComposite = createExceptionComposite(ec, sectionInstance.getTitle(), e);
        }

        if (sectionComposite != null) {
            ec.setClient(sectionComposite);
            ec.setExpanded(shouldExpand);
        }

        ec.addExpansionListener(new ExpansionAdapter() {
            @Override
            public void expansionStateChanged(final ExpansionEvent e) {
                form.layout();
            }
        });

        if (!sectionInstance.isVisible(context)) {
            ec.setVisible(false);
        }

        ec.setData(SECTION_CONFIG_DATA_NAME, sectionConfig);
        ec.setData(SECTION_INSTANCE_DATA_NAME, sectionInstance);
        return ec;
    }

    private void createBackgroundSectionLoadJob(final Section section, final TeamExplorerSectionConfig config,
            final ITeamExplorerSection instance) {
        // Use the general title from the config, not the content-specific one
        // from the instance
        final SectionLoadJob job = new SectionLoadJob(config.getTitle(), instance);
        job.setProperty(SECTION_CONTROL_PROPERTY_NAME, section);
        job.setProperty(SECTION_INSTANCE_PROPERY_NAME, instance);
        job.addJobChangeListener(new SectionLoadJobChangeListener());
        job.schedule();
    }

    protected Composite createLoadingComposite(final Composite parent) {
        final Composite composite = toolkit.createComposite(parent);

        // Form-style border painting not enabled (0 pixel margins OK) because
        // no applicable controls in this composite
        SWTUtil.gridLayout(composite);

        final Label textLabel = toolkit.createLabel(composite,
                Messages.getString("TeamExplorerBaseControl.LoadingLabelText"), SWT.WRAP); //$NON-NLS-1$
        GridDataBuilder.newInstance().hAlignFill().hGrab().applyTo(textLabel);

        return composite;
    }

    protected Composite createCancelledComposite(final Composite parent) {
        final Composite composite = toolkit.createComposite(parent);

        // Form-style border painting not enabled (0 pixel margins OK) because
        // no applicable controls in this composite
        SWTUtil.gridLayout(composite);

        final String message = Messages.getString("TeamExplorerBaseSection.Cancelled"); //$NON-NLS-1$
        final Label label = toolkit.createLabel(composite, message);
        GridDataBuilder.newInstance().hAlignFill().hGrab().applyTo(label);

        return composite;
    }

    protected Composite createExceptionComposite(final Composite parent, final String itemName, final Throwable e) {
        if (context == null || !context.isConnected()) {
            return createOfflineComposite(parent);
        }

        log.warn("Caught exception creating TE UI component", e); //$NON-NLS-1$

        // Make sure to use a non-disposed parent composite
        if (parent == null || parent.isDisposed()) {
            return parent;
        }

        final Composite composite = toolkit.createComposite(parent);

        // Form-style border painting not enabled (0 pixel margins OK) because
        // no applicable controls in this composite
        SWTUtil.gridLayout(composite);

        final Composite border = toolkit.createComposite(composite, SWT.BORDER);
        SWTUtil.gridLayout(border, 2);
        GridDataBuilder.newInstance().hAlignFill().hGrab().applyTo(border);

        final ImageHelper imageHelper = new ImageHelper(TFSCommonUIClientPlugin.PLUGIN_ID);
        final Image image = imageHelper.getImage("/images/common/team_explorer_exception.gif"); //$NON-NLS-1$
        final Label imageLabel = toolkit.createLabel(border, "", SWT.NONE); //$NON-NLS-1$
        imageLabel.setImage(image);
        GridDataBuilder.newInstance().applyTo(imageLabel);

        final String format = Messages.getString("TeamExplorerControl.ExceptionCreatingContributionFormat"); //$NON-NLS-1$
        final String message = MessageFormat.format(format, itemName,
                e.getLocalizedMessage() != null ? e.getLocalizedMessage()
                        : Messages.getString("TeamExplorerControl.NoMessageAvailable")); //$NON-NLS-1$

        final Label textLabel = toolkit.createLabel(border, message, SWT.WRAP);
        GridDataBuilder.newInstance().hAlignFill().hGrab().applyTo(textLabel);

        return composite;
    }

    private Composite createOfflineComposite(final Composite parent) {
        // Create the container composite.
        final Composite composite = toolkit.createComposite(parent);
        SWTUtil.gridLayout(composite, 1, true, 0, 5);
        final Label label = toolkit.createLabel(composite,
                Messages.getString("TeamExplorerReportsSection.DisconnectedLabel"));//$NON-NLS-1$
        GridDataBuilder.newInstance().hAlignFill().hGrab().applyTo(label);
        return composite;
    }

    protected void regenerateSection(final String sectionID) {
        final Section section = sectionMap.get(sectionID);
        boolean expandedState = true;
        if (section != null) {
            expandedState = section.isExpanded();
        }

        sectionMap.remove(sectionID);

        final Composite parentComposite = getParentComposite();
        final Control[] children = parentComposite.getChildren();

        for (int i = 0; i < children.length; i++) {
            if (children[i] == section) {
                final TeamExplorerSectionConfig sectionConfig = (TeamExplorerSectionConfig) section
                        .getData(SECTION_CONFIG_DATA_NAME);

                final ITeamExplorerSection sectionInstance = (ITeamExplorerSection) section
                        .getData(SECTION_INSTANCE_DATA_NAME);

                section.dispose();

                final Section newSection = createSectionContent(parentComposite, sectionConfig, sectionInstance);

                final GridData gridData = new GridData();
                gridData.horizontalAlignment = SWT.FILL;
                gridData.grabExcessHorizontalSpace = true;

                if (i > 0) {
                    gridData.verticalIndent = 0;
                }

                if (i == children.length - 1) {
                    gridData.verticalAlignment = SWT.FILL;
                    gridData.grabExcessVerticalSpace = true;
                }

                newSection.setLayoutData(gridData);
                newSection.setExpanded(expandedState);
                sectionMap.put(sectionID, newSection);

                newSection.setText(sectionInstance.getTitle());
                newSection.moveBelow(children[Math.max(0, i - 1)]);
                return;
            }
        }
    }

    /**
     * Get the parent composite to put control on
     *
     * @return
     */
    protected Composite getParentComposite() {
        return pageComposite;
    }

    /**
     * @return the text that describes the current project and team, shown in
     *         the {@link Form}'s message area
     */
    protected String getProjectAndTeamText() {
        if (context.isConnectedToCollection()) {
            final ProjectInfo project = context.getCurrentProjectInfo();
            if (project != null) {
                final TeamConfiguration team = context.getCurrentTeam();
                if (team != null && !team.isDefaultTeam()) {
                    return MessageFormat.format(
                            Messages.getString("TeamExplorerControl.TeamExplorerProjectAndTeamFormat"), //$NON-NLS-1$
                            project.getName(), team.getTeamName());
                }

                return project.getName();
            } else {
                return null;
            }
        } else {
            return Messages.getString("TeamExplorerView.Disconnected"); //$NON-NLS-1$ ;
        }
    }

    public void refreshView() {
    }

    private class SectionLoadJob extends Job {
        private final ITeamExplorerSection sectionInstance;

        public SectionLoadJob(final String title, final ITeamExplorerSection sectionInstance) {
            super(MessageFormat.format(Messages.getString("TeamExplorerControl.SectionLoadJobFormat"), title)); //$NON-NLS-1$
            this.sectionInstance = sectionInstance;
        }

        @Override
        protected IStatus run(final IProgressMonitor monitor) {
            try {
                sectionInstance.initialize(monitor, context, stateMap.get(sectionInstance.getID()));
            } catch (final CanceledException e) {
                return Status.CANCEL_STATUS;
            } catch (final TransportRequestHandlerCanceledException e) {
                return Status.CANCEL_STATUS;
            } catch (final Exception e) {
                log.error("Failed to initialize Team Explorer section", e); //$NON-NLS-1$

                // Return a status of WARNING so that the progress monitor won't
                // display a modal dialog for an ERROR status. We'll handle the
                // exception in the job adapter's 'done' method.
                return new Status(Status.WARNING, TFSCommonClientPlugin.PLUGIN_ID, null, e);
            }

            return monitor.isCanceled() ? Status.CANCEL_STATUS : Status.OK_STATUS;
        }
    }

    private class SectionRegenerateListener implements TeamExplorerSectionRegenerateListener {
        @Override
        public void onSectionRegenerate(final String sectionID) {
            UIHelpers.runOnUIThread(true, new Runnable() {
                @Override
                public void run() {
                    regenerateSection(sectionID);
                }
            });
        }
    }

    private class SectionLoadJobChangeListener extends JobChangeAdapter {
        @Override
        public void done(final IJobChangeEvent event) {
            final ITeamExplorerSection sectionInstance;
            final Section ec;

            sectionInstance = (ITeamExplorerSection) event.getJob().getProperty(SECTION_INSTANCE_PROPERY_NAME);
            ec = (Section) event.getJob().getProperty(SECTION_CONTROL_PROPERTY_NAME);

            UIHelpers.runOnUIThread(true, new Runnable() {
                @Override
                public void run() {
                    if (ec != null && !ec.isDisposed()) {
                        // Dispose of the place holder UI.
                        ec.getClient().dispose();

                        // Create the final UI.
                        final Composite content;
                        if (event.getResult() == Status.OK_STATUS) {
                            content = sectionInstance.getSectionContent(toolkit, ec, SWT.NONE, context);
                        } else if (event.getResult().getException() != null) {
                            content = createExceptionComposite(ec, sectionInstance.getTitle(),
                                    event.getResult().getException());
                        } else {
                            content = createCancelledComposite(ec);
                        }

                        // Display the final UI.
                        ec.setText(sectionInstance.getTitle());
                        ec.setClient(content);

                        if (sectionExpandStateMap.get(sectionInstance.getID()) != null) {
                            ec.setExpanded(sectionExpandStateMap.get(sectionInstance.getID()));
                        } else {
                            ec.setExpanded(true);
                        }

                        // Simulate a resize to get the GridData wHints set
                        // properly after initial layout.
                        //
                        // HACK: We only need to do this if there are listeners
                        // for this section. We always have a listener for the
                        // search control and only within sections in the
                        // pending changes page. Thus, all pages other than
                        // pending changes page will have only one listener so
                        // we don't have to simulate a resize for this section.
                        // This could be improved but is being included as a
                        // quick and dirty optimization for Dev12 RTM.
                        if (context.getEvents().getListenerCount(TeamExplorerEvents.FORM_RESIZED) > 1) {
                            handleBodyResize();
                        }
                    }
                }
            });
        }
    }
}