blob: 9fc836caefc39e206938f211779db4aa4e4cebb1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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
*******************************************************************************/
package org.eclipse.ui.internal;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.eclipse.jface.util.Geometry;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartConstants;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.contexts.IContextService;
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.presentations.PresentationFactoryUtil;
import org.eclipse.ui.internal.presentations.util.AbstractTabFolder;
import org.eclipse.ui.internal.presentations.util.AbstractTabItem;
import org.eclipse.ui.internal.presentations.util.TabbedStackPresentation;
import org.eclipse.ui.presentations.StackDropResult;
/**
* TODO: Drag from detached to fast view bar back to detached causes NPE
*
* @since 3.1
*/
public class DetachedWindow implements IDragOverListener {
private PartStack folder;
private WorkbenchPage page;
private Rectangle bounds = new Rectangle(0,0,0,0);
private Shell windowShell;
private boolean hideViewsOnClose = true;
private ShellListener shellListener = new ShellAdapter() {
public void shellClosed(ShellEvent e) {
handleClose();
}
};
private Listener resizeListener = new Listener() {
public void handleEvent(Event event) {
Shell shell = (Shell) event.widget;
folder.setBounds(shell.getClientArea());
}
};
private IPropertyListener propertyListener = new IPropertyListener() {
public void propertyChanged(Object source, int propId) {
if (propId == PartStack.PROP_SELECTION) {
activePartChanged(getPartReference(folder.getSelection()));
}
}
};
private IWorkbenchPartReference activePart;
private IPropertyListener partPropertyListener = new IPropertyListener() {
public void propertyChanged(Object source, int propId) {
if (propId == IWorkbenchPartConstants.PROP_TITLE) {
updateTitle();
}
}
};
/**
* Create a new FloatingWindow.
*/
public DetachedWindow(WorkbenchPage workbenchPage) {
this.page = workbenchPage;
folder = new ViewStack(page, false, PresentationFactoryUtil.ROLE_VIEW, null);
folder.addListener(propertyListener);
}
protected void activePartChanged(IWorkbenchPartReference partReference) {
if (activePart == partReference) {
return;
}
if (activePart != null) {
activePart.removePropertyListener(partPropertyListener);
}
activePart = partReference;
if (partReference != null) {
partReference.addPropertyListener(partPropertyListener);
}
updateTitle();
}
private void updateTitle() {
if (activePart != null) {
// Uncomment to set the shell title to match the title of the active part
// String text = activePart.getTitle();
//
// if (!text.equals(s.getText())) {
// s.setText(text);
// }
}
}
private static IWorkbenchPartReference getPartReference(PartPane pane) {
if (pane == null) {
return null;
}
return pane.getPartReference();
}
public Shell getShell() {
return windowShell;
}
public void create() {
windowShell = ((WorkbenchWindow)page.getWorkbenchWindow()).getDetachedWindowPool().allocateShell(shellListener);
windowShell.setData(this);
windowShell.setText(""); //$NON-NLS-1$
DragUtil.addDragTarget(windowShell, this);
hideViewsOnClose = true;
if (bounds.isEmpty()) {
Point center = Geometry.centerPoint(page.getWorkbenchWindow().getShell().getBounds());
Point size = new Point(300, 200);
Point upperLeft = Geometry.subtract(center, Geometry.divide(size, 2));
bounds = Geometry.createRectangle(upperLeft, size);
}
getShell().setBounds(bounds);
configureShell(windowShell);
createContents(windowShell);
windowShell.layout(true);
folder.setBounds(windowShell.getClientArea());
}
/**
* Adds a visual part to this window.
* Supports reparenting.
*/
public void add(ViewPane part) {
Shell shell = getShell();
if (shell != null) {
part.reparent(shell);
}
folder.add(part);
// Ensure that the shell's minimum size is capable of showing the initial first tab
// We can only do this for 'Tabbed' stacked presentations...
if (folder.getPresentation() instanceof TabbedStackPresentation) {
TabbedStackPresentation stack = (TabbedStackPresentation) folder.getPresentation();
AbstractTabFolder tabFolder = stack.getTabFolder();
if (tabFolder.getItemCount() == 1) {
// Get the space that we need to show the tab
AbstractTabItem firstItem = tabFolder.getItem(0);
Rectangle tabRect = firstItem.getBounds();
// Take the current shell 'trim' into account
int shellHeight = windowShell.getBounds().height - windowShell.getClientArea().height;
int shellWidth = windowShell.getBounds().width - windowShell.getClientArea().width;
windowShell.setMinimumSize(tabRect.width + shellWidth, tabRect.height + shellHeight);
}
}
}
public boolean belongsToWorkbenchPage(IWorkbenchPage workbenchPage) {
return (this.page == workbenchPage);
}
public boolean close() {
hideViewsOnClose = false;
Shell shell = getShell();
if (shell != null) {
shell.close();
}
return true;
}
/**
* Closes this window and disposes its shell.
*/
private boolean handleClose() {
if (hideViewsOnClose) {
List views = new ArrayList();
collectViewPanes(views, getChildren());
Iterator itr = views.iterator();
while (itr.hasNext()) {
ViewPane child = (ViewPane) itr.next();
// Only close if closable...
if (child.isCloseable()) {
page.hideView(child.getViewReference());
} else {
page.attachView(child.getViewReference());
}
}
}
if (folder != null) {
folder.dispose();
}
if (windowShell != null) {
windowShell.removeListener(SWT.Resize, resizeListener);
DragUtil.removeDragTarget(windowShell, this);
bounds = windowShell.getBounds();
// Unregister this detached view as a window (for key bindings).
final IContextService contextService = (IContextService) getWorkbenchPage().getWorkbenchWindow().getWorkbench().getService(IContextService.class);
contextService.unregisterShell(windowShell);
windowShell.setData(null);
windowShell = null;
}
return true;
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.dnd.IDragOverListener#drag(org.eclipse.swt.widgets.Control, java.lang.Object, org.eclipse.swt.graphics.Point, org.eclipse.swt.graphics.Rectangle)
*/
public IDropTarget drag(Control currentControl, Object draggedObject,
Point position, Rectangle dragRectangle) {
if (!(draggedObject instanceof PartPane)) {
return null;
}
final PartPane sourcePart = (PartPane) draggedObject;
if (sourcePart.getWorkbenchWindow() != page.getWorkbenchWindow()) {
return null;
}
// Only handle the event if the source part is acceptable to the particular PartStack
IDropTarget target = null;
if (folder.allowsDrop(sourcePart)) {
target = folder.getDropTarget(draggedObject, position);
if (target == null) {
Rectangle displayBounds = DragUtil.getDisplayBounds(folder.getControl());
if (displayBounds.contains(position)) {
target = folder.createDropTarget(sourcePart, new StackDropResult(displayBounds, null));
} else {
return null;
}
}
}
return target;
}
/**
* Answer a list of the view panes.
*/
private void collectViewPanes(List result, LayoutPart[] parts) {
for (int i = 0, length = parts.length; i < length; i++) {
LayoutPart part = parts[i];
if (part instanceof ViewPane) {
result.add(part);
}
}
}
/**
* This method will be called to initialize the given Shell's layout
*/
protected void configureShell(Shell shell) {
updateTitle();
shell.addListener(SWT.Resize, resizeListener);
// Register this detached view as a window (for key bindings).
final IContextService contextService = (IContextService) getWorkbenchPage()
.getWorkbenchWindow().getWorkbench().getService(IContextService.class);
contextService.registerShell(shell,
IContextService.TYPE_WINDOW);
page.getWorkbenchWindow().getWorkbench().getHelpSystem().setHelp(shell,
IWorkbenchHelpContextIds.DETACHED_WINDOW);
}
/**
* Override this method to create the widget tree that is used as the window's contents.
*/
protected Control createContents(Composite parent) {
// Create the tab folder.
folder.createControl(parent);
// Reparent each view in the tab folder.
Vector detachedChildren = new Vector();
collectViewPanes(detachedChildren, getChildren());
Enumeration itr = detachedChildren.elements();
while (itr.hasMoreElements()) {
LayoutPart part = (LayoutPart) itr.nextElement();
part.reparent(parent);
}
// Return tab folder control.
return folder.getControl();
}
public LayoutPart[] getChildren() {
return folder.getChildren();
}
public WorkbenchPage getWorkbenchPage() {
return this.page;
}
/**
* @see IPersistablePart
*/
public void restoreState(IMemento memento) {
// Read the bounds.
Integer bigInt;
bigInt = memento.getInteger(IWorkbenchConstants.TAG_X);
int x = bigInt.intValue();
bigInt = memento.getInteger(IWorkbenchConstants.TAG_Y);
int y = bigInt.intValue();
bigInt = memento.getInteger(IWorkbenchConstants.TAG_WIDTH);
int width = bigInt.intValue();
bigInt = memento.getInteger(IWorkbenchConstants.TAG_HEIGHT);
int height = bigInt.intValue();
bigInt = memento.getInteger(IWorkbenchConstants.TAG_FLOAT);
// Set the bounds.
bounds = new Rectangle(x, y, width, height);
if (getShell() != null) {
getShell().setBounds(bounds);
}
// Create the folder.
IMemento childMem = memento.getChild(IWorkbenchConstants.TAG_FOLDER);
if (childMem != null) {
folder.restoreState(childMem);
}
}
/**
* @see IPersistablePart
*/
public void saveState(IMemento memento) {
if (getShell() != null) {
bounds = getShell().getBounds();
}
// Save the bounds.
memento.putInteger(IWorkbenchConstants.TAG_X, bounds.x);
memento.putInteger(IWorkbenchConstants.TAG_Y, bounds.y);
memento.putInteger(IWorkbenchConstants.TAG_WIDTH, bounds.width);
memento.putInteger(IWorkbenchConstants.TAG_HEIGHT, bounds.height);
// Save the views.
IMemento childMem = memento.createChild(IWorkbenchConstants.TAG_FOLDER);
folder.saveState(childMem);
}
/* (non-Javadoc)
* @see org.eclipse.ui.internal.IWorkbenchDragDropPart#getControl()
*/
public Control getControl() {
return folder.getControl();
}
/**
* Opens the detached window.
*/
public int open() {
if (getShell() == null) {
create();
}
Rectangle bounds = getShell().getBounds();
getShell().setVisible(true);
if (!bounds.equals(getShell().getBounds())) {
getShell().setBounds(bounds);
}
return Window.OK;
}
}