| /******************************************************************************* |
| * Copyright (c) 2000, 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; Chris Torrence, ITT Visual Information Solutions - bug 51580 |
| *******************************************************************************/ |
| package org.eclipse.ui.internal; |
| |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.PropertyChangeEvent; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.FocusAdapter; |
| import org.eclipse.swt.events.FocusEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.KeyListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.events.TraverseEvent; |
| import org.eclipse.swt.events.TraverseListener; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.FillLayout; |
| 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.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Sash; |
| import org.eclipse.ui.IPropertyListener; |
| import org.eclipse.ui.IWorkbenchPart; |
| import org.eclipse.ui.IWorkbenchPartReference; |
| import org.eclipse.ui.internal.dnd.SwtUtil; |
| import org.eclipse.ui.part.MultiEditor; |
| import org.eclipse.ui.presentations.IPresentablePart; |
| |
| /** |
| * Provides the common behavior for both views |
| * and editor panes. |
| * |
| * TODO: Delete ViewPane and EditorPane, and make this class non-abstract. |
| * |
| * TODO: Stop subclassing LayoutPart. This class cannot be interchanged with other LayoutParts. |
| * Pointers that refer to PartPane instances should do so directly rather than referring to |
| * LayoutPart and downcasting. The getPresentablePart() method only applies to PartPanes, and |
| * should be removed from LayoutPart. |
| */ |
| public abstract class PartPane extends LayoutPart implements IPropertyListener, |
| Listener, IPropertyChangeListener { |
| |
| public static final String PROP_ZOOMED = "zoomed"; //$NON-NLS-1$ |
| |
| private boolean isZoomed = false; |
| |
| private MenuManager paneMenuManager; |
| private ListenerList listeners = new ListenerList(); |
| private ListenerList partListeners = new ListenerList(); |
| |
| protected IWorkbenchPartReference partReference; |
| |
| protected WorkbenchPage page; |
| |
| protected Composite control; |
| |
| private boolean inLayout = true; |
| |
| private TraverseListener traverseListener = new TraverseListener() { |
| /* (non-Javadoc) |
| * @see org.eclipse.swt.events.TraverseListener#keyTraversed(org.eclipse.swt.events.TraverseEvent) |
| */ |
| public void keyTraversed(TraverseEvent e) { |
| // Hack: Currently, SWT sets focus whenever we call Control.traverse. This doesn't |
| // cause too much of a problem for ctrl-pgup and ctrl-pgdn, but it is seriously unexpected |
| // for other traversal events. When (and if) it becomes possible to call traverse() without |
| // forcing a focus change, this if statement should be removed and ALL events should be |
| // forwarded to the container. |
| if (e.detail == SWT.TRAVERSE_PAGE_NEXT |
| || e.detail == SWT.TRAVERSE_PAGE_PREVIOUS) { |
| ILayoutContainer container = getContainer(); |
| if (container != null && container instanceof LayoutPart) { |
| LayoutPart parent = (LayoutPart) container; |
| Control parentControl = parent.getControl(); |
| if (parentControl != null && !parentControl.isDisposed()) { |
| e.doit = parentControl.traverse(e.detail); |
| if (e.doit) { |
| e.detail = SWT.TRAVERSE_NONE; |
| } |
| } |
| } |
| } |
| } |
| |
| }; |
| |
| private boolean busy; |
| |
| public static class Sashes { |
| public Sash left; |
| |
| public Sash right; |
| |
| public Sash top; |
| |
| public Sash bottom; |
| } |
| |
| /** |
| * Construct a pane for a part. |
| */ |
| public PartPane(IWorkbenchPartReference partReference, |
| WorkbenchPage workbenchPage) { |
| super(partReference.getId()); |
| this.partReference = partReference; |
| this.page = workbenchPage; |
| } |
| |
| public void addSizeMenuItem(Menu menu, int index) { |
| //Add size menu |
| MenuItem item = new MenuItem(menu, SWT.CASCADE, index); |
| item.setText(WorkbenchMessages.PartPane_size); |
| Menu sizeMenu = new Menu(menu); |
| item.setMenu(sizeMenu); |
| addSizeItems(sizeMenu); |
| } |
| |
| /** |
| * |
| */ |
| public void createControl(Composite parent) { |
| if (getControl() != null) { |
| return; |
| } |
| |
| partReference.addPropertyListener(this); |
| partReference.addPartPropertyListener(this); |
| // Create view form. |
| control = new Composite(parent, SWT.NONE); |
| control.setLayout(new FillLayout()); |
| // the part should never be visible by default. It will be made visible |
| // by activation. This allows us to have views appear in tabs without |
| // becoming active by default. |
| control.setVisible(false); |
| control.moveAbove(null); |
| // Create a title bar. |
| createTitleBar(); |
| |
| |
| // When the pane or any child gains focus, notify the workbench. |
| control.addListener(SWT.Activate, this); |
| |
| control.addTraverseListener(traverseListener); |
| } |
| |
| /** |
| * Create a title bar for the pane if required. |
| */ |
| protected abstract void createTitleBar(); |
| |
| /** |
| * @private |
| */ |
| public void dispose() { |
| super.dispose(); |
| |
| if ((control != null) && (!control.isDisposed())) { |
| control.removeListener(SWT.Activate, this); |
| control.removeTraverseListener(traverseListener); |
| control.dispose(); |
| control = null; |
| } |
| if ((paneMenuManager != null)) { |
| paneMenuManager.dispose(); |
| paneMenuManager = null; |
| } |
| |
| partReference.removePropertyListener(this); |
| partReference.removePartPropertyListener(this); |
| } |
| |
| /** |
| * User has requested to close the pane. |
| * Take appropriate action depending on type. |
| */ |
| abstract public void doHide(); |
| |
| /** |
| * Zooms in on the part contained in this pane. |
| */ |
| protected void doZoom() { |
| if (isDocked()) { |
| page.toggleZoom(partReference); |
| } |
| } |
| |
| /** |
| * Gets the presentation bounds. |
| */ |
| public Rectangle getBounds() { |
| return getControl().getBounds(); |
| } |
| |
| /** |
| * Get the control. |
| */ |
| public Control getControl() { |
| return control; |
| } |
| |
| /** |
| * Answer the part child. |
| */ |
| public IWorkbenchPartReference getPartReference() { |
| return partReference; |
| } |
| |
| /** |
| * @see Listener |
| */ |
| public void handleEvent(Event event) { |
| if (event.type == SWT.Activate) { |
| if (inLayout) { |
| requestActivation(); |
| } |
| } |
| } |
| |
| /** |
| * Return whether the pane is zoomed or not |
| */ |
| public boolean isZoomed() { |
| return isZoomed; |
| } |
| |
| /** |
| * Move the control over another one. |
| */ |
| public void moveAbove(Control refControl) { |
| if (getControl() != null) { |
| getControl().moveAbove(refControl); |
| } |
| } |
| |
| /** |
| * Notify the workbook page that the part pane has |
| * been activated by the user. |
| */ |
| public void requestActivation() { |
| IWorkbenchPart part = partReference.getPart(true); |
| // Cannot activate the outer bit of a MultiEditor. In previous versions of the |
| // workbench, MultiEditors had their own implementation of EditorPane for the purpose |
| // of overriding requestActivation with a NOP... however, keeping the old pattern would |
| // mean it is necessary to eagerly activate an editor's plugin in order to determine |
| // what type of pane to create. |
| if (part instanceof MultiEditor) { |
| return; |
| } |
| |
| this.page.requestActivation(part); |
| } |
| |
| /** |
| * Sets the parent for this part. |
| */ |
| public void setContainer(ILayoutContainer container) { |
| |
| if (container instanceof LayoutPart) { |
| LayoutPart part = (LayoutPart) container; |
| |
| Control containerControl = part.getControl(); |
| |
| if (containerControl != null) { |
| Control control = getControl(); |
| Composite newParent = containerControl.getParent(); |
| if (control != null && newParent != control.getParent()) { |
| reparent(newParent); |
| } |
| } |
| } |
| super.setContainer(container); |
| } |
| |
| /** |
| * Shows the receiver if <code>visible</code> is true otherwise hide it. |
| */ |
| public void setVisible(boolean makeVisible) { |
| // Avoid redundant visibility changes |
| if (makeVisible == getVisible()) { |
| return; |
| } |
| |
| if (makeVisible) { |
| partReference.getPart(true); |
| } |
| |
| super.setVisible(makeVisible); |
| |
| ((WorkbenchPartReference) partReference).fireVisibilityChange(); |
| } |
| |
| /** |
| * Sets focus to this part. |
| */ |
| public void setFocus() { |
| requestActivation(); |
| |
| IWorkbenchPart part = partReference.getPart(true); |
| if (part != null) { |
| Control control = getControl(); |
| if (!SwtUtil.isFocusAncestor(control)) { |
| // First try to call part.setFocus |
| part.setFocus(); |
| } |
| } |
| } |
| |
| /** |
| * Sets the workbench page of the view. |
| */ |
| public void setWorkbenchPage(WorkbenchPage workbenchPage) { |
| this.page = workbenchPage; |
| } |
| |
| /** |
| * Set whether the pane is zoomed or not |
| */ |
| public void setZoomed(boolean isZoomed) { |
| if (this.isZoomed == isZoomed) { |
| return; // do nothing if we're already in the right state. |
| } |
| |
| super.setZoomed(isZoomed); |
| |
| this.isZoomed = isZoomed; |
| |
| ((WorkbenchPartReference) partReference).fireZoomChange(); |
| } |
| |
| /** |
| * Informs the pane that it's window shell has |
| * been activated. |
| */ |
| /* package */abstract void shellActivated(); |
| |
| /** |
| * Informs the pane that it's window shell has |
| * been deactivated. |
| */ |
| /* package */abstract void shellDeactivated(); |
| |
| /** |
| * Indicate focus in part. |
| */ |
| public abstract void showFocus(boolean inFocus); |
| |
| /** |
| * @see IPartDropTarget::targetPartFor |
| */ |
| public LayoutPart targetPartFor(LayoutPart dragSource) { |
| return this; |
| } |
| |
| /** |
| * Returns the PartStack that contains this PartPane, or null if none. |
| * |
| * @return |
| */ |
| public PartStack getStack() { |
| ILayoutContainer container = getContainer(); |
| if (container instanceof PartStack) { |
| return (PartStack) container; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Show a title label menu for this pane. |
| */ |
| public void showPaneMenu() { |
| PartStack folder = getStack(); |
| |
| if (folder != null) { |
| folder.showPaneMenu(); |
| } |
| } |
| |
| /** |
| * Show the context menu for this part. |
| */ |
| public void showSystemMenu() { |
| PartStack folder = getStack(); |
| |
| if (folder != null) { |
| folder.showSystemMenu(); |
| } |
| } |
| |
| /** |
| * Finds and return the sashes around this part. |
| */ |
| protected Sashes findSashes() { |
| Sashes result = new Sashes(); |
| |
| ILayoutContainer container = getContainer(); |
| |
| if (container == null) { |
| return result; |
| } |
| |
| container.findSashes(this, result); |
| return result; |
| } |
| |
| /** |
| * Enable the user to resize this part using |
| * the keyboard to move the specified sash |
| */ |
| protected void moveSash(final Sash sash) { |
| moveSash(sash, this); |
| } |
| |
| public static void moveSash(final Sash sash, |
| final LayoutPart toGetFocusWhenDone) { |
| final KeyListener listener = new KeyAdapter() { |
| public void keyPressed(KeyEvent e) { |
| if (e.character == SWT.ESC || e.character == '\r') { |
| if (toGetFocusWhenDone != null) { |
| toGetFocusWhenDone.setFocus(); |
| } |
| } |
| } |
| }; |
| sash.addFocusListener(new FocusAdapter() { |
| public void focusGained(FocusEvent e) { |
| sash.setBackground(sash.getDisplay().getSystemColor( |
| SWT.COLOR_LIST_SELECTION)); |
| sash.addKeyListener(listener); |
| } |
| |
| public void focusLost(FocusEvent e) { |
| sash.setBackground(null); |
| sash.removeKeyListener(listener); |
| } |
| }); |
| sash.setFocus(); |
| |
| } |
| |
| /** |
| * Add a menu item to the Size Menu |
| */ |
| protected void addSizeItem(Menu sizeMenu, String labelMessage, |
| final Sash sash) { |
| MenuItem item = new MenuItem(sizeMenu, SWT.NONE); |
| item.setText(labelMessage); |
| item.addSelectionListener(new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| moveSash(sash); |
| } |
| }); |
| item.setEnabled(!isZoomed() && sash != null); |
| } |
| |
| /** |
| * Returns the workbench page of this pane. |
| */ |
| public WorkbenchPage getPage() { |
| return page; |
| } |
| |
| /** |
| * Add the Left,Right,Up,Botton menu items to the Size menu. |
| */ |
| protected void addSizeItems(Menu sizeMenu) { |
| Sashes sashes = findSashes(); |
| addSizeItem(sizeMenu, |
| WorkbenchMessages.PartPane_sizeLeft, sashes.left); |
| addSizeItem(sizeMenu, |
| WorkbenchMessages.PartPane_sizeRight, sashes.right); |
| addSizeItem(sizeMenu, |
| WorkbenchMessages.PartPane_sizeTop, sashes.top); |
| addSizeItem(sizeMenu, WorkbenchMessages.PartPane_sizeBottom, sashes.bottom); |
| } |
| |
| /** |
| * Pin this part. |
| */ |
| protected void doDock() { |
| // do nothing |
| } |
| |
| /** |
| * Set the busy state of the pane. |
| */ |
| public void setBusy(boolean isBusy) { |
| if (isBusy != busy) { |
| busy = isBusy; |
| firePropertyChange(IPresentablePart.PROP_BUSY); |
| } |
| } |
| |
| /** |
| * Show a highlight for the receiver if it is |
| * not currently the part in the front of its |
| * presentation. |
| * |
| */ |
| public void showHighlight() { |
| //No nothing by default |
| } |
| |
| /** |
| * @return |
| */ |
| public abstract Control getToolBar(); |
| |
| /** |
| * @return |
| */ |
| public boolean hasViewMenu() { |
| return false; |
| } |
| |
| /** |
| * @param location |
| */ |
| public void showViewMenu(Point location) { |
| |
| } |
| |
| public boolean isBusy() { |
| return busy; |
| } |
| |
| /** |
| * Writes a description of the layout to the given string buffer. |
| * This is used for drag-drop test suites to determine if two layouts are the |
| * same. Like a hash code, the description should compare as equal iff the |
| * layouts are the same. However, it should be user-readable in order to |
| * help debug failed tests. Although these are english readable strings, |
| * they do not need to be translated. |
| * |
| * @param buf |
| */ |
| public void describeLayout(StringBuffer buf) { |
| |
| IWorkbenchPartReference part = getPartReference(); |
| |
| if (part != null) { |
| buf.append(part.getPartName()); |
| return; |
| } |
| } |
| |
| /** |
| * @return |
| * @since 3.1 |
| */ |
| public abstract boolean isCloseable(); |
| |
| public void setInLayout(boolean inLayout) { |
| this.inLayout = inLayout; |
| } |
| |
| public boolean getInLayout() { |
| return inLayout; |
| } |
| |
| public boolean allowsAutoFocus() { |
| if (!inLayout) { |
| return false; |
| } |
| |
| return super.allowsAutoFocus(); |
| } |
| |
| /** |
| * Clears all contribution items from the contribution managers (this is done separately |
| * from dispose() since it is done after the part is disposed). This is a bit of a hack. |
| * Really, the contribution managers should be part of the site, not the PartPane. If these |
| * were moved elsewhere, then disposal of the PartPane would be atomic and this method could |
| * be removed. |
| */ |
| public void removeContributions() { |
| |
| } |
| |
| public void addPropertyListener(IPropertyListener listener) { |
| listeners.add(listener); |
| } |
| |
| public void removePropertyListener(IPropertyListener listener) { |
| listeners.remove(listener); |
| } |
| |
| public void firePropertyChange(int propertyId) { |
| Object listeners[] = this.listeners.getListeners(); |
| for (int i = 0; i < listeners.length; i++) { |
| ((IPropertyListener) listeners[i]).propertyChanged(this, propertyId); |
| } |
| } |
| |
| public void propertyChanged(Object source, int propId) { |
| firePropertyChange(propId); |
| } |
| |
| public void addPartPropertyListener(IPropertyChangeListener listener) { |
| partListeners.add(listener); |
| } |
| |
| public void removePartPropertyListener(IPropertyChangeListener listener) { |
| partListeners.remove(listener); |
| } |
| |
| public void firePartPropertyChange(PropertyChangeEvent event) { |
| Object[] l = partListeners.getListeners(); |
| for (int i = 0; i < l.length; i++) { |
| ((IPropertyChangeListener)l[i]).propertyChange(event); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) |
| */ |
| public void propertyChange(PropertyChangeEvent event) { |
| firePartPropertyChange(event); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.internal.LayoutPart#computePreferredSize(boolean, int, int, int) |
| */ |
| public int computePreferredSize(boolean width, int availableParallel, |
| int availablePerpendicular, int preferredParallel) { |
| |
| return ((WorkbenchPartReference)partReference).computePreferredSize(width, |
| availableParallel, availablePerpendicular, preferredParallel); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.internal.LayoutPart#getSizeFlags(boolean) |
| */ |
| public int getSizeFlags(boolean horizontal) { |
| return ((WorkbenchPartReference)partReference).getSizeFlags(horizontal); |
| } |
| |
| } |