summaryrefslogtreecommitdiff
path: root/propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2012-04-19 13:32:08 -0700
committerTor Norbye <tnorbye@google.com>2012-04-19 13:32:08 -0700
commit4b1a9d7baa71df312585022c22e8025fafaba95a (patch)
treef456b9a3b7e64bee9a8f9980f58c641aa6dc3a7e /propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java
parent710a161c9c528b5e5cb5cd6d0d41736881c9697f (diff)
downloadeclipse-windowbuilder-4b1a9d7baa71df312585022c22e8025fafaba95a.tar.gz
Add support for window docking in the property sheet code
This checkin migrates more of the WindowBuilder code into the property sheet library; in particular, it adds the window docking code for use with the property sheet in ADT. There are also some modifications to the code to support ADT use cases, documented in the updated README. Change-Id: I38e0e2137fdcc179aa6f8927d201ff002c831617
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java')
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java1007
1 files changed, 1007 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java b/propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java
new file mode 100644
index 0000000..23d2f83
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/flyout/FlyoutControlComposite.java
@@ -0,0 +1,1007 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.core.controls.flyout;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.events.MouseTrackAdapter;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+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.Sash;
+import org.eclipse.swt.widgets.Tracker;
+import org.eclipse.wb.core.controls.Messages;
+import org.eclipse.wb.draw2d.IColorConstants;
+import org.eclipse.wb.draw2d.ICursorConstants;
+import org.eclipse.wb.internal.core.utils.ui.DrawUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link FlyoutControlComposite} is container for two {@link Control}'s. One (client control) is
+ * used to fill client area. Second (flyout control) can be docked to any enabled position or
+ * temporary hidden.
+ *
+ * @author scheglov_ke
+ * @coverage core.control
+ */
+public final class FlyoutControlComposite extends Composite {
+ private static final int RESIZE_WIDTH = 5;
+ private static final int TITLE_LINES = 30;
+ private static final int TITLE_MARGIN = 5;
+ private static final Font TITLE_FONT = JFaceResources.getFontRegistry().getBold(
+ JFaceResources.DEFAULT_FONT);
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Images
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private static final Image PIN = loadImage("icons/pin.gif");
+ private static final Image ARROW_LEFT = loadImage("icons/arrow_left.gif");
+ private static final Image ARROW_RIGHT = loadImage("icons/arrow_right.gif");
+ private static final Image ARROW_TOP = loadImage("icons/arrow_top.gif");
+ private static final Image ARROW_BOTTOM = loadImage("icons/arrow_bottom.gif");
+
+ private static Image loadImage(String path) {
+ return DrawUtils.loadImage(FlyoutControlComposite.class, path);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Instance fields
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private final IFlyoutPreferences m_preferences;
+ private final FlyoutContainer m_flyoutContainer;
+ private int m_minWidth = 150;
+ private int m_validDockLocations = -1;
+ private final List<IFlyoutMenuContributor> m_menuContributors =
+ new ArrayList<IFlyoutMenuContributor>();
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public FlyoutControlComposite(Composite parent, int style, IFlyoutPreferences preferences) {
+ super(parent, style);
+ m_preferences = preferences;
+ // add listeners
+ addListener(SWT.Resize, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ if (getShell().getMinimized()) {
+ return;
+ }
+ layout();
+ }
+ });
+ // create container for flyout control
+ m_flyoutContainer = new FlyoutContainer(this, SWT.NO_BACKGROUND);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Parents
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return the parent {@link Composite} for flyout {@link Control}.
+ */
+ public Composite getFlyoutParent() {
+ return m_flyoutContainer;
+ }
+
+ /**
+ * @return the parent {@link Composite} for client {@link Control}.
+ */
+ public Composite getClientParent() {
+ return this;
+ }
+
+ /**
+ * Sets the bit set with valid docking locations.
+ */
+ public void setValidDockLocations(int validDockLocations) {
+ m_validDockLocations = validDockLocations;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets the minimal width of flyout.
+ */
+ public void setMinWidth(int minWidth) {
+ m_minWidth = minWidth;
+ }
+
+ /**
+ * Sets the text of title.
+ */
+ public void setTitleText(String text) {
+ m_flyoutContainer.setTitleText(text);
+ }
+
+ /**
+ * Adds new {@link IFlyoutMenuContributor}.
+ */
+ public void addMenuContributor(IFlyoutMenuContributor contributor) {
+ if (!m_menuContributors.contains(contributor)) {
+ m_menuContributors.add(contributor);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Layout
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void layout() {
+ Rectangle clientArea = getClientArea();
+ int state = m_preferences.getState();
+ Control client = getChildren()[1];
+ // check, may be "clientArea" is empty, for example because CTabFolder page is not visible
+ if (clientArea.width == 0 || clientArea.height == 0) {
+ return;
+ }
+ // check, maybe flyout has no Control, so "client" should fill client area
+ if (m_flyoutContainer.getControl() == null
+ // BEGIN ADT MODIFICATIONS
+ || !m_flyoutContainer.getControl().getVisible()
+ // END ADT MODIFICATIONS
+ ) {
+ m_flyoutContainer.setBounds(0, 0, 0, 0);
+ client.setBounds(clientArea);
+ return;
+ }
+ // prepare width to display
+ int width;
+ int offset;
+ if (state == IFlyoutPreferences.STATE_OPEN) {
+ width = m_preferences.getWidth();
+ // limit maximum value
+ if (isHorizontal()) {
+ width = Math.min(clientArea.width / 2, width);
+ } else {
+ width = Math.min(clientArea.height / 2, width);
+ }
+ // limit minimum value
+ width = Math.max(width, m_minWidth);
+ width = Math.max(width, 2 * m_flyoutContainer.m_titleHeight + m_flyoutContainer.m_titleWidth);
+ // remember actual width
+ m_preferences.setWidth(width);
+ //
+ offset = width;
+ } else if (state == IFlyoutPreferences.STATE_EXPANDED) {
+ offset = m_flyoutContainer.m_titleHeight;
+ width = m_preferences.getWidth();
+ } else {
+ width = m_flyoutContainer.m_titleHeight;
+ offset = width;
+ }
+ // change bounds for flyout container and client control
+ {
+ if (isWest()) {
+ m_flyoutContainer.setBounds(0, 0, width, clientArea.height);
+ client.setBounds(offset, 0, clientArea.width - offset, clientArea.height);
+ } else if (isEast()) {
+ m_flyoutContainer.setBounds(clientArea.width - width, 0, width, clientArea.height);
+ client.setBounds(0, 0, clientArea.width - offset, clientArea.height);
+ } else if (isNorth()) {
+ m_flyoutContainer.setBounds(0, 0, clientArea.width, width);
+ client.setBounds(0, offset, clientArea.width, clientArea.height - offset);
+ } else if (isSouth()) {
+ m_flyoutContainer.setBounds(0, clientArea.height - width, clientArea.width, width);
+ client.setBounds(0, 0, clientArea.width, clientArea.height - offset);
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Internal utils
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private boolean isHorizontal() {
+ return isWest() || isEast();
+ }
+
+ private boolean isWest() {
+ return getDockLocation() == IFlyoutPreferences.DOCK_WEST;
+ }
+
+ private boolean isEast() {
+ return getDockLocation() == IFlyoutPreferences.DOCK_EAST;
+ }
+
+ private boolean isNorth() {
+ return getDockLocation() == IFlyoutPreferences.DOCK_NORTH;
+ }
+
+ private boolean isSouth() {
+ return getDockLocation() == IFlyoutPreferences.DOCK_SOUTH;
+ }
+
+ /**
+ * @return <code>true</code> if given docking location is valid.
+ */
+ private boolean isValidDockLocation(int location) {
+ return (location & m_validDockLocations) == location;
+ }
+
+ /**
+ * @return current docking location.
+ */
+ private int getDockLocation() {
+ return m_preferences.getDockLocation();
+ }
+
+ /**
+ * Sets new docking location.
+ */
+ private void setDockLocation(int dockLocation) {
+ m_preferences.setDockLocation(dockLocation);
+ layout();
+ }
+
+ // BEGIN ADT MODIFICATIONS
+ /** If the flyout hover is showing, dismiss it */
+ public void dismissHover() {
+ if (m_flyoutContainer != null) {
+ m_flyoutContainer.dismissHover();
+ }
+ }
+
+ /** Sets a listener to be modified when windows are opened, collapsed and expanded */
+ public void setListener(IFlyoutListener listener) {
+ assert m_listener == null; // Only one listener supported
+ m_listener = listener;
+ }
+ private IFlyoutListener m_listener;
+ // END ADT MODIFICATIONS
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // FlyoutContainer
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Container for flyout {@link Control}.
+ *
+ * @author scheglov_ke
+ */
+ private final class FlyoutContainer extends Composite {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Container
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public FlyoutContainer(Composite parent, int style) {
+ super(parent, style);
+ configureMenu();
+ updateTitleImage("Flyout");
+ // add listeners
+ addListener(SWT.Dispose, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ if (m_titleImage != null) {
+ m_titleImage.dispose();
+ m_titleImageRotated.dispose();
+ m_titleImage = null;
+ m_titleImageRotated = null;
+ }
+ if (m_backImage != null) {
+ m_backImage.dispose();
+ m_backImage = null;
+ }
+ }
+ });
+ {
+ Listener listener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ layout();
+ }
+ };
+ addListener(SWT.Move, listener);
+ addListener(SWT.Resize, listener);
+ }
+ addListener(SWT.Paint, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ handlePaint(event.gc);
+ }
+ });
+ // mouse listeners
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent event) {
+ if (event.button == 1) {
+ handle_mouseDown(event);
+ }
+ }
+
+ @Override
+ public void mouseUp(MouseEvent event) {
+ if (event.button == 1) {
+ handle_mouseUp(event);
+ }
+ }
+ });
+ addMouseTrackListener(new MouseTrackAdapter() {
+ @Override
+ public void mouseExit(MouseEvent e) {
+ m_stateHover = false;
+ redraw();
+ setCursor(null);
+ }
+
+ @Override
+ public void mouseHover(MouseEvent e) {
+ handle_mouseHover();
+ }
+ });
+ addMouseMoveListener(new MouseMoveListener() {
+ @Override
+ public void mouseMove(MouseEvent event) {
+ handle_mouseMove(event);
+ }
+ });
+ }
+
+ // BEGIN ADT MODIFICATIONS
+ private void dismissHover() {
+ int state = m_preferences.getState();
+ if (state == IFlyoutPreferences.STATE_EXPANDED) {
+ state = IFlyoutPreferences.STATE_COLLAPSED;
+ m_preferences.setState(state);
+ redraw();
+ FlyoutControlComposite.this.layout();
+ if (m_listener != null) {
+ m_listener.stateChanged(IFlyoutPreferences.STATE_EXPANDED, state);
+ }
+ }
+ }
+ // END END MODIFICATIONS
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Events: mouse
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private boolean m_resize;
+ private boolean m_stateHover;
+
+ /**
+ * Handler for {@link SWT#MouseDown} event.
+ */
+ private void handle_mouseDown(MouseEvent event) {
+ if (m_stateHover) {
+ int state = m_preferences.getState();
+ // BEGIN ADT MODIFICATIONS
+ int oldState = state;
+ // END ADT MODIFICATIONS
+ if (state == IFlyoutPreferences.STATE_OPEN) {
+ state = IFlyoutPreferences.STATE_COLLAPSED;
+ } else {
+ state = IFlyoutPreferences.STATE_OPEN;
+ }
+ m_preferences.setState(state);
+ redraw();
+ FlyoutControlComposite.this.layout();
+ // BEGIN ADT MODIFICATIONS
+ if (m_listener != null) {
+ m_listener.stateChanged(oldState, state);
+ }
+ // END ADT MODIFICATIONS
+ } else if (getCursor() == ICursorConstants.SIZEWE || getCursor() == ICursorConstants.SIZENS) {
+ m_resize = true;
+ } else if (getCursor() == ICursorConstants.SIZEALL) {
+ handleDocking();
+ }
+ }
+
+ /**
+ * Handler for {@link SWT#MouseUp} event.
+ */
+ private void handle_mouseUp(MouseEvent event) {
+ if (m_resize) {
+ m_resize = false;
+ handle_mouseMove(event);
+ }
+ }
+
+ /**
+ * Handler for {@link SWT#MouseMove} event.
+ */
+ private void handle_mouseMove(MouseEvent event) {
+ final FlyoutControlComposite container = FlyoutControlComposite.this;
+ if (m_resize) {
+ // prepare width
+ int width;
+ if (isHorizontal()) {
+ width = getSize().x;
+ } else {
+ width = getSize().y;
+ }
+ // prepare new width
+ int newWidth = width;
+ if (isWest()) {
+ newWidth = event.x + RESIZE_WIDTH / 2;
+ } else if (isEast()) {
+ newWidth = width - event.x + RESIZE_WIDTH / 2;
+ } else if (isNorth()) {
+ newWidth = event.y + RESIZE_WIDTH / 2;
+ } else if (isSouth()) {
+ newWidth = width - event.y + RESIZE_WIDTH / 2;
+ }
+ // update width
+ if (newWidth != width) {
+ m_preferences.setWidth(newWidth);
+ redraw();
+ container.layout();
+ }
+ } else {
+ Rectangle clientArea = getClientArea();
+ boolean inside = clientArea.contains(event.x, event.y);
+ int x = event.x;
+ int y = event.y;
+ if (inside) {
+ // check for state
+ {
+ boolean oldStateHover = m_stateHover;
+ if (isEast()) {
+ m_stateHover = x > clientArea.width - m_titleHeight && y < m_titleHeight;
+ } else {
+ m_stateHover = x < m_titleHeight && y < m_titleHeight;
+ }
+ if (m_stateHover != oldStateHover) {
+ redraw();
+ }
+ if (m_stateHover) {
+ setCursor(null);
+ return;
+ }
+ }
+ // check for resize band
+ if (isOpenExpanded()) {
+ if (isWest() && x >= clientArea.width - RESIZE_WIDTH) {
+ setCursor(ICursorConstants.SIZEWE);
+ } else if (isEast() && x <= RESIZE_WIDTH) {
+ setCursor(ICursorConstants.SIZEWE);
+ } else if (isNorth() && y >= clientArea.height - RESIZE_WIDTH) {
+ setCursor(ICursorConstants.SIZENS);
+ } else if (isSouth() && y <= RESIZE_WIDTH) {
+ setCursor(ICursorConstants.SIZENS);
+ } else {
+ setCursor(null);
+ }
+ }
+ // check for docking
+ if (getCursor() == null) {
+ setCursor(ICursorConstants.SIZEALL);
+ }
+ } else {
+ setCursor(null);
+ }
+ }
+ }
+
+ /**
+ * Handler for {@link SWT#MouseHover} event - temporary expands flyout and collapse again when
+ * mouse moves above client.
+ */
+ private void handle_mouseHover() {
+ if (m_preferences.getState() == IFlyoutPreferences.STATE_COLLAPSED && !m_stateHover) {
+ m_preferences.setState(IFlyoutPreferences.STATE_EXPANDED);
+ //
+ final FlyoutControlComposite container = FlyoutControlComposite.this;
+ container.layout();
+ // BEGIN ADT MODIFICATIONS
+ if (m_listener != null) {
+ m_listener.stateChanged(IFlyoutPreferences.STATE_COLLAPSED,
+ IFlyoutPreferences.STATE_EXPANDED);
+ }
+ // END ADT MODIFICATIONS
+ // add listeners
+ Listener listener = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ if (event.type == SWT.Dispose) {
+ getDisplay().removeFilter(SWT.MouseMove, this);
+ } else {
+ Point p = ((Control) event.widget).toDisplay(event.x, event.y);
+ // during resize mouse can be temporary outside of flyout - ignore
+ if (m_resize) {
+ return;
+ }
+ // mouse in in flyout container - ignore
+ if (getClientArea().contains(toControl(p.x, p.y))) {
+ return;
+ }
+ // mouse is in full container - collapse
+ if (container.getClientArea().contains(container.toControl(p.x, p.y))) {
+ getDisplay().removeFilter(SWT.MouseMove, this);
+ // it is possible, that user restored (OPEN) flyout, so collapse only if we still in expand state
+ if (m_preferences.getState() == IFlyoutPreferences.STATE_EXPANDED) {
+ m_preferences.setState(IFlyoutPreferences.STATE_COLLAPSED);
+ container.layout();
+ // BEGIN ADT MODIFICATIONS
+ if (m_listener != null) {
+ m_listener.stateChanged(IFlyoutPreferences.STATE_EXPANDED,
+ IFlyoutPreferences.STATE_COLLAPSED);
+ }
+ // END ADT MODIFICATIONS
+ }
+ }
+ }
+ }
+ };
+ addListener(SWT.Dispose, listener);
+ getDisplay().addFilter(SWT.MouseMove, listener);
+ }
+ }
+
+ /**
+ * Handler for docking.
+ */
+ private void handleDocking() {
+ final FlyoutControlComposite container = FlyoutControlComposite.this;
+ final int width = m_preferences.getWidth();
+ final int oldDockLocation = getDockLocation();
+ final int[] newDockLocation = new int[]{oldDockLocation};
+ final Tracker dockingTracker = new Tracker(container, SWT.NONE);
+ dockingTracker.setRectangles(new Rectangle[]{getBounds()});
+ dockingTracker.setStippled(true);
+ dockingTracker.addListener(SWT.Move, new Listener() {
+ @Override
+ public void handleEvent(Event event2) {
+ Rectangle clientArea = container.getClientArea();
+ Point location = container.toControl(event2.x, event2.y);
+ int h3 = clientArea.height / 3;
+ // check locations
+ if (location.y < h3 && isValidDockLocation(IFlyoutPreferences.DOCK_NORTH)) {
+ dockingTracker.setRectangles(new Rectangle[]{new Rectangle(0,
+ 0,
+ clientArea.width,
+ width)});
+ newDockLocation[0] = IFlyoutPreferences.DOCK_NORTH;
+ } else if (location.y > 2 * h3 && isValidDockLocation(IFlyoutPreferences.DOCK_SOUTH)) {
+ dockingTracker.setRectangles(new Rectangle[]{new Rectangle(0,
+ clientArea.height - width,
+ clientArea.width,
+ width)});
+ newDockLocation[0] = IFlyoutPreferences.DOCK_SOUTH;
+ } else if (location.x < clientArea.width / 2
+ && isValidDockLocation(IFlyoutPreferences.DOCK_WEST)) {
+ dockingTracker.setRectangles(new Rectangle[]{new Rectangle(0,
+ 0,
+ width,
+ clientArea.height)});
+ newDockLocation[0] = IFlyoutPreferences.DOCK_WEST;
+ } else if (isValidDockLocation(IFlyoutPreferences.DOCK_EAST)) {
+ dockingTracker.setRectangles(new Rectangle[]{new Rectangle(clientArea.width - width,
+ 0,
+ width,
+ clientArea.height)});
+ newDockLocation[0] = IFlyoutPreferences.DOCK_EAST;
+ } else {
+ dockingTracker.setRectangles(new Rectangle[]{getBounds()});
+ newDockLocation[0] = oldDockLocation;
+ }
+ }
+ });
+ // start tracking
+ if (dockingTracker.open()) {
+ setDockLocation(newDockLocation[0]);
+ }
+ // dispose tracker
+ dockingTracker.dispose();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * @return the {@link Control} installed on this {@link FlyoutControlComposite}, or
+ * <code>null</code> if there are no any {@link Control}.
+ */
+ private Control getControl() {
+ Control[] children = getChildren();
+ return children.length == 1 ? children[0] : null;
+ }
+
+ /**
+ * Sets the text of title.
+ */
+ public void setTitleText(String text) {
+ updateTitleImage(text);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Layout
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public void layout() {
+ Control control = getControl();
+ if (control == null) {
+ return;
+ }
+ // OK, we have control, so can continue layout
+ Rectangle clientArea = getClientArea();
+ if (isOpenExpanded()) {
+ if (isWest()) {
+ int y = m_titleHeight;
+ control.setBounds(0, y, clientArea.width - RESIZE_WIDTH, clientArea.height - y);
+ } else if (isEast()) {
+ int y = m_titleHeight;
+ control.setBounds(RESIZE_WIDTH, y, clientArea.width - RESIZE_WIDTH, clientArea.height - y);
+ } else if (isNorth()) {
+ int y = m_titleHeight;
+ control.setBounds(0, y, clientArea.width, clientArea.height - y - RESIZE_WIDTH);
+ } else if (isSouth()) {
+ int y = RESIZE_WIDTH + m_titleHeight;
+ control.setBounds(0, y, clientArea.width, clientArea.height - y);
+ }
+ } else {
+ control.setBounds(0, 0, 0, 0);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Paint
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private Image m_backImage;
+
+ /**
+ * Handler for {@link SWT#Paint} event.
+ */
+ private void handlePaint(GC paintGC) {
+ Rectangle clientArea = getClientArea();
+ // prepare back image
+ GC gc;
+ {
+ if (m_backImage == null || !m_backImage.getBounds().equals(clientArea)) {
+ if (m_backImage != null) {
+ m_backImage.dispose();
+ }
+ m_backImage = new Image(getDisplay(), clientArea.width, clientArea.height);
+ }
+ // prepare GC
+ gc = new GC(m_backImage);
+ gc.setBackground(paintGC.getBackground());
+ gc.setForeground(paintGC.getForeground());
+ gc.fillRectangle(clientArea);
+ }
+ //
+ if (isOpenExpanded()) {
+ // draw header
+ {
+ // draw title
+ if (isWest()) {
+ drawStateImage(gc, 0, 0);
+ gc.drawImage(m_titleImage, m_titleHeight, 0);
+ } else if (isEast()) {
+ int x = clientArea.width - m_titleHeight;
+ drawStateImage(gc, x, 0);
+ gc.drawImage(m_titleImage, x - m_titleWidth, 0);
+ } else if (isNorth()) {
+ drawStateImage(gc, 0, 0);
+ gc.drawImage(m_titleImage, m_titleHeight, 0);
+ } else if (isSouth()) {
+ int y = RESIZE_WIDTH;
+ drawStateImage(gc, 0, y);
+ gc.drawImage(m_titleImage, m_titleHeight, y);
+ }
+ }
+ // draw resize band
+ drawResizeBand(gc);
+ } else {
+ if (isHorizontal()) {
+ drawStateImage(gc, 0, 0);
+ gc.drawImage(m_titleImageRotated, 0, m_titleHeight);
+ } else {
+ drawStateImage(gc, 0, 0);
+ gc.drawImage(m_titleImage, m_titleHeight, 0);
+ }
+ DrawUtils.drawHighlightRectangle(gc, 0, 0, clientArea.width, clientArea.height);
+ }
+ // flush back image
+ {
+ gc.dispose();
+ paintGC.drawImage(m_backImage, 0, 0);
+ }
+ }
+
+ /**
+ * Draws the state image (arrow) at given location.
+ */
+ private void drawStateImage(GC gc, int x, int y) {
+ DrawUtils.drawImageCHCV(gc, getStateImage(), x, y, m_titleHeight, m_titleHeight);
+ if (m_stateHover) {
+ DrawUtils.drawHighlightRectangle(gc, x, y, m_titleHeight, m_titleHeight);
+ }
+ }
+
+ /**
+ * @return the {@link Image} corresponding to current state (open or collapsed).
+ */
+ private Image getStateImage() {
+ int location = getDockLocation();
+ int state = m_preferences.getState();
+ if (state == IFlyoutPreferences.STATE_OPEN) {
+ switch (location) {
+ case IFlyoutPreferences.DOCK_WEST :
+ return ARROW_LEFT;
+ case IFlyoutPreferences.DOCK_EAST :
+ return ARROW_RIGHT;
+ case IFlyoutPreferences.DOCK_NORTH :
+ return ARROW_TOP;
+ case IFlyoutPreferences.DOCK_SOUTH :
+ return ARROW_BOTTOM;
+ }
+ } else if (state == IFlyoutPreferences.STATE_EXPANDED) {
+ return PIN;
+ } else {
+ switch (location) {
+ case IFlyoutPreferences.DOCK_WEST :
+ return ARROW_RIGHT;
+ case IFlyoutPreferences.DOCK_EAST :
+ return ARROW_LEFT;
+ case IFlyoutPreferences.DOCK_NORTH :
+ return ARROW_BOTTOM;
+ case IFlyoutPreferences.DOCK_SOUTH :
+ return ARROW_TOP;
+ }
+ }
+ //
+ return null;
+ }
+
+ /**
+ * Draws that resize band, {@link Sash} like.
+ */
+ private void drawResizeBand(GC gc) {
+ Rectangle clientArea = getClientArea();
+ // prepare locations
+ int x, y, width, height;
+ if (isHorizontal()) {
+ if (isWest()) {
+ x = clientArea.width - RESIZE_WIDTH;
+ } else {
+ x = 0;
+ }
+ y = 0;
+ width = RESIZE_WIDTH;
+ height = clientArea.height;
+ } else {
+ x = 0;
+ if (isNorth()) {
+ y = clientArea.height - RESIZE_WIDTH;
+ } else {
+ y = 0;
+ }
+ width = clientArea.width;
+ height = RESIZE_WIDTH;
+ }
+ // draw band
+ DrawUtils.drawHighlightRectangle(gc, x, y, width, height);
+ }
+
+ /**
+ * @return <code>true</code> if flyout is open or expanded.
+ */
+ private boolean isOpenExpanded() {
+ int state = m_preferences.getState();
+ return state == IFlyoutPreferences.STATE_OPEN || state == IFlyoutPreferences.STATE_EXPANDED;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Title image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private int m_titleWidth;
+ private int m_titleHeight;
+ private Image m_titleImage;
+ private Image m_titleImageRotated;
+
+ /**
+ * Creates {@link Image} for given title text.
+ */
+ private void updateTitleImage(String text) {
+ // prepare size of text
+ Point textSize;
+ {
+ GC gc = new GC(this);
+ gc.setFont(TITLE_FONT);
+ textSize = gc.textExtent(text);
+ gc.dispose();
+ }
+ // dispose existing image
+ if (m_titleImage != null) {
+ m_titleImage.dispose();
+ m_titleImageRotated.dispose();
+ }
+ // prepare new image
+ {
+ m_titleWidth = textSize.x + 2 * TITLE_LINES + 4 * TITLE_MARGIN;
+ m_titleHeight = textSize.y;
+ m_titleImage = new Image(getDisplay(), m_titleWidth, m_titleHeight);
+ GC gc = new GC(m_titleImage);
+ try {
+ gc.setBackground(getBackground());
+ gc.fillRectangle(0, 0, m_titleWidth, m_titleHeight);
+ int x = 0;
+ // draw left lines
+ {
+ x += TITLE_MARGIN;
+ drawTitleLines(gc, x, m_titleHeight, TITLE_LINES);
+ x += TITLE_LINES + TITLE_MARGIN;
+ }
+ // draw text
+ {
+ gc.setForeground(IColorConstants.black);
+ gc.setFont(TITLE_FONT);
+ gc.drawText(text, x, 0);
+ x += textSize.x;
+ }
+ // draw right lines
+ {
+ x += TITLE_MARGIN;
+ drawTitleLines(gc, x, m_titleHeight, TITLE_LINES);
+ }
+ } finally {
+ gc.dispose();
+ }
+ }
+ // prepare rotated image
+ m_titleImageRotated = DrawUtils.createRotatedImage(m_titleImage);
+ }
+
+ /**
+ * Draws two title lines.
+ */
+ private void drawTitleLines(GC gc, int x, int height, int width) {
+ drawTitleLine(gc, x, height / 3, width);
+ drawTitleLine(gc, x, 2 * height / 3, width);
+ }
+
+ /**
+ * Draws single title line.
+ */
+ private void drawTitleLine(GC gc, int x, int y, int width) {
+ int right = x + TITLE_LINES;
+ //
+ gc.setForeground(IColorConstants.buttonLightest);
+ gc.drawLine(x, y, right - 2, y);
+ gc.drawLine(x, y + 1, right - 2, y + 1);
+ //
+ gc.setForeground(IColorConstants.buttonDarker);
+ gc.drawLine(right - 2, y, right - 1, y);
+ gc.drawLine(x + 2, y + 1, right - 2, y + 1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Menu
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private void configureMenu() {
+ final MenuManager manager = new MenuManager();
+ manager.setRemoveAllWhenShown(true);
+ manager.addMenuListener(new IMenuListener() {
+ @Override
+ public void menuAboutToShow(IMenuManager menuMgr) {
+ addDockActions();
+ for (IFlyoutMenuContributor contributor : m_menuContributors) {
+ contributor.contribute(manager);
+ }
+ }
+
+ private void addDockActions() {
+ MenuManager dockManager = new MenuManager(Messages.FlyoutControlComposite_dockManager);
+ addDockAction(
+ dockManager,
+ Messages.FlyoutControlComposite_dockLeft,
+ IFlyoutPreferences.DOCK_WEST);
+ addDockAction(
+ dockManager,
+ Messages.FlyoutControlComposite_dockRight,
+ IFlyoutPreferences.DOCK_EAST);
+ addDockAction(
+ dockManager,
+ Messages.FlyoutControlComposite_dockTop,
+ IFlyoutPreferences.DOCK_NORTH);
+ addDockAction(
+ dockManager,
+ Messages.FlyoutControlComposite_dockBottom,
+ IFlyoutPreferences.DOCK_SOUTH);
+ manager.add(dockManager);
+ }
+
+ private void addDockAction(MenuManager dockManager, String text, int location) {
+ if ((m_validDockLocations & location) != 0) {
+ dockManager.add(new DockAction(text, location));
+ }
+ }
+ });
+ // set menu
+ setMenu(manager.createContextMenu(this));
+ // dispose it later
+ addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ manager.dispose();
+ }
+ });
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // DockAction
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private class DockAction extends Action {
+ private final int m_location;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public DockAction(String text, int location) {
+ super(text, AS_RADIO_BUTTON);
+ m_location = location;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Action
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public boolean isChecked() {
+ return getDockLocation() == m_location;
+ }
+
+ @Override
+ public void run() {
+ setDockLocation(m_location);
+ }
+ }
+}