org.eclipse.ui.internal.PerspectiveSwitcher.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ui.internal.PerspectiveSwitcher.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2009 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Chris Grindstaff <chris@gstaff.org> - Fix for bug 158016
 *     Tonny Madsen, RCP Company - bug 201055
 *     Mark Hoffmann <mark.hoffmann@web.de> - Fix for bug 84603
 *******************************************************************************/
package org.eclipse.ui.internal;

import java.util.Arrays;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.NotEnabledException;
import org.eclipse.core.commands.NotHandledException;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CBanner;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.CoolBar;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PerspectiveAdapter;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
import org.eclipse.ui.internal.dnd.AbstractDropTarget;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.internal.dnd.IDragOverListener;
import org.eclipse.ui.internal.dnd.IDropTarget;
import org.eclipse.ui.internal.layout.CacheWrapper;
import org.eclipse.ui.internal.layout.CellLayout;
import org.eclipse.ui.internal.layout.ITrimManager;
import org.eclipse.ui.internal.layout.IWindowTrim;
import org.eclipse.ui.internal.layout.LayoutUtil;
import org.eclipse.ui.internal.layout.Row;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.statushandlers.StatusManager;

/**
 * A utility class to manage the perspective switcher.  At some point, it might be nice to
 * move all this into PerspectiveViewBar.
 * 
 * @since 3.0
 */
public class PerspectiveSwitcher implements IWindowTrim {

    /**
     * The minimal width for the switcher (i.e. for the open button and chevron).
     */
    private static final int MIN_WIDTH = 45;

    /**
     * The average width for each perspective button.
     */
    private static final int ITEM_WIDTH = 80;

    /**
     * The minimum default width.
     */
    private static final int MIN_DEFAULT_WIDTH = 160;

    private IWorkbenchWindow window;

    private CBanner topBar;

    private int style;

    private Composite parent;

    private Composite trimControl;

    private Label trimSeparator;

    private GridData trimLayoutData;

    private boolean trimVisible = false;

    private int trimOldLength = 0;

    private PerspectiveBarManager perspectiveBar;

    private CoolBar perspectiveCoolBar;

    private CacheWrapper perspectiveCoolBarWrapper;

    private CoolItem coolItem;

    private CacheWrapper toolbarWrapper;

    // The menus are cached, so the radio buttons should not be disposed until
    // the switcher is disposed.
    private Menu popupMenu;

    private Menu genericMenu;

    private static final int INITIAL = -1;

    private static final int TOP_RIGHT = 1;

    private static final int TOP_LEFT = 2;

    private static final int LEFT = 3;

    private int currentLocation = INITIAL;

    private IPreferenceStore apiPreferenceStore = PrefUtil.getAPIPreferenceStore();

    private IPropertyChangeListener propertyChangeListener;

    private Listener popupListener = new Listener() {
        public void handleEvent(Event event) {
            if (event.type == SWT.MenuDetect) {
                showPerspectiveBarPopup(new Point(event.x, event.y));
            }
        }
    };

    class ChangeListener extends PerspectiveAdapter implements IPageListener {
        public void perspectiveOpened(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
            if (findPerspectiveShortcut(perspective, page) == null) {
                addPerspectiveShortcut(perspective, page);
            }
        }

        public void perspectiveClosed(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
            // Don't remove the shortcut if the workbench is
            // closing. This causes a spurious 'layout' on the
            // shell during close, leading to possible life-cycle issues
            if (page != null && !page.getWorkbenchWindow().getWorkbench().isClosing()) {
                removePerspectiveShortcut(perspective, page);
            }
        }

        public void perspectiveActivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
            selectPerspectiveShortcut(perspective, page, true);
        }

        public void perspectiveDeactivated(IWorkbenchPage page, IPerspectiveDescriptor perspective) {
            selectPerspectiveShortcut(perspective, page, false);
        }

        public void perspectiveSavedAs(IWorkbenchPage page, IPerspectiveDescriptor oldPerspective,
                IPerspectiveDescriptor newPerspective) {
            updatePerspectiveShortcut(oldPerspective, newPerspective, page);
        }

        public void pageActivated(IWorkbenchPage page) {
        }

        public void pageClosed(IWorkbenchPage page) {
        }

