| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.ui.internal; |
| |
| import org.eclipse.jface.action.ContributionItem; |
| import org.eclipse.jface.action.IContributionItem; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.util.Geometry; |
| 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.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.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Sash; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.ui.internal.dnd.DragUtil; |
| import org.eclipse.ui.internal.presentations.SystemMenuFastView; |
| import org.eclipse.ui.internal.presentations.SystemMenuFastViewOrientation; |
| import org.eclipse.ui.internal.presentations.SystemMenuSizeFastView; |
| import org.eclipse.ui.internal.presentations.UpdatingActionContributionItem; |
| import org.eclipse.ui.presentations.AbstractPresentationFactory; |
| import org.eclipse.ui.presentations.IPresentablePart; |
| import org.eclipse.ui.presentations.IStackPresentationSite; |
| import org.eclipse.ui.presentations.StackPresentation; |
| |
| /** |
| * Handles the presentation of an active fastview. A fast view pane docks to one side of a |
| * parent composite, and is capable of displaying a single view. The view may be resized. |
| * Displaying a new view will hide any view currently being displayed in the pane. |
| * |
| * Currently, the fast view pane does not own or contain the view. It only controls the view's |
| * position and visibility. |
| * |
| * @see org.ecliplse.ui.internal.FastViewBar |
| */ |
| public class FastViewPane { |
| private int side = SWT.LEFT; |
| |
| private ViewPane currentPane; |
| private Composite clientComposite; |
| private static final int SASH_SIZE = 3; |
| private static final int MIN_FASTVIEW_SIZE = 10; |
| private int size; |
| private Sash sash; |
| |
| // Traverse listener -- listens to ESC and closes the active fastview |
| private Listener escapeListener = new Listener() { |
| public void handleEvent(Event event) { |
| if (event.character == SWT.ESC) { |
| if (currentPane != null) { |
| currentPane.getPage().hideFastView(); |
| } |
| } |
| } |
| }; |
| |
| // Counts how many times we've scheduled a redraw... use this to avoid resizing |
| // the widgetry when we're getting resize requests faster than we can process them. |
| // This is needed for GTK, which resizes slowly (bug 54517) |
| private int redrawCounter = 0; |
| |
| private DefaultStackPresentationSite site = new DefaultStackPresentationSite() { |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.internal.skins.IPresentationSite#setState(int) |
| */ |
| public void setState(int newState) { |
| super.setState(newState); |
| ViewPane pane = currentPane; |
| switch(newState) { |
| case IStackPresentationSite.STATE_MINIMIZED: |
| currentPane.getPage().hideFastView(); |
| break; |
| case IStackPresentationSite.STATE_MAXIMIZED: |
| pane.setZoomed(true); |
| sash.setVisible(false); |
| getPresentation().setBounds(getBounds()); |
| break; |
| case IStackPresentationSite.STATE_RESTORED: |
| pane.setZoomed(false); |
| sash.setVisible(true); |
| getPresentation().setBounds(getBounds()); |
| break; |
| default: |
| } |
| } |
| |
| public void close(IPresentablePart part) { |
| if (!isCloseable(part)) { |
| return; |
| } |
| currentPane.getPage().hideView(currentPane.getViewReference()); |
| } |
| |
| public void close(IPresentablePart[] parts) { |
| for (int idx = 0; idx < parts.length; idx++) { |
| close(parts[idx]); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.internal.skins.IPresentationSite#dragStart(org.eclipse.ui.internal.skins.IPresentablePart, boolean) |
| */ |
| public void dragStart(IPresentablePart beingDragged, Point initialPosition, boolean keyboard) { |
| dragStart(initialPosition, keyboard); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.ui.internal.skins.IPresentationSite#dragStart(boolean) |
| */ |
| public void dragStart(Point initialPosition, boolean keyboard) { |
| ViewPane pane = currentPane; |
| |
| Control control = getPresentation().getControl(); |
| |
| Rectangle bounds = Geometry.toDisplay(clientComposite, |
| control.getBounds()); |
| |
| WorkbenchPage page = currentPane.getPage(); |
| |
| Perspective persp = page.getActivePerspective(); |
| |
| page.hideFastView(); |
| if (page.isZoomed()) { |
| page.zoomOut(); |
| } |
| |
| DragUtil.performDrag(pane, bounds, |
| initialPosition, !keyboard); |
| } |
| |
| public IPresentablePart getSelectedPart() { |
| if (currentPane == null) { |
| return null; |
| } |
| return currentPane.getPresentablePart(); |
| } |
| |
| public void addSystemActions(IMenuManager menuManager) { |
| appendToGroupIfPossible(menuManager, "misc", new SystemMenuFastViewOrientation(currentPane)); //$NON-NLS-1$ |
| appendToGroupIfPossible(menuManager, "misc", new UpdatingActionContributionItem(fastViewAction)); //$NON-NLS-1$ |
| appendToGroupIfPossible(menuManager, "size", new SystemMenuSizeFastView(FastViewPane.this)); //$NON-NLS-1$ |
| } |
| |
| public boolean isPartMoveable(IPresentablePart toMove) { |
| return true; |
| } |
| |
| public boolean isStackMoveable() { |
| return true; |
| } |
| |
| }; |
| |
| private SystemMenuFastView fastViewAction = new SystemMenuFastView(site); |
| |
| private static void appendToGroupIfPossible(IMenuManager m, String groupId, ContributionItem item) { |
| try { |
| m.appendToGroup(groupId, item); |
| } catch (IllegalArgumentException e) { |
| m.add(item); |
| } |
| } |
| |
| private Listener mouseDownListener = new Listener() { |
| public void handleEvent(Event event) { |
| if (event.widget instanceof Control) { |
| Control control = (Control)event.widget; |
| |
| if (control.getShell() != clientComposite.getShell()) { |
| return; |
| } |
| |
| if (event.widget instanceof ToolBar) { |
| // Ignore mouse down on actual tool bar buttons |
| Point pt = new Point(event.x, event.y); |
| ToolBar toolBar = (ToolBar) event.widget; |
| if (toolBar.getItem(pt) != null) |
| return; |
| } |
| |
| Point loc = DragUtil.getEventLoc(event); |
| |
| Rectangle bounds = DragUtil.getDisplayBounds(clientComposite); |
| if (site.getState() != IStackPresentationSite.STATE_MAXIMIZED) { |
| bounds = Geometry.getExtrudedEdge(bounds, size + SASH_SIZE, side); |
| } |
| |
| if (!bounds.contains(loc)) { |
| site.setState(IStackPresentationSite.STATE_MINIMIZED); |
| } |
| } |
| } |
| }; |
| |
| private IContributionItem systemMenuContribution; |
| |
| public void moveSash() { |
| final KeyListener listener = new KeyAdapter() { |
| public void keyPressed(KeyEvent e) { |
| if (e.character == SWT.ESC || e.character == '\r') { |
| currentPane.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(); |
| } |
| |
| private Listener resizeListener = new Listener() { |
| public void handleEvent(Event event) { |
| if (event.type == SWT.Resize && currentPane != null) { |
| setSize(size); |
| } |
| } |
| }; |
| |
| private SelectionAdapter selectionListener = new SelectionAdapter () { |
| public void widgetSelected(SelectionEvent e) { |
| |
| if (currentPane != null) { |
| Rectangle bounds = clientComposite.getClientArea(); |
| Point location = new Point(e.x, e.y); |
| int distanceFromEdge = Geometry.getDistanceFromEdge(bounds, location, side); |
| if (distanceFromEdge < MIN_FASTVIEW_SIZE) { |
| distanceFromEdge = MIN_FASTVIEW_SIZE; |
| } |
| |
| if (!(side == SWT.TOP || side == SWT.LEFT)) { |
| distanceFromEdge -= SASH_SIZE; |
| } |
| |
| setSize(distanceFromEdge); |
| |
| if (e.detail != SWT.DRAG) { |
| updateFastViewSashBounds(); |
| // getPresentation().getControl().moveAbove(null); |
| // currentPane.moveAbove(null); |
| // sash.moveAbove(null); |
| //currentPane.getControl().redraw(); |
| sash.redraw(); |
| } |
| } |
| } |
| }; |
| |
| private void setSize(int size) { |
| this.size = size; |
| |
| // Do the rest of this method inside an asyncExec. This allows the method |
| // to return quickly (without resizing). This way, if we recieve a lot of |
| // resize requests in a row, we only need to process the last one. |
| // This is needed for GTK, which resizes slowly (bug 54517) |
| redrawCounter++; |
| getPresentation().getControl().getDisplay().asyncExec(new Runnable() { |
| public void run() { |
| --redrawCounter; |
| StackPresentation presentation = getPresentation(); |
| if (presentation == null || presentation.getControl().isDisposed()) { |
| return; |
| } |
| if (redrawCounter == 0) { |
| getPresentation().setBounds(getBounds()); |
| |
| updateFastViewSashBounds(); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Returns the current fastview size ratio. Returns 0.0 if there is no fastview visible. |
| * |
| * @return |
| */ |
| public float getCurrentRatio() { |
| if (currentPane == null) { |
| return 0.0f; |
| } |
| |
| boolean isVertical = !Geometry.isHorizontal(side); |
| Rectangle clientArea = clientComposite.getClientArea(); |
| |
| int clientSize = Geometry.getDimension(clientArea, isVertical); |
| |
| return (float)size / (float)clientSize; |
| } |
| |
| private Rectangle getClientArea() { |
| return clientComposite.getClientArea(); |
| } |
| |
| private Rectangle getBounds() { |
| Rectangle bounds = getClientArea(); |
| |
| if (site.getState() == IStackPresentationSite.STATE_MAXIMIZED) { |
| return bounds; |
| } |
| |
| boolean horizontal = Geometry.isHorizontal(side); |
| |
| int available = Geometry.getDimension(bounds, !horizontal); |
| |
| return Geometry.getExtrudedEdge(bounds, Math.min(FastViewPane.this.size, available), side); |
| } |
| |
| /** |
| * Displays the given view as a fastview. The view will be docked to the edge of the |
| * given composite until it is subsequently hidden by a call to hideFastView. |
| * |
| * @param newClientComposite |
| * @param pane |
| * @param newSide |
| */ |
| public void showView(Composite newClientComposite, ViewPane pane, int newSide, float sizeRatio) { |
| side = newSide; |
| |
| if (currentPane != null) { |
| hideView(); |
| } |
| |
| currentPane = pane; |
| |
| fastViewAction.setPane(currentPane); |
| clientComposite = newClientComposite; |
| |
| clientComposite.addListener(SWT.Resize, resizeListener); |
| |
| // Create the control first |
| Control ctrl = pane.getControl(); |
| if (ctrl == null) { |
| pane.createControl(clientComposite); |
| ctrl = pane.getControl(); |
| } |
| |
| ctrl.addListener(SWT.Traverse, escapeListener); |
| |
| // Temporarily use the same appearance as docked views .. eventually, fastviews will |
| // be independently pluggable. |
| AbstractPresentationFactory factory = ((WorkbenchWindow) pane.getWorkbenchWindow()) |
| .getWindowConfigurer().getPresentationFactory(); |
| StackPresentation presentation = factory.createViewPresentation( |
| newClientComposite, site); |
| |
| site.setPresentation(presentation); |
| site.setPresentationState(IStackPresentationSite.STATE_RESTORED); |
| presentation.addPart(pane.getPresentablePart(), null); |
| presentation.selectPart(pane.getPresentablePart()); |
| presentation.setActive(StackPresentation.AS_ACTIVE_FOCUS); |
| presentation.setVisible(true); |
| |
| // Show pane fast. |
| ctrl.setEnabled(true); // Add focus support. |
| Composite parent = ctrl.getParent(); |
| |
| pane.setVisible(true); |
| pane.setFocus(); |
| |
| boolean horizontal = Geometry.isHorizontal(side); |
| sash = new Sash(parent, Geometry.getSwtHorizontalOrVerticalConstant(horizontal)); |
| |
| sash.addSelectionListener(selectionListener); |
| |
| Rectangle clientArea = newClientComposite.getClientArea(); |
| |
| getPresentation().getControl().moveAbove(null); |
| currentPane.moveAbove(null); |
| sash.moveAbove(null); |
| |
| setSize((int)(Geometry.getDimension(clientArea, !horizontal) * sizeRatio)); |
| |
| Display display = sash.getDisplay(); |
| |
| display.addFilter(SWT.MouseDown, mouseDownListener); |
| } |
| |
| /** |
| * Updates the position of the resize sash. |
| * |
| * @param bounds |
| */ |
| private void updateFastViewSashBounds() { |
| Rectangle bounds = getBounds(); |
| |
| int oppositeSide = Geometry.getOppositeSide(side); |
| Rectangle newBounds = Geometry.getExtrudedEdge(bounds, -SASH_SIZE, oppositeSide); |
| |
| Rectangle oldBounds = sash.getBounds(); |
| |
| if (!newBounds.equals(oldBounds)) { |
| sash.setBounds(newBounds); |
| } |
| } |
| |
| /** |
| * Disposes of any active widgetry being used for the fast view pane. Does not dispose |
| * of the view itself. |
| */ |
| public void dispose() { |
| hideView(); |
| } |
| |
| /** |
| * Returns the bounding rectangle for the currently visible fastview, given the rectangle |
| * in which the fastview can dock. |
| * |
| * @param clientArea |
| * @param ratio |
| * @param orientation |
| * @return |
| */ |
| private Rectangle getFastViewBounds() { |
| Rectangle clientArea = clientComposite.getClientArea(); |
| |
| boolean isVertical = !Geometry.isHorizontal(side); |
| int clientSize = Geometry.getDimension(clientArea, isVertical); |
| int viewSize = Math.min(Geometry.getDimension(getBounds(), isVertical), |
| clientSize - MIN_FASTVIEW_SIZE); |
| |
| return Geometry.getExtrudedEdge(clientArea, viewSize, side); |
| } |
| |
| /** |
| * @return |
| */ |
| private StackPresentation getPresentation() { |
| return site.getPresentation(); |
| } |
| |
| /** |
| * Hides the sash for the fastview if it is currently visible. This method may not be |
| * required anymore, and might be removed from the public interface. |
| */ |
| public void hideFastViewSash() { |
| if (sash != null) { |
| sash.setVisible(false); |
| } |
| } |
| |
| /** |
| * Hides the currently visible fastview. |
| */ |
| public void hideView() { |
| |
| if (clientComposite != null) { |
| Display display = clientComposite.getDisplay(); |
| |
| display.removeFilter(SWT.MouseDown, mouseDownListener); |
| } |
| |
| if (currentPane == null) { |
| return; |
| } |
| |
| //unzoom before hiding |
| currentPane.setZoomed(false); |
| |
| if (sash != null) { |
| sash.dispose(); |
| sash = null; |
| } |
| |
| clientComposite.removeListener(SWT.Resize, resizeListener); |
| |
| // Get pane. |
| // Hide the right side sash first |
| //hideFastViewSash(); |
| Control ctrl = currentPane.getControl(); |
| |
| ctrl.removeListener(SWT.Traverse, escapeListener); |
| |
| // Hide it completely. |
| getPresentation().setVisible(false); |
| site.dispose(); |
| //currentPane.setFastViewSash(null); |
| ctrl.setEnabled(false); // Remove focus support. |
| |
| currentPane = null; |
| } |
| |
| /** |
| * @return Returns the currently visible fastview or null if none |
| */ |
| public ViewPane getCurrentPane() { |
| return currentPane; |
| } |
| |
| /** |
| * Zooms or unzooms the fast view pane. |
| * |
| */ |
| public void toggleZoom() { |
| if (site.getState() == IStackPresentationSite.STATE_MAXIMIZED) { |
| site.setState(IStackPresentationSite.STATE_RESTORED); |
| } else { |
| site.setState(IStackPresentationSite.STATE_MAXIMIZED); |
| } |
| } |
| |
| } |