001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: * Stefan Xenos, IBM; Chris Torrence, ITT Visual Information Solutions - bug 51580
011: *******************************************************************************/package org.eclipse.ui.internal;
012:
013: import java.util.ArrayList;
014: import java.util.Enumeration;
015: import java.util.Iterator;
016: import java.util.List;
017: import java.util.Vector;
018:
019: import org.eclipse.jface.util.Geometry;
020: import org.eclipse.jface.window.Window;
021: import org.eclipse.swt.SWT;
022: import org.eclipse.swt.events.ShellAdapter;
023: import org.eclipse.swt.events.ShellEvent;
024: import org.eclipse.swt.events.ShellListener;
025: import org.eclipse.swt.graphics.Point;
026: import org.eclipse.swt.graphics.Rectangle;
027: import org.eclipse.swt.widgets.Composite;
028: import org.eclipse.swt.widgets.Control;
029: import org.eclipse.swt.widgets.Event;
030: import org.eclipse.swt.widgets.Listener;
031: import org.eclipse.swt.widgets.Shell;
032: import org.eclipse.ui.IMemento;
033: import org.eclipse.ui.IPropertyListener;
034: import org.eclipse.ui.IWorkbenchPage;
035: import org.eclipse.ui.IWorkbenchPartConstants;
036: import org.eclipse.ui.IWorkbenchPartReference;
037: import org.eclipse.ui.contexts.IContextService;
038: import org.eclipse.ui.internal.dnd.DragUtil;
039: import org.eclipse.ui.internal.dnd.IDragOverListener;
040: import org.eclipse.ui.internal.dnd.IDropTarget;
041: import org.eclipse.ui.internal.presentations.PresentationFactoryUtil;
042: import org.eclipse.ui.internal.presentations.util.AbstractTabFolder;
043: import org.eclipse.ui.internal.presentations.util.TabFolderEvent;
044: import org.eclipse.ui.internal.presentations.util.TabFolderListener;
045: import org.eclipse.ui.internal.presentations.util.TabbedStackPresentation;
046: import org.eclipse.ui.presentations.StackDropResult;
047:
048: /**
049: * TODO: Drag from detached to fast view bar back to detached causes NPE
050: *
051: * @since 3.1
052: */
053: public class DetachedWindow implements IDragOverListener {
054:
055: public static final int INFINITE = Integer.MAX_VALUE;
056:
057: private PartStack folder;
058:
059: private WorkbenchPage page;
060:
061: private Rectangle bounds = new Rectangle(0, 0, 0, 0);
062:
063: private Shell windowShell;
064:
065: private boolean hideViewsOnClose = true;
066:
067: private ShellListener shellListener = new ShellAdapter() {
068: public void shellClosed(ShellEvent e) {
069: handleClose();
070: }
071: };
072:
073: private Listener resizeListener = new Listener() {
074: public void handleEvent(Event event) {
075: Shell shell = (Shell) event.widget;
076: folder.setBounds(shell.getClientArea());
077: }
078: };
079:
080: private IPropertyListener propertyListener = new IPropertyListener() {
081: public void propertyChanged(Object source, int propId) {
082: if (propId == PartStack.PROP_SELECTION) {
083: activePartChanged(getPartReference(folder
084: .getSelection()));
085: }
086: }
087: };
088:
089: private IWorkbenchPartReference activePart;
090:
091: private IPropertyListener partPropertyListener = new IPropertyListener() {
092: public void propertyChanged(Object source, int propId) {
093: if (propId == IWorkbenchPartConstants.PROP_TITLE) {
094: updateTitle();
095: }
096: }
097: };
098:
099: /**
100: * Create a new FloatingWindow.
101: */
102: public DetachedWindow(WorkbenchPage workbenchPage) {
103: this .page = workbenchPage;
104:
105: folder = new ViewStack(page, false,
106: PresentationFactoryUtil.ROLE_VIEW, null);
107: folder.addListener(propertyListener);
108: }
109:
110: protected void activePartChanged(
111: IWorkbenchPartReference partReference) {
112: if (activePart == partReference) {
113: return;
114: }
115:
116: if (activePart != null) {
117: activePart.removePropertyListener(partPropertyListener);
118: }
119: activePart = partReference;
120: if (partReference != null) {
121: partReference.addPropertyListener(partPropertyListener);
122: }
123: updateTitle();
124: }
125:
126: private void updateTitle() {
127: if (activePart != null) {
128: // Uncomment to set the shell title to match the title of the active part
129: // String text = activePart.getTitle();
130: //
131: // if (!text.equals(s.getText())) {
132: // s.setText(text);
133: // }
134: }
135: }
136:
137: /**
138: * Ensure that the shell's minimum size is equal to the minimum size
139: * of the first part added to the shell.
140: */
141: private void updateMinimumSize() {
142: // We can only do this for 'Tabbed' stacked presentations.
143: if (folder.getPresentation() instanceof TabbedStackPresentation) {
144: TabbedStackPresentation stack = (TabbedStackPresentation) folder
145: .getPresentation();
146:
147: if (stack.getPartList().length == 1) {
148: // Get the minimum space required for the part
149: int width = stack.computePreferredSize(true, INFINITE,
150: INFINITE, 0);
151: int height = stack.computePreferredSize(false,
152: INFINITE, INFINITE, 0);
153:
154: // Take the current shell 'trim' into account
155: int shellHeight = windowShell.getBounds().height
156: - windowShell.getClientArea().height;
157: int shellWidth = windowShell.getBounds().width
158: - windowShell.getClientArea().width;
159:
160: windowShell.setMinimumSize(width + shellWidth, height
161: + shellHeight);
162: }
163: }
164: }
165:
166: private static IWorkbenchPartReference getPartReference(
167: PartPane pane) {
168:
169: if (pane == null) {
170: return null;
171: }
172:
173: return pane.getPartReference();
174: }
175:
176: public Shell getShell() {
177: return windowShell;
178: }
179:
180: public void create() {
181: windowShell = ((WorkbenchWindow) page.getWorkbenchWindow())
182: .getDetachedWindowPool().allocateShell(shellListener);
183: windowShell.setData(this );
184: windowShell.setText(""); //$NON-NLS-1$
185: DragUtil.addDragTarget(windowShell, this );
186: hideViewsOnClose = true;
187: if (bounds.isEmpty()) {
188: Point center = Geometry.centerPoint(page
189: .getWorkbenchWindow().getShell().getBounds());
190: Point size = new Point(300, 200);
191: Point upperLeft = Geometry.subtract(center, Geometry
192: .divide(size, 2));
193:
194: bounds = Geometry.createRectangle(upperLeft, size);
195: }
196: getShell().setBounds(bounds);
197:
198: configureShell(windowShell);
199:
200: createContents(windowShell);
201: windowShell.layout(true);
202: folder.setBounds(windowShell.getClientArea());
203: }
204:
205: /**
206: * Adds a visual part to this window.
207: * Supports reparenting.
208: */
209: public void add(ViewPane part) {
210:
211: Shell shell = getShell();
212: if (shell != null) {
213: part.reparent(shell);
214: }
215: folder.add(part);
216: updateMinimumSize();
217: }
218:
219: public boolean belongsToWorkbenchPage(IWorkbenchPage workbenchPage) {
220: return (this .page == workbenchPage);
221: }
222:
223: public boolean close() {
224: hideViewsOnClose = false;
225: Shell shell = getShell();
226: if (shell != null) {
227: shell.close();
228: }
229: return true;
230: }
231:
232: /**
233: * Closes this window and disposes its shell.
234: */
235: private boolean handleClose() {
236:
237: if (hideViewsOnClose) {
238: List views = new ArrayList();
239: collectViewPanes(views, getChildren());
240: Iterator itr = views.iterator();
241: while (itr.hasNext()) {
242: ViewPane child = (ViewPane) itr.next();
243:
244: // Only close if closable...
245: if (child.isCloseable()) {
246: page.hideView(child.getViewReference());
247: } else {
248: page.attachView(child.getViewReference());
249: }
250: }
251: }
252:
253: if (folder != null) {
254: folder.dispose();
255: }
256:
257: if (windowShell != null) {
258: windowShell.removeListener(SWT.Resize, resizeListener);
259: DragUtil.removeDragTarget(windowShell, this );
260: bounds = windowShell.getBounds();
261:
262: // Unregister this detached view as a window (for key bindings).
263: final IContextService contextService = (IContextService) getWorkbenchPage()
264: .getWorkbenchWindow().getWorkbench().getService(
265: IContextService.class);
266: contextService.unregisterShell(windowShell);
267:
268: windowShell.setData(null);
269: windowShell = null;
270: }
271:
272: return true;
273: }
274:
275: /* (non-Javadoc)
276: * @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)
277: */
278: public IDropTarget drag(Control currentControl,
279: Object draggedObject, Point position,
280: Rectangle dragRectangle) {
281:
282: if (!(draggedObject instanceof PartPane)) {
283: return null;
284: }
285:
286: final PartPane sourcePart = (PartPane) draggedObject;
287:
288: if (sourcePart.getWorkbenchWindow() != page
289: .getWorkbenchWindow()) {
290: return null;
291: }
292:
293: // Only handle the event if the source part is acceptable to the particular PartStack
294: IDropTarget target = null;
295: if (folder.allowsDrop(sourcePart)) {
296: target = folder.getDropTarget(draggedObject, position);
297:
298: if (target == null) {
299: Rectangle displayBounds = DragUtil
300: .getDisplayBounds(folder.getControl());
301: if (displayBounds.contains(position)) {
302: target = folder.createDropTarget(sourcePart,
303: new StackDropResult(displayBounds, null));
304: } else {
305: return null;
306: }
307: }
308: }
309:
310: return target;
311: }
312:
313: /**
314: * Answer a list of the view panes.
315: */
316: private void collectViewPanes(List result, LayoutPart[] parts) {
317: for (int i = 0, length = parts.length; i < length; i++) {
318: LayoutPart part = parts[i];
319: if (part instanceof ViewPane) {
320: result.add(part);
321: }
322: }
323: }
324:
325: /**
326: * This method will be called to initialize the given Shell's layout
327: */
328: protected void configureShell(Shell shell) {
329: updateTitle();
330: shell.addListener(SWT.Resize, resizeListener);
331:
332: // Register this detached view as a window (for key bindings).
333: final IContextService contextService = (IContextService) getWorkbenchPage()
334: .getWorkbenchWindow().getWorkbench().getService(
335: IContextService.class);
336: contextService
337: .registerShell(shell, IContextService.TYPE_WINDOW);
338:
339: page.getWorkbenchWindow().getWorkbench().getHelpSystem()
340: .setHelp(shell,
341: IWorkbenchHelpContextIds.DETACHED_WINDOW);
342: }
343:
344: /**
345: * Override this method to create the widget tree that is used as the window's contents.
346: */
347: protected Control createContents(Composite parent) {
348: // Create the tab folder.
349: folder.createControl(parent);
350:
351: // Reparent each view in the tab folder.
352: Vector detachedChildren = new Vector();
353: collectViewPanes(detachedChildren, getChildren());
354: Enumeration itr = detachedChildren.elements();
355: while (itr.hasMoreElements()) {
356: LayoutPart part = (LayoutPart) itr.nextElement();
357: part.reparent(parent);
358: }
359:
360: if (folder.getPresentation() instanceof TabbedStackPresentation) {
361: TabbedStackPresentation stack = (TabbedStackPresentation) folder
362: .getPresentation();
363: AbstractTabFolder tabFolder = stack.getTabFolder();
364: tabFolder.addListener(new TabFolderListener() {
365: public void handleEvent(TabFolderEvent e) {
366: switch (e.type) {
367: case TabFolderEvent.EVENT_CLOSE: {
368: updateMinimumSize();
369: break;
370: }
371: case TabFolderEvent.EVENT_PREFERRED_SIZE: {
372: updateMinimumSize();
373: break;
374: }
375: }
376: }
377: });
378: }
379:
380: // Return tab folder control.
381: return folder.getControl();
382: }
383:
384: public LayoutPart[] getChildren() {
385: return folder.getChildren();
386: }
387:
388: public WorkbenchPage getWorkbenchPage() {
389: return this .page;
390: }
391:
392: /**
393: * @see IPersistablePart
394: */
395: public void restoreState(IMemento memento) {
396: // Read the bounds.
397: Integer bigInt;
398: bigInt = memento.getInteger(IWorkbenchConstants.TAG_X);
399: int x = bigInt.intValue();
400: bigInt = memento.getInteger(IWorkbenchConstants.TAG_Y);
401: int y = bigInt.intValue();
402: bigInt = memento.getInteger(IWorkbenchConstants.TAG_WIDTH);
403: int width = bigInt.intValue();
404: bigInt = memento.getInteger(IWorkbenchConstants.TAG_HEIGHT);
405: int height = bigInt.intValue();
406: bigInt = memento.getInteger(IWorkbenchConstants.TAG_FLOAT);
407:
408: // Set the bounds.
409: bounds = new Rectangle(x, y, width, height);
410: if (getShell() != null) {
411: getShell().setBounds(bounds);
412: }
413:
414: // Create the folder.
415: IMemento childMem = memento
416: .getChild(IWorkbenchConstants.TAG_FOLDER);
417: if (childMem != null) {
418: folder.restoreState(childMem);
419: }
420: }
421:
422: /**
423: * @see IPersistablePart
424: */
425: public void saveState(IMemento memento) {
426: if (getShell() != null) {
427: bounds = getShell().getBounds();
428: }
429:
430: // Save the bounds.
431: memento.putInteger(IWorkbenchConstants.TAG_X, bounds.x);
432: memento.putInteger(IWorkbenchConstants.TAG_Y, bounds.y);
433: memento.putInteger(IWorkbenchConstants.TAG_WIDTH, bounds.width);
434: memento.putInteger(IWorkbenchConstants.TAG_HEIGHT,
435: bounds.height);
436:
437: // Save the views.
438: IMemento childMem = memento
439: .createChild(IWorkbenchConstants.TAG_FOLDER);
440: folder.saveState(childMem);
441: }
442:
443: /* (non-Javadoc)
444: * @see org.eclipse.ui.internal.IWorkbenchDragDropPart#getControl()
445: */
446: public Control getControl() {
447: return folder.getControl();
448: }
449:
450: /**
451: * Opens the detached window.
452: */
453: public int open() {
454:
455: if (getShell() == null) {
456: create();
457: }
458:
459: Rectangle bounds = getShell().getBounds();
460: getShell().setVisible(true);
461:
462: if (!bounds.equals(getShell().getBounds())) {
463: getShell().setBounds(bounds);
464: }
465:
466: return Window.OK;
467: }
468:
469: }
|