        public void pageOpened(IWorkbenchPage page) {
        }
    }

    private ChangeListener changeListener = new ChangeListener();

    private Listener dragListener;

    private IDragOverListener dragTarget;

    private DisposeListener toolBarListener;

    private IReorderListener reorderListener;

    /**
      * Creates an instance of the perspective switcher.
     * @param window  it's window
     * @param topBar  the CBanner to place this widget in
     * @param style   the widget style to use
     */
    public PerspectiveSwitcher(IWorkbenchWindow window, CBanner topBar, int style) {
        this.window = window;
        this.topBar = topBar;
        this.style = style;
        setPropertyChangeListener();
        // this listener will only be run when the Shell is being disposed
        // and each WorkbenchWindow has its own PerspectiveSwitcher
        toolBarListener = new DisposeListener() {
            public void widgetDisposed(DisposeEvent e) {
                dispose();
            }
        };
        window.addPerspectiveListener(changeListener);
        window.addPageListener(changeListener);
    }

    private static int convertLocation(String preference) {
        if (IWorkbenchPreferenceConstants.TOP_RIGHT.equals(preference)) {
            return TOP_RIGHT;
        }
        if (IWorkbenchPreferenceConstants.TOP_LEFT.equals(preference)) {
            return TOP_LEFT;
        }
        if (IWorkbenchPreferenceConstants.LEFT.equals(preference)) {
            return LEFT;
        }

        return TOP_RIGHT;
    }

    /**
      * Create the contents of the receiver
     * @param parent
     */
    public void createControl(Composite parent) {
        Assert.isTrue(this.parent == null);
        this.parent = parent;
        // set the initial location read from the preference
        setPerspectiveBarLocation(
                PrefUtil.getAPIPreferenceStore().getString(IWorkbenchPreferenceConstants.DOCK_PERSPECTIVE_BAR));
    }

    private void addPerspectiveShortcut(IPerspectiveDescriptor perspective, IWorkbenchPage workbenchPage) {
        if (perspectiveBar == null) {
            return;
        }

        PerspectiveBarContributionItem item = new PerspectiveBarContributionItem(perspective, workbenchPage);
        perspectiveBar.addItem(item);
        setCoolItemSize(coolItem);
        // This is need to update the vertical size of the tool bar on GTK+ when
        // using large fonts.
        if (perspectiveBar != null) {
            perspectiveBar.update(true);
        }

        if (currentLocation == LEFT) {
            updatePerspectiveBar();
        }
    }

    /**
      * Find a contribution item that matches the perspective provided.
      * 
     * @param perspective
     * @param page
     * @return the <code>IContributionItem</code> or null if no matches were found
     */
    public IContributionItem findPerspectiveShortcut(IPerspectiveDescriptor perspective, IWorkbenchPage page) {
        if (perspectiveBar == null) {
            return null;
        }

        IContributionItem[] items = perspectiveBar.getItems();
        int length = items.length;
        for (int i = 0; i < length; i++) {
            IContributionItem item = items[i];
            if (item instanceof PerspectiveBarContributionItem
                    && ((PerspectiveBarContributionItem) item).handles(perspective, page)) {
                return item;
            }
        }
        return null;
    }

    private void removePerspectiveShortcut(IPerspectiveDescriptor perspective, IWorkbenchPage page) {
        if (perspectiveBar == null) {
            return;
        }

        IContributionItem item = findPerspectiveShortcut(perspective, page);
        if (item != null) {
            if (item instanceof PerspectiveBarContributionItem) {
                perspectiveBar.removeItem((PerspectiveBarContributionItem) item);
            }
            item.dispose();
            perspectiveBar.update(false);
            setCoolItemSize(coolItem);

            if (currentLocation == LEFT) {
                updatePerspectiveBar();
                LayoutUtil.resize(perspectiveBar.getControl());
            }
        }
    }

    /**
      * Locate the perspective bar according to the provided location
     * @param preference the location to put the perspective bar at
     */
    public void setPerspectiveBarLocation(String preference) {
        // return if the control has not been created. createControl(...) will
        // handle updating the state in that case
        if (parent == null) {
            return;
        }
        int newLocation = convertLocation(preference);
        if (newLocation == currentLocation) {
            return;
        }
        createControlForLocation(newLocation);
        currentLocation = newLocation;
        showPerspectiveBar();
        if (newLocation == TOP_LEFT || newLocation == TOP_RIGHT) {
            updatePerspectiveBar();
            updateBarParent();
        }
    }

    /**
     * Make the perspective bar visible in its current location. This method
     * should not be used unless the control has been successfully created.
     */
    private void showPerspectiveBar() {
        switch (currentLocation) {
        case TOP_LEFT:
            topBar.setRight(null);
            topBar.setBottom(perspectiveCoolBarWrapper.getControl());
            break;
        case TOP_RIGHT:
            topBar.setBottom(null);
            topBar.setRight(perspectiveCoolBarWrapper.getControl());
            topBar.setRightWidth(getDefaultWidth());
            break;
        case LEFT:
            topBar.setBottom(null);
            topBar.setRight(null);
            LayoutUtil.resize(topBar);
            getTrimManager().addTrim(SWT.LEFT, this);
            break;
        default:
            return;
        }

        LayoutUtil.resize(perspectiveBar.getControl());
    }

    /**
     * Returns the default width for the switcher.
     */
    private int getDefaultWidth() {
        String extras = PrefUtil.getAPIPreferenceStore()
                .getString(IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_EXTRAS);
        StringTokenizer tok = new StringTokenizer(extras, ", "); //$NON-NLS-1$
        int numExtras = tok.countTokens();
        int numPersps = Math.max(numExtras, 1); // assume initial perspective is also listed in extras

        // Fixed bug 84603: [RCP] [PerspectiveBar] New API or pref to set default perspective bar size
        String sizeString = PrefUtil.getAPIPreferenceStore()
                .getString(IWorkbenchPreferenceConstants.PERSPECTIVE_BAR_SIZE);
        int size = MIN_DEFAULT_WIDTH;
        try {
            size = Integer.parseInt(sizeString);
        } catch (NumberFormatException e) {
            // leave size value at MIN_DEFAULT_WIDTH
        }
        int defaultWidth = Math.max(MIN_DEFAULT_WIDTH, size);
        return Math.max(defaultWidth, MIN_WIDTH + (numPersps * ITEM_WIDTH));
    }

    /**
     * Get the trim manager from the default workbench window. If the current
     * workbench window is -not- the <code>WorkbenchWindow</code> then return null.
     * 
     * @return The trim manager for the current workbench window
     */
    private ITrimManager getTrimManager() {
        if (window instanceof WorkbenchWindow)
            return ((WorkbenchWindow) window).getTrimManager();

        return null; // not using the default workbench window
    }

    /**
      * Update the receiver
     * @param force
     */
    public void update(boolean force) {
        if (perspectiveBar == null) {
            return;
        }

        perspectiveBar.update(force);

        if (currentLocation == LEFT) {
            ToolItem[] items = perspectiveBar.getControl().getItems();
            boolean shouldExpand = items.length > 0;
            if (shouldExpand != trimVisible) {
                perspectiveBar.getControl().setVisible(true);
                trimVisible = shouldExpand;
            }

            if (items.length != trimOldLength) {
                LayoutUtil.resize(trimControl);
                trimOldLength = items.length;
            }
        }
    }

    private void selectPerspectiveShortcut(IPerspectiveDescriptor perspective, IWorkbenchPage page,
            boolean selected) {
        IContributionItem item = findPerspectiveShortcut(perspective, page);
        if (item != null && (item instanceof PerspectiveBarContributionItem)) {
            if (selected) {
                // check if not visible and ensure visible
                PerspectiveBarContributionItem contribItem = (PerspectiveBarContributionItem) item;
                perspectiveBar.select(contribItem);
            }
            // select or de-select
            ((PerspectiveBarContributionItem) item).setSelection(selected);
        }
    }

    private void updatePerspectiveShortcut(IPerspectiveDescriptor oldDesc, IPerspectiveDescriptor newDesc,
            IWorkbenchPage page) {
        IContributionItem item = findPerspectiveShortcut(oldDesc, page);
        if (item != null && (item instanceof PerspectiveBarContributionItem)) {
            ((PerspectiveBarContributionItem) item).update(newDesc);
        }
    }

    /**
      * Answer the perspective bar manager
     * @return the manager
     */
    public PerspectiveBarManager getPerspectiveBar() {
        return perspectiveBar;
    }

    /**
     * Dispose resources being held by the receiver
     */
    public void dispose() {
        window.removePerspectiveListener(changeListener);
        window.removePageListener(changeListener);
        if (propertyChangeListener != null) {
            apiPreferenceStore.removePropertyChangeListener(propertyChangeListener);
            propertyChangeListener = null;
        }
        unhookDragSupport();
        disposeChildControls();
        toolBarListener = null;
    }

    private void disposeChildControls() {

        if (trimControl != null) {
            trimControl.dispose();
            trimControl = null;
        }

        if (trimSeparator != null) {
            trimSeparator.dispose();
            trimSeparator = null;
        }

        if (perspectiveCoolBar != null) {
            perspectiveCoolBar.dispose();
            perspectiveCoolBar = null;
        }

        if (toolbarWrapper != null) {
            toolbarWrapper.dispose();
            toolbarWrapper = null;
        }

        if (perspectiveBar != null) {
            perspectiveBar.dispose();
            perspectiveBar = null;
        }

        perspectiveCoolBarWrapper = null;
    }

    /**
     * Ensures the control has been set for the argument location. If the
     * control already exists and can be used the argument location, nothing
     * happens. Updates the location attribute.
     * 
     * @param newLocation
     */
    private void createControlForLocation(int newLocation) {
        // if there is a control, then perhaps it can be reused
        if (perspectiveBar != null && perspectiveBar.getControl() != null
                && !perspectiveBar.getControl().isDisposed()) {
            if (newLocation == LEFT && currentLocation == LEFT) {
                return;
            }
            if ((newLocation == TOP_LEFT || newLocation == TOP_RIGHT)
                    && (currentLocation == TOP_LEFT || currentLocation == TOP_RIGHT)) {
                return;
            }
        }

        if (perspectiveBar != null) {
            perspectiveBar.getControl().removeDisposeListener(toolBarListener);
            unhookDragSupport();
        }
        // otherwise dispose the current controls and make new ones

        // First, make sure that the existing verion is removed from the trim layout
        getTrimManager().removeTrim(this);

        disposeChildControls();
        if (newLocation == LEFT) {
            createControlForLeft();
        } else {
            createControlForTop();
        }
        hookDragSupport();

        perspectiveBar.getControl().addDisposeListener(toolBarListener);
    }

    /**
     * Remove any drag and drop support and associated listeners hooked for the
     * perspective switcher.
     */
    private void unhookDragSupport() {
        ToolBar bar = perspectiveBar.getControl();

        if (bar == null || bar.isDisposed() || dragListener == null) {
            return;
        }
        // PresentationUtil.removeDragListener(bar, dragListener);
        DragUtil.removeDragTarget(perspectiveBar.getControl(), dragTarget);
        dragListener = null;
        dragTarget = null;
    }

    /**
    * Attach drag and drop support and associated listeners hooked for
    * the perspective switcher.
    */
    private void hookDragSupport() {
        dragListener = new Listener() {
            /* (non-Javadoc)
            * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
            */
            public void handleEvent(Event event) {
                ToolBar toolbar = perspectiveBar.getControl();
                ToolItem item = toolbar.getItem(new Point(event.x, event.y));

                if (item != null) {
                    //ignore the first item, which remains in position Zero
                    if (item.getData() instanceof PerspectiveBarNewContributionItem) {
                        return;
                    }

                    Rectangle bounds = item.getBounds();
                    Rectangle parentBounds = toolbar.getBounds();
                    bounds.x += parentBounds.x;
                    bounds.y += parentBounds.y;
                    startDragging(item.getData(), toolbar.getDisplay().map(toolbar, null, bounds));
                } else {
                    //startDragging(toolbar, toolbar.getDisplay().map(toolbar, null, toolbar.getBounds()));
                }
            }

            private void startDragging(Object widget, Rectangle bounds) {
                if (!DragUtil.performDrag(widget, bounds, new Point(bounds.x, bounds.y), true)) {
                    //currently do nothing on a failed drag
                }
            }
        };

        dragTarget = new IDragOverListener() {
            protected PerspectiveDropTarget perspectiveDropTarget;

            class PerspectiveDropTarget extends AbstractDropTarget {

                private PerspectiveBarContributionItem perspective;

                private Point location;

                /**
                * @param location
                * @param draggedObject
                */
                public PerspectiveDropTarget(Object draggedObject, Point location) {
                    update(draggedObject, location);
                }

                /**
                 * 
                 * @param draggedObject
                 * @param location
                 */
                private void update(Object draggedObject, Point location) {
                    this.location = location;
                    this.perspective = (PerspectiveBarContributionItem) draggedObject;
                }

                /*
                 * (non-Javadoc)
                 * 
                 * @see org.eclipse.ui.internal.dnd.IDropTarget#drop()
                 */
                public void drop() {
                    ToolBar toolBar = perspectiveBar.getControl();
                    ToolItem item = toolBar.getItem(toolBar.getDisplay().map(null, toolBar, location));
                    if (toolBar.getItem(0) == item) {
                        return;
                    }
                    ToolItem[] items = toolBar.getItems();
                    ToolItem droppedItem = null;
                    int dropIndex = -1;
                    for (int i = 0; i < items.length; i++) {
                        if (item == items[i]) {
                            dropIndex = i;
                        }
                        if (items[i].getData() == perspective) {
                            droppedItem = items[i];
                        }
                    }
                    if (dropIndex != -1 && droppedItem != null && (droppedItem != item)) {
                        PerspectiveBarContributionItem barItem = (PerspectiveBarContributionItem) droppedItem
                                .getData();
                        // policy is to insert at the beginning so mirror the value when indicating a
                        // new position for the perspective
                        if (reorderListener != null) {
                            reorderListener.reorder(barItem.getPerspective(),
                                    Math.abs(dropIndex - (items.length - 1)));
                        }

                        perspectiveBar.relocate(barItem, dropIndex);
                    }
                }

                /*
                 * (non-Javadoc)
                 * 
                 * @see org.eclipse.ui.internal.dnd.IDropTarget#getCursor()
                 */
                public Cursor getCursor() {
                    return DragCursors.getCursor(DragCursors.CENTER);
                }

                boolean sameShell() {
                    return perspective.getToolItem().getParent().getShell()
                            .equals(perspectiveBar.getControl().getShell());
                }

                public Rectangle getSnapRectangle() {
                    ToolBar toolBar = perspectiveBar.getControl();
                    ToolItem item = toolBar.getItem(toolBar.getDisplay().map(null, toolBar, location));
                    Rectangle bounds;
                    if (item != null && item != toolBar.getItem(0)) {
                        bounds = item.getBounds();
                    } else {
                        // it should not be possible to start a drag with item 0
                        return null;
                    }
                    return toolBar.getDisplay().map(toolBar, null, bounds);
                }
            }

            public IDropTarget drag(Control currentControl, Object draggedObject, Point position,
                    Rectangle dragRectangle) {
                if (draggedObject instanceof PerspectiveBarContributionItem) {
                    if (perspectiveDropTarget == null) {
                        perspectiveDropTarget = new PerspectiveDropTarget(draggedObject, position);
                    } else {
                        perspectiveDropTarget.update(draggedObject, position);
                    }
                    // do not support drag to perspective bars between shells.
                    if (!perspectiveDropTarget.sameShell()) {
                        return null;
                    }

                    return perspectiveDropTarget;
                } // else if (draggedObject instanceof IPerspectiveBar) {
                  //   return new PerspectiveBarDropTarget();
                  //}

                return null;
            }

        };

        // PresentationUtil.addDragListener(perspectiveBar.getControl(),
        // dragListener);
        DragUtil.addDragTarget(perspectiveBar.getControl(), dragTarget);
    }

    private void setPropertyChangeListener() {
        propertyChangeListener = new IPropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
                if (IWorkbenchPreferenceConstants.SHOW_TEXT_ON_PERSPECTIVE_BAR
                        .equals(propertyChangeEvent.getProperty())) {
                    if (perspectiveBar == null) {
                        return;
                    }
                    updatePerspectiveBar();
                    updateBarParent();
                }
            }
        };
        apiPreferenceStore.addPropertyChangeListener(propertyChangeListener);
    }

    private void createControlForLeft() {
        trimControl = new Composite(parent, SWT.NONE);

        trimControl.setLayout(new CellLayout(1).setMargins(0, 0).setSpacing(3, 3).setDefaultRow(Row.fixed())
                .setDefaultColumn(Row.growing()));

        perspectiveBar = createBarManager(SWT.VERTICAL);

        perspectiveBar.createControl(trimControl);
        perspectiveBar.getControl().addListener(SWT.MenuDetect, popupListener);

        //        trimSeparator = new Label(trimControl, SWT.SEPARATOR | SWT.HORIZONTAL);
        //        GridData sepData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING
        //                | GridData.HORIZONTAL_ALIGN_CENTER);
        //        sepData.widthHint = SEPARATOR_LENGTH;
        //        trimSeparator.setLayoutData(sepData);
        //        trimSeparator.setVisible(false);

        trimLayoutData = new GridData(GridData.FILL_BOTH);
        trimVisible = false;
        perspectiveBar.getControl().setLayoutData(trimLayoutData);
    }

    private void createControlForTop() {
        perspectiveBar = createBarManager(SWT.HORIZONTAL);

        perspectiveCoolBarWrapper = new CacheWrapper(topBar);
        perspectiveCoolBar = new CoolBar(perspectiveCoolBarWrapper.getControl(), SWT.FLAT);
        coolItem = new CoolItem(perspectiveCoolBar, SWT.DROP_DOWN);
        toolbarWrapper = new CacheWrapper(perspectiveCoolBar);
        perspectiveBar.createControl(toolbarWrapper.getControl());
        coolItem.setControl(toolbarWrapper.getControl());
        perspectiveCoolBar.setLocked(true);
        perspectiveBar.setParent(perspectiveCoolBar);
        perspectiveBar.update(true);

        // adjust the toolbar size to display as many items as possible
        perspectiveCoolBar.addControlListener(new ControlAdapter() {
            public void controlResized(ControlEvent e) {
                setCoolItemSize(coolItem);
            }
        });

        coolItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                if (e.detail == SWT.ARROW) {
                    if (perspectiveBar != null) {
                        perspectiveBar.handleChevron(e);
                    }
                }
            }
        });
        coolItem.setMinimumSize(0, 0);
        perspectiveBar.getControl().addListener(SWT.MenuDetect, popupListener);
    }

    /**
     * @param coolItem
     */
    private void setCoolItemSize(final CoolItem coolItem) {
        // there is no coolItem when the bar is on the left
        if (currentLocation == LEFT) {
            return;
        }

        ToolBar toolbar = perspectiveBar.getControl();
        if (toolbar == null) {
            return;
        }

        int rowHeight = 0;
        ToolItem[] toolItems = toolbar.getItems();
        for (int i = 0; i < toolItems.length; i++) {
            rowHeight = Math.max(rowHeight, toolItems[i].getBounds().height);
        }

        Rectangle area = perspectiveCoolBar.getClientArea();
        int rows = rowHeight <= 0 ? 1 : (int) Math.max(1, Math.floor(area.height / rowHeight));
        if (rows == 1 || (toolbar.getStyle() & SWT.WRAP) == 0 || currentLocation == TOP_LEFT) {
            Point p = toolbar.computeSize(SWT.DEFAULT, SWT.DEFAULT);
            coolItem.setSize(coolItem.computeSize(p.x, p.y));
            return;
        }
        Point offset = coolItem.computeSize(0, 0);
        Point wrappedSize = toolbar.computeSize(area.width - offset.x, SWT.DEFAULT);
        int h = rows * rowHeight;
        int w = wrappedSize.y <= h ? wrappedSize.x : wrappedSize.x + 1;
        coolItem.setSize(coolItem.computeSize(w, h));
    }

    private void showPerspectiveBarPopup(Point pt) {
        if (perspectiveBar == null) {
            return;
        }

        // Get the tool item under the mouse.
        ToolBar toolBar = perspectiveBar.getControl();
        ToolItem toolItem = toolBar.getItem(toolBar.toControl(pt));

        // Get the action for the tool item.
        Object data = null;
        if (toolItem != null) {
            data = toolItem.getData();
        }
        if (toolItem == null || !(data instanceof PerspectiveBarContributionItem)) {
            if (genericMenu == null) {
                Menu menu = new Menu(toolBar);
                addDockOnSubMenu(menu);
                addShowTextItem(menu);
                genericMenu = menu;
            }

            // set the state of the menu items to match the preferences
            genericMenu.getItem(1).setSelection(PrefUtil.getAPIPreferenceStore()
                    .getBoolean(IWorkbenchPreferenceConstants.SHOW_TEXT_ON_PERSPECTIVE_BAR));
            updateLocationItems(genericMenu.getItem(0).getMenu(), currentLocation);

            // Show popup menu.
            genericMenu.setLocation(pt.x, pt.y);
            genericMenu.setVisible(true);
            return;
        }

        if (data == null || !(data instanceof PerspectiveBarContributionItem)) {
            return;
        }

        PerspectiveBarContributionItem pbci = (PerspectiveBarContributionItem) data;
        IPerspectiveDescriptor selectedPerspective = pbci.getPerspective();

        // The perspective bar menu is created lazily here.
        // Its data is set (each time) to the tool item, which refers to the SetPagePerspectiveAction
        // which in turn refers to the page and perspective.
        // It is important not to refer to the action, the page or the perspective directly
        // since otherwise the menu hangs on to them after they are closed.
        // By hanging onto the tool item instead, these references are cleared when the
        // corresponding page or perspective is closed.
        // See bug 11282 for more details on why it is done this way.
        if (popupMenu != null) {
            popupMenu.dispose();
            popupMenu = null;
        }
        popupMenu = createPopup(toolBar, selectedPerspective);
        popupMenu.setData(toolItem);

        // Show popup menu.
        popupMenu.setLocation(pt.x, pt.y);
        popupMenu.setVisible(true);
    }

    /**
     * @param persp the perspective
     * @return <code>true</code> if the perspective is active in the active page
     */
    private boolean perspectiveIsActive(IPerspectiveDescriptor persp) {
        IWorkbenchPage page = window.getActivePage();
        return page != null && persp.equals(page.getPerspective());
    }

    /**
     * @param persp the perspective
     * @return <code>true</code> if the perspective is open in the active page
     */
    private boolean perspectiveIsOpen(IPerspectiveDescriptor persp) {
        IWorkbenchPage page = window.getActivePage();
        return page != null && Arrays.asList(page.getOpenPerspectives()).contains(persp);
    }

    private Menu createPopup(ToolBar toolBar, IPerspectiveDescriptor persp) {
        Menu menu = new Menu(toolBar);
        if (perspectiveIsActive(persp)) {
            addCustomizeItem(menu);
            addSaveAsItem(menu);
            addResetItem(menu);
        }
        if (perspectiveIsOpen(persp)) {
            addCloseItem(menu);
        }

        new MenuItem(menu, SWT.SEPARATOR);
        addDockOnSubMenu(menu);
        addShowTextItem(menu);
        return menu;
    }

    private void addCloseItem(Menu menu) {
        MenuItem menuItem = new MenuItem(menu, SWT.NONE);
        menuItem.setText(WorkbenchMessages.WorkbenchWindow_close);
        window.getWorkbench().getHelpSystem().setHelp(menuItem, IWorkbenchHelpContextIds.CLOSE_PAGE_ACTION);
        menuItem.addSelectionListener(new SelectionAdapter() {

            public void widgetSelected(SelectionEvent e) {
                ToolItem perspectiveToolItem = (ToolItem) popupMenu.getData();

                if (perspectiveToolItem != null && !perspectiveToolItem.isDisposed()) {
                    PerspectiveBarContributionItem item = (PerspectiveBarContributionItem) perspectiveToolItem
                            .getData();
                    IPerspectiveDescriptor persp = item.getPerspective();

                    ICommandService commandService = (ICommandService) window.getService(ICommandService.class);
                    Command command = commandService
                            .getCommand(IWorkbenchCommandConstants.WINDOW_CLOSE_PERSPECTIVE);

                    HashMap parameters = new HashMap();
                    parameters.put(IWorkbenchCommandConstants.WINDOW_CLOSE_PERSPECTIVE_PARM_ID, persp.getId());

                    ParameterizedCommand pCommand = ParameterizedCommand.generateCommand(command, parameters);

                    IHandlerService handlerService = (IHandlerService) window.getService(IHandlerService.class);
                    try {
                        handlerService.executeCommand(pCommand, new Event());
                    } catch (ExecutionException e1) {
                    } catch (NotDefinedException e1) {
                    } catch (NotEnabledException e1) {
                    } catch (NotHandledException e1) {
                    }
                }
            }
        });
    }

    /**
     * @param direction one of <code>SWT.HORIZONTAL</code> or <code>SWT.VERTICAL</code>
     */
    private PerspectiveBarManager createBarManager(int direction) {
        PerspectiveBarManager barManager = new PerspectiveBarManager(style | direction);
        // this is the index in which the item for recently perspectives should
        // be inserted into
        int perspectiveInsertionIndex = 0;
        if (apiPreferenceStore.getBoolean(IWorkbenchPreferenceConstants.SHOW_OPEN_ON_PERSPECTIVE_BAR)) {
            barManager.add(new PerspectiveBarNewContributionItem(window));
            // the 'Open Perspective' needs to go first, so we offset the other
            // perspective entries after it by setting our index to '1'
            perspectiveInsertionIndex = 1;
        }

        // add an item for all open perspectives
        IWorkbenchPage page = window.getActivePage();
        if (page != null) {
            // these are returned with the most recently opened one first
            IPerspectiveDescriptor[] perspectives = page.getOpenPerspectives();
            for (int i = 0; i < perspectives.length; i++) {
                barManager.insert(perspectiveInsertionIndex,
                        new PerspectiveBarContributionItem(perspectives[i], page));
            }
        }

        return barManager;
    }

    private void updateLocationItems(Menu parent, int newLocation) {
        MenuItem left;
        MenuItem topLeft;
        MenuItem topRight;

        topRight = parent.getItem(0);
        topLeft = parent.getItem(1);
        left = parent.getItem(2);

        if (newLocation == LEFT) {
            left.setSelection(true);
            topRight.setSelection(false);
            topLeft.setSelection(false);
        } else if (newLocation == TOP_LEFT) {
            topLeft.setSelection(true);
            left.setSelection(false);
            topRight.setSelection(false);
        } else {
            topRight.setSelection(true);
            left.setSelection(false);
            topLeft.setSelection(false);
        }
    }

    private void addDockOnSubMenu(Menu menu) {
        MenuItem item = new MenuItem(menu, SWT.CASCADE);
        item.setText(WorkbenchMessages.PerspectiveSwitcher_dockOn);

        final Menu subMenu = new Menu(item);

        final MenuItem menuItemTopRight = new MenuItem(subMenu, SWT.RADIO);
        menuItemTopRight.setText(WorkbenchMessages.PerspectiveSwitcher_topRight);

        window.getWorkbench().getHelpSystem().setHelp(menuItemTopRight,
                IWorkbenchHelpContextIds.DOCK_ON_PERSPECTIVE_ACTION);

        final MenuItem menuItemTopLeft = new MenuItem(subMenu, SWT.RADIO);
        menuItemTopLeft.setText(WorkbenchMessages.PerspectiveSwitcher_topLeft);

        window.getWorkbench().getHelpSystem().setHelp(menuItemTopLeft,
                IWorkbenchHelpContextIds.DOCK_ON_PERSPECTIVE_ACTION);

        final MenuItem menuItemLeft = new MenuItem(subMenu, SWT.RADIO);
        menuItemLeft.setText(WorkbenchMessages.PerspectiveSwitcher_left);

        window.getWorkbench().getHelpSystem().setHelp(menuItemLeft,
                IWorkbenchHelpContextIds.DOCK_ON_PERSPECTIVE_ACTION);

        SelectionListener listener = new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                MenuItem item = (MenuItem) e.widget;
                String pref = null;
                if (item.equals(menuItemLeft)) {
                    updateLocationItems(subMenu, LEFT);
                    pref = IWorkbenchPreferenceConstants.LEFT;
                } else if (item.equals(menuItemTopLeft)) {
                    updateLocationItems(subMenu, TOP_LEFT);
                    pref = IWorkbenchPreferenceConstants.TOP_LEFT;
                } else {
                    updateLocationItems(subMenu, TOP_RIGHT);
                    pref = IWorkbenchPreferenceConstants.TOP_RIGHT;
                }
                IPreferenceStore apiStore = PrefUtil.getAPIPreferenceStore();
                if (!pref.equals(apiStore.getDefaultString(IWorkbenchPreferenceConstants.DOCK_PERSPECTIVE_BAR))) {
                    PrefUtil.getInternalPreferenceStore().setValue(IPreferenceConstants.OVERRIDE_PRESENTATION,
                            true);
                }
                apiStore.setValue(IWorkbenchPreferenceConstants.DOCK_PERSPECTIVE_BAR, pref);
            }
        };

        menuItemTopRight.addSelectionListener(listener);
        menuItemTopLeft.addSelectionListener(listener);
        menuItemLeft.addSelectionListener(listener);
        item.setMenu(subMenu);
        updateLocationItems(subMenu, currentLocation);
    }

    private void addShowTextItem(Menu menu) {
        final MenuItem showtextMenuItem = new MenuItem(menu, SWT.CHECK);
        showtextMenuItem.setText(WorkbenchMessages.PerspectiveBar_showText);
        window.getWorkbench().getHelpSystem().setHelp(showtextMenuItem,
                IWorkbenchHelpContextIds.SHOW_TEXT_PERSPECTIVE_ACTION);

        showtextMenuItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                if (perspectiveBar == null) {
                    return;
                }

                boolean preference = showtextMenuItem.getSelection();
                if (preference != PrefUtil.getAPIPreferenceStore()
                        .getDefaultBoolean(IWorkbenchPreferenceConstants.SHOW_TEXT_ON_PERSPECTIVE_BAR)) {
                    PrefUtil.getInternalPreferenceStore().setValue(IPreferenceConstants.OVERRIDE_PRESENTATION,
                            true);
                }
                PrefUtil.getAPIPreferenceStore()
                        .setValue(IWorkbenchPreferenceConstants.SHOW_TEXT_ON_PERSPECTIVE_BAR, preference);
            }
        });
        showtextMenuItem.setSelection(PrefUtil.getAPIPreferenceStore()
                .getBoolean(IWorkbenchPreferenceConstants.SHOW_TEXT_ON_PERSPECTIVE_BAR));
    }

    private void addCustomizeItem(Menu menu) {
        final MenuItem customizeMenuItem = new MenuItem(menu, SWT.Activate);
        customizeMenuItem.setText(WorkbenchMessages.PerspectiveBar_customize);
        window.getWorkbench().getHelpSystem().setHelp(customizeMenuItem,
                IWorkbenchHelpContextIds.EDIT_ACTION_SETS_ACTION);
        customizeMenuItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                if (perspectiveBar == null) {
                    return;
                }
                IHandlerService handlerService = (IHandlerService) window.getService(IHandlerService.class);
                try {
                    handlerService.executeCommand(IWorkbenchCommandConstants.WINDOW_CUSTOMIZE_PERSPECTIVE, null);
                } catch (ExecutionException e1) {
                } catch (NotDefinedException e1) {
                } catch (NotEnabledException e1) {
                } catch (NotHandledException e1) {
                }
            }
        });
    }

    private void addSaveAsItem(Menu menu) {
        final MenuItem saveasMenuItem = new MenuItem(menu, SWT.Activate);
        saveasMenuItem.setText(WorkbenchMessages.PerspectiveBar_saveAs);
        window.getWorkbench().getHelpSystem().setHelp(saveasMenuItem,
                IWorkbenchHelpContextIds.SAVE_PERSPECTIVE_ACTION);
        saveasMenuItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent event) {
                if (perspectiveBar == null) {
                    return;
                }
                IHandlerService handlerService = (IHandlerService) window.getService(IHandlerService.class);
                IStatus status = Status.OK_STATUS;
                try {
                    handlerService.executeCommand(IWorkbenchCommandConstants.WINDOW_SAVE_PERSPECTIVE_AS, null);
                } catch (ExecutionException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotDefinedException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotEnabledException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotHandledException e) {
                }
                if (!status.isOK())
                    StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG);
            }
        });
    }

    private void addResetItem(Menu menu) {
        final MenuItem resetMenuItem = new MenuItem(menu, SWT.Activate);
        resetMenuItem.setText(WorkbenchMessages.PerspectiveBar_reset);
        window.getWorkbench().getHelpSystem().setHelp(resetMenuItem,
                IWorkbenchHelpContextIds.RESET_PERSPECTIVE_ACTION);
        resetMenuItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent event) {
                if (perspectiveBar == null) {
                    return;
                }
                IHandlerService handlerService = (IHandlerService) window.getService(IHandlerService.class);
                IStatus status = Status.OK_STATUS;
                try {
                    handlerService.executeCommand(IWorkbenchCommandConstants.WINDOW_RESET_PERSPECTIVE, null);
                } catch (ExecutionException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotDefinedException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotEnabledException e) {
                    status = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, e.getMessage(), e);
                } catch (NotHandledException e) {
                }
                if (!status.isOK())
                    StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.LOG);
            }
        });
    }

    /**
     * Method to save the width of the perspective bar in the
     * @param persBarMem
     */
    public void saveState(IMemento persBarMem) {
        // save the width of the perspective bar
        IMemento childMem = persBarMem.createChild(IWorkbenchConstants.TAG_ITEM_SIZE);

        int x;
        if (currentLocation == TOP_RIGHT && topBar != null) {
            x = topBar.getRightWidth();
        } else {
            x = getDefaultWidth();
        }

        childMem.putString(IWorkbenchConstants.TAG_X, Integer.toString(x));
    }

    /**
     * Method to restore the width of the perspective bar
     * @param memento
     */
    public void restoreState(IMemento memento) {
        if (memento == null) {
            return;
        }
        // restore the width of the perspective bar
        IMemento attributes = memento.getChild(IWorkbenchConstants.TAG_PERSPECTIVE_BAR);
        IMemento size = null;
        if (attributes != null) {
            size = attributes.getChild(IWorkbenchConstants.TAG_ITEM_SIZE);
        }
        if (size != null && currentLocation == TOP_RIGHT && topBar != null) {
            final Integer x = size.getInteger(IWorkbenchConstants.TAG_X);
            StartupThreading.runWithoutExceptions(new StartupRunnable() {

                public void runWithException() {
                    if (x != null) {
                        topBar.setRightWidth(x.intValue());
                    } else {
                        topBar.setRightWidth(getDefaultWidth());
                    }
                }
            });
        }
    }

    /**
     * Method to rebuild and update the toolbar when necessary
     */
    void updatePerspectiveBar() {
        // Update each item as the text may have to be shortened.
        IContributionItem[] items = perspectiveBar.getItems();
        for (int i = 0; i < items.length; i++) {
            items[i].update();
        }
        // make sure the selected item is visible
        perspectiveBar.arrangeToolbar();
        setCoolItemSize(coolItem);
        perspectiveBar.getControl().redraw();

        if (getControl() != null)
            getControl().pack(true);
    }

    /**
     * Updates the height of the CBanner if the perspective bar
     * is docked on the top right
     */
    public void updateBarParent() {
        if (perspectiveBar == null || perspectiveBar.getControl() == null) {
            return;
        }

        // TOP_LEFT and LEFT need only relayout in this case, however TOP_RIGHT
        // will need to set the minimum height of the CBanner as it might have changed.
        if (currentLocation == TOP_RIGHT && topBar != null) {
            // This gets the height of the tallest tool item.
            int maxRowHeight = 0;
            ToolItem[] toolItems = perspectiveBar.getControl().getItems();
            for (int i = 0; i < toolItems.length; i++) {
                maxRowHeight = Math.max(maxRowHeight, toolItems[i].getBounds().height);
            }
            // This sets the CBanner's minimum height to support large fonts
            // TODO: Actually calculate the correct 'min' size for the right side
            topBar.setRightMinimumSize(new Point(MIN_WIDTH, maxRowHeight));
        }

        LayoutUtil.resize(perspectiveBar.getControl());
    }

    /**
     * Add a listener for reordering of perspectives (usually done through drag
     * and drop).
     * 
     * @param listener
     */
    public void addReorderListener(IReorderListener listener) {
        reorderListener = listener;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#dock(int)
     */
    public void dock(int dropSide) {
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#getControl()
     */
    public Control getControl() {
        return trimControl;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#getId()
     */
    public String getId() {
        return "org.eclipse.ui.internal.PerspectiveSwitcher"; //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#getDisplayName()
     */
    public String getDisplayName() {
        return WorkbenchMessages.TrimCommon_PerspectiveSwitcher_TrimName;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#getValidSides()
     */
    public int getValidSides() {
        return SWT.NONE;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#isCloseable()
     */
    public boolean isCloseable() {
        return false;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.internal.IWindowTrim#handleClose()
     */
    public void handleClose() {
        // nothing to do...
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IWindowTrim#getWidthHint()
     */
    public int getWidthHint() {
        return SWT.DEFAULT;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IWindowTrim#getHeightHint()
     */
    public int getHeightHint() {
        return SWT.DEFAULT;
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.IWindowTrim#isResizeable()
     */
    public boolean isResizeable() {
        return false;
    }
}