blob: 416404ddc0eb84c03cc5cdb61e3e2c0da04460c9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2007 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
* Stefan Xenos, IBM - bug 51580
* Chris Torrence, ITT Visual Information Solutions - bugs 51580 202208
*******************************************************************************/
package org.eclipse.ui.internal.presentations.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.preferences.IDynamicPropertyMap;
import org.eclipse.ui.internal.preferences.PreferenceStoreAdapter;
import org.eclipse.ui.internal.preferences.PreferencesAdapter;
import org.eclipse.ui.internal.preferences.PropertyMapAdapter;
import org.eclipse.ui.internal.preferences.ThemeManagerAdapter;
import org.eclipse.ui.internal.presentations.defaultpresentation.DefaultPartList;
import org.eclipse.ui.internal.util.PrefUtil;
import org.eclipse.ui.presentations.IPartMenu;
import org.eclipse.ui.presentations.IPresentablePart;
import org.eclipse.ui.presentations.IPresentationSerializer;
import org.eclipse.ui.presentations.IStackPresentationSite;
import org.eclipse.ui.presentations.StackDropResult;
import org.eclipse.ui.presentations.StackPresentation;
/**
* @since 3.0
*/
public final class TabbedStackPresentation extends StackPresentation {
private PresentablePartFolder folder;
private ISystemMenu systemMenu;
private ISystemMenu partList;
private PreferenceStoreAdapter apiPreferences = new PreferenceStoreAdapter(PrefUtil
.getAPIPreferenceStore());
private ThemeManagerAdapter themePreferences = new ThemeManagerAdapter(
PlatformUI.getWorkbench().getThemeManager());
private TabOrder tabs;
private TabDragHandler dragBehavior;
private boolean initializing = true;
private int ignoreSelectionChanges = 0;
private TabFolderListener tabFolderListener = new TabFolderListener() {
public void handleEvent(TabFolderEvent e) {
switch (e.type) {
case TabFolderEvent.EVENT_MINIMIZE: {
getSite().setState(IStackPresentationSite.STATE_MINIMIZED);
break;
}
case TabFolderEvent.EVENT_MAXIMIZE: {
getSite().setState(IStackPresentationSite.STATE_MAXIMIZED);
break;
}
case TabFolderEvent.EVENT_RESTORE: {
getSite().setState(IStackPresentationSite.STATE_RESTORED);
break;
}
case TabFolderEvent.EVENT_CLOSE: {
IPresentablePart part = folder.getPartForTab(e.tab);
if (part != null) {
getSite().close(new IPresentablePart[] { part });
}
break;
}
case TabFolderEvent.EVENT_SHOW_LIST: {
showPartList();
break;
}
case TabFolderEvent.EVENT_GIVE_FOCUS_TO_PART: {
IPresentablePart part = getSite().getSelectedPart();
if (part != null) {
part.setFocus();
}
break;
}
case TabFolderEvent.EVENT_PANE_MENU: {
IPresentablePart part = getSite().getSelectedPart();
if (part != null) {
part.setFocus();
}
TabbedStackPresentation.this.showPaneMenu(folder
.getPartForTab(e.tab), new Point(e.x, e.y));
break;
}
case TabFolderEvent.EVENT_DRAG_START: {
AbstractTabItem beingDragged = e.tab;
Point initialLocation = new Point(e.x, e.y);
if (beingDragged == null) {
getSite().dragStart(initialLocation, false);
} else {
IPresentablePart part = folder.getPartForTab(beingDragged);
try {
dragStart = folder.indexOf(part);
getSite().dragStart(part, initialLocation, false);
} finally {
dragStart = -1;
}
}
break;
}
case TabFolderEvent.EVENT_TAB_SELECTED: {
if (ignoreSelectionChanges > 0) {
return;
}
IPresentablePart part = folder.getPartForTab(e.tab);
if (part != null) {
getSite().selectPart(part);
}
break;
}
case TabFolderEvent.EVENT_SYSTEM_MENU: {
IPresentablePart part = folder.getPartForTab(e.tab);
if (part == null) {
part = getSite().getSelectedPart();
}
if (part != null) {
showSystemMenu(new Point(e.x, e.y), part);
}
break;
}
case TabFolderEvent.EVENT_PREFERRED_SIZE: {
IPresentablePart part = folder.getPartForTab(e.tab);
if (part == null) {
// Standalone views with no title have no tab, so just get the part.
IPresentablePart[] parts = getSite().getPartList();
if (parts.length > 0) part = parts[0];
}
if (part == getSite().getSelectedPart()) {
getSite().flushLayout();
}
break;
}
}
}
};
private int dragStart = -1;
private Map prefs = new HashMap();
public TabbedStackPresentation(IStackPresentationSite site, AbstractTabFolder widget, ISystemMenu systemMenu) {
this(site, new PresentablePartFolder(widget), systemMenu);
}
public TabbedStackPresentation(IStackPresentationSite site, PresentablePartFolder folder, ISystemMenu systemMenu) {
this(site, folder, new LeftToRightTabOrder(folder), new ReplaceDragHandler(folder.getTabFolder()), systemMenu);
}
public TabbedStackPresentation(IStackPresentationSite site,
PresentablePartFolder newFolder, TabOrder tabs, TabDragHandler dragBehavior, ISystemMenu systemMenu) {
super(site);
this.systemMenu = systemMenu;
this.folder = newFolder;
this.tabs = tabs;
this.dragBehavior = dragBehavior;
// Add a dispose listener. This will call the presentationDisposed()
// method when the widget is destroyed.
folder.getTabFolder().getControl().addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
presentationDisposed();
}
});
folder.getTabFolder().addListener(tabFolderListener);
this.partList = new DefaultPartList(site, newFolder);
}
/**
* Restores a presentation from a previously stored state
*
* @param serializer (not null)
* @param savedState (not null)
*/
public void restoreState(IPresentationSerializer serializer,
IMemento savedState) {
tabs.restoreState(serializer, savedState);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#saveState(org.eclipse.ui.presentations.IPresentationSerializer, org.eclipse.ui.IMemento)
*/
public void saveState(IPresentationSerializer context, IMemento memento) {
super.saveState(context, memento);
tabs.saveState(context, memento);
}
/**
* Returns true iff the presentation has been disposed
*
* @return true iff the presentation has been disposed
*/
private boolean isDisposed() {
return folder == null || folder.isDisposed();
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#setBounds(org.eclipse.swt.graphics.Rectangle)
*/
public void setBounds(Rectangle bounds) {
folder.setBounds(bounds);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#computeMinimumSize()
*/
public Point computeMinimumSize() {
return folder.getTabFolder().computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
/**
* Returns the minimum size for this stack, taking into account
* the available perpendicular space.
* @param width indicates whether a width (=true) or a height (=false) is being computed
* @param availablePerpendicular available space perpendicular to the direction being measured
* or INFINITE if unbounded (pixels).
* @return returns the preferred minimum size (pixels).
* This is a width if width == true or a height if width == false.
*/
private int computePreferredMinimumSize(boolean width, int availablePerpendicular) {
int minSize;
int hint = availablePerpendicular == INFINITE ? SWT.DEFAULT : availablePerpendicular;
if (width) {
minSize = folder.getTabFolder().computeSize(SWT.DEFAULT, hint).x;
} else {
minSize = folder.getTabFolder().computeSize(hint, SWT.DEFAULT).y;
}
return minSize;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISizeProvider#computePreferredSize(boolean, int, int, int)
*/
public int computePreferredSize(boolean width, int availableParallel,
int availablePerpendicular, int preferredResult) {
// If there is exactly one part in the stack, this just returns the
// preferred size of the part as the preferred size of the stack.
IPresentablePart[] parts = getSite().getPartList();
if (parts.length == 1 && parts[0] != null
&& !(getSite().getState() == IStackPresentationSite.STATE_MINIMIZED)) {
int partSize = parts[0].computePreferredSize(width,
availableParallel, availablePerpendicular, preferredResult);
if (partSize == INFINITE)
return partSize;
// Adjust preferred size to take into account tab and border trim.
int minSize = computePreferredMinimumSize(width, availablePerpendicular);
if (width) {
// PaneFolder adds some bogus tab spacing, so just find the maximum width.
partSize = Math.max(minSize, partSize);
} else {
// Add them (but only if there's enough room)
if (INFINITE-minSize > partSize)
partSize += minSize;
}
return partSize;
}
if (preferredResult != INFINITE || getSite().getState() == IStackPresentationSite.STATE_MINIMIZED) {
int minSize = computePreferredMinimumSize(width, availablePerpendicular);
if (getSite().getState() == IStackPresentationSite.STATE_MINIMIZED) {
return minSize;
}
return Math.max(minSize, preferredResult);
}
return INFINITE;
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#getSizeFlags(boolean)
*/
public int getSizeFlags(boolean width) {
int flags = 0;
// If there is exactly one part in the stack,
// then take into account the size flags of the part.
IPresentablePart[] parts = getSite().getPartList();
if (parts.length == 1 && parts[0] != null) {
flags |= parts[0].getSizeFlags(width);
}
return flags | super.getSizeFlags(width);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#showPartList()
*/
public void showPartList() {
if (partList != null) {
final int numberOfParts = folder.getTabFolder().getItemCount();
if (numberOfParts > 0) {
partList.show(getControl(), folder.getTabFolder()
.getPartListLocation(), getSite().getSelectedPart());
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#dispose()
*/
public void dispose() {
// Dispose the tab folder's widgetry
folder.getTabFolder().getControl().dispose();
}
/**
* Called when the tab folder is disposed.
*/
private void presentationDisposed() {
apiPreferences.dispose();
themePreferences.dispose();
Iterator iter = prefs.values().iterator();
while(iter.hasNext()) {
PropertyMapAdapter next = (PropertyMapAdapter)iter.next();
next.dispose();
}
if (systemMenu != null) {
systemMenu.dispose();
}
if (partList != null) {
partList.dispose();
}
systemMenu = null;
partList = null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#setActive(int)
*/
public void setActive(int newState) {
folder.getTabFolder().setActive(newState);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#setVisible(boolean)
*/
public void setVisible(boolean isVisible) {
IPresentablePart current = getSite().getSelectedPart();
if (current != null) {
current.setVisible(isVisible);
}
folder.setVisible(isVisible);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#setState(int)
*/
public void setState(int state) {
folder.getTabFolder().setState(state);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#getControl()
*/
public Control getControl() {
return folder.getTabFolder().getControl();
}
/**
* @return AbstractTabFolder the presentation's tab folder
*/
public AbstractTabFolder getTabFolder() {
return folder.getTabFolder();
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#addPart(org.eclipse.ui.presentations.IPresentablePart, java.lang.Object)
*/
public void addPart(IPresentablePart newPart, Object cookie) {
ignoreSelectionChanges++;
try {
if (initializing) {
tabs.addInitial(newPart);
} else {
if (cookie == null) {
tabs.add(newPart);
} else {
int insertionPoint = dragBehavior
.getInsertionPosition(cookie);
tabs.insert(newPart, insertionPoint);
}
}
} finally {
ignoreSelectionChanges--;
}
if (tabs.getPartList().length == 1) {
if (newPart.getSizeFlags(true) != 0 || newPart.getSizeFlags(false) != 0) {
getSite().flushLayout();
}
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#movePart(org.eclipse.ui.presentations.IPresentablePart, java.lang.Object)
*/
public void movePart(IPresentablePart toMove, Object cookie) {
ignoreSelectionChanges++;
try {
int insertionPoint = dragBehavior.getInsertionPosition(cookie);
if (insertionPoint == folder.indexOf(toMove)) {
return;
}
tabs.move(toMove, insertionPoint);
} finally {
ignoreSelectionChanges--;
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#removePart(org.eclipse.ui.presentations.IPresentablePart)
*/
public void removePart(IPresentablePart oldPart) {
ignoreSelectionChanges++;
try {
tabs.remove(oldPart);
} finally {
ignoreSelectionChanges--;
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#selectPart(org.eclipse.ui.presentations.IPresentablePart)
*/
public void selectPart(IPresentablePart toSelect) {
initializing = false;
tabs.select(toSelect);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#dragOver(org.eclipse.swt.widgets.Control, org.eclipse.swt.graphics.Point)
*/
public StackDropResult dragOver(Control currentControl, Point location) {
return dragBehavior.dragOver(currentControl, location, dragStart);
}
public void showSystemMenu() {
showSystemMenu(folder.getTabFolder().getSystemMenuLocation(), getSite().getSelectedPart());
}
public void showSystemMenu(Point displayCoordinates, IPresentablePart context) {
if (context != getSite().getSelectedPart()) {
getSite().selectPart(context);
}
systemMenu.show(getControl(), displayCoordinates, context);
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#showPaneMenu()
*/
public void showPaneMenu() {
IPresentablePart part = getSite().getSelectedPart();
if (part != null) {
showPaneMenu(part, folder.getTabFolder().getPaneMenuLocation());
}
}
public void showPaneMenu(IPresentablePart part, Point location) {
Assert.isTrue(!isDisposed());
IPartMenu menu = part.getMenu();
if (menu != null) {
menu.showMenu(location);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.presentations.StackPresentation#getTabList(org.eclipse.ui.presentations.IPresentablePart)
*/
public Control[] getTabList(IPresentablePart part) {
ArrayList list = new ArrayList();
if (folder.getTabFolder().getTabPosition() == SWT.BOTTOM) {
if (part.getControl() != null) {
list.add(part.getControl());
}
}
list.add(folder.getTabFolder().getControl());
if (part.getToolBar() != null) {
list.add(part.getToolBar());
}
if (folder.getTabFolder().getTabPosition() == SWT.TOP) {
if (part.getControl() != null) {
list.add(part.getControl());
}
}
return (Control[]) list.toArray(new Control[list.size()]);
}
public void setPartList(ISystemMenu menu) {
this.partList = menu;
}
public IDynamicPropertyMap getTheme() {
return themePreferences;
}
public IDynamicPropertyMap getApiPreferences() {
return apiPreferences;
}
public IDynamicPropertyMap getPluginPreferences(Plugin toQuery) {
String id = toQuery.getBundle().getSymbolicName();
IDynamicPropertyMap result = (IDynamicPropertyMap)prefs.get(id);
if (result != null) {
return result;
}
result = new PreferencesAdapter(toQuery.getPluginPreferences());
prefs.put(id, result);
return result;
}
/**
* Move the tabs around. This is for testing <b>ONLY</b>.
* @param part the part to move
* @param index the new index
* @since 3.2
*/
public void moveTab(IPresentablePart part, int index) {
tabs.move(part, index);
folder.layout(true);
}
/**
* Get the tab list. This is for testing <b>ONLY</b>.
* @return the presentable parts in order.
* @since 3.2
*/
public IPresentablePart[] getPartList() {
return tabs.getPartList();
}
/**
* Cause the folder to hide or show its
* Minimize and Maximize affordances.
*
* @param show
* <code>true</code> - the min/max buttons are visible.
* @since 3.3
*/
public void showMinMax(boolean show) {
folder.getTabFolder().showMinMax(show);
}
}