blob: a910810382513a2c378553e3c49a3a8fba8bca71 [file] [log] [blame]
/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.ui.internal.forms.widgets;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEffect;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
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.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.ILayoutExtension;
import org.eclipse.ui.forms.widgets.SizeCache;
import org.eclipse.ui.forms.widgets.Twistie;
import org.eclipse.ui.internal.forms.IMessageToolTipManager;
/**
* Form heading title.
*/
public class TitleRegion extends Canvas {
public static final int STATE_NORMAL = 0;
public static final int STATE_HOVER_LIGHT = 1;
public static final int STATE_HOVER_FULL = 2;
private int hoverState;
private static final int HMARGIN = 1;
private static final int VMARGIN = 5;
private static final int SPACING = 5;
private static final int ARC_WIDTH = 20;
private static final int ARC_HEIGHT = 20;
private Image image;
private BusyIndicator busyLabel;
private Label titleLabel;
private SizeCache titleCache;
private int fontHeight = -1;
private int fontBaselineHeight = -1;
private MenuHyperlink menuHyperlink;
private MenuManager menuManager;
private boolean dragSupport;
private int dragOperations;
private Transfer[] dragTransferTypes;
private DragSourceListener dragListener;
private DragSource dragSource;
private Image dragImage;
private class HoverListener implements MouseTrackListener,
MouseMoveListener {
public void mouseEnter(MouseEvent e) {
setHoverState(STATE_HOVER_FULL);
}
public void mouseExit(MouseEvent e) {
setHoverState(STATE_NORMAL);
}
public void mouseHover(MouseEvent e) {
}
public void mouseMove(MouseEvent e) {
if (e.button > 0)
setHoverState(STATE_NORMAL);
else
setHoverState(STATE_HOVER_FULL);
}
}
private class MenuHyperlink extends Twistie {
private boolean firstTime = true;
public MenuHyperlink(Composite parent, int style) {
super(parent, style);
setExpanded(true);
}
public void setExpanded(boolean expanded) {
if (firstTime) {
super.setExpanded(expanded);
firstTime = false;
} else {
Menu menu = menuManager.createContextMenu(menuHyperlink);
menu.setVisible(true);
}
}
}
private class TitleRegionLayout extends Layout implements ILayoutExtension {
protected Point computeSize(Composite composite, int wHint, int hHint,
boolean flushCache) {
return layout(composite, false, 0, 0, wHint, hHint, flushCache);
}
protected void layout(Composite composite, boolean flushCache) {
Rectangle carea = composite.getClientArea();
layout(composite, true, carea.x, carea.y, carea.width,
carea.height, flushCache);
}
private Point layout(Composite composite, boolean move, int x, int y,
int width, int height, boolean flushCache) {
int iwidth = width == SWT.DEFAULT ? SWT.DEFAULT : width - HMARGIN
* 2;
Point bsize = null;
Point tsize = null;
Point msize = null;
if (busyLabel != null) {
bsize = busyLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
if (menuManager != null) {
menuHyperlink.setVisible(!menuManager.isEmpty()
&& titleLabel.getVisible());
if (menuHyperlink.getVisible())
msize = menuHyperlink.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
if (flushCache)
titleCache.flush();
titleCache.setControl(titleLabel);
int twidth = iwidth == SWT.DEFAULT ? iwidth : iwidth - SPACING * 2;
if (bsize != null && twidth != SWT.DEFAULT)
twidth -= bsize.x + SPACING;
if (msize != null && twidth != SWT.DEFAULT)
twidth -= msize.x + SPACING;
if (titleLabel.getVisible()) {
tsize = titleCache.computeSize(twidth, SWT.DEFAULT);
if (twidth != SWT.DEFAULT) {
// correct for the case when width hint is larger
// than the maximum width - this is when the text
// can be rendered on one line with width to spare
int maxWidth = titleCache.computeSize(SWT.DEFAULT,
SWT.DEFAULT).x;
tsize.x = Math.min(tsize.x, maxWidth);
// System.out.println("twidth="+twidth+",
// tsize.x="+tsize.x); //$NON-NLS-1$//$NON-NLS-2$
}
} else
tsize = new Point(0, 0);
Point size = new Point(width, height);
if (!move) {
// compute size
size.x = tsize.x > 0 ? HMARGIN * 2 + SPACING * 2 + tsize.x : 0;
size.y = tsize.y;
if (bsize != null) {
size.x += bsize.x + SPACING;
size.y = Math.max(size.y, bsize.y);
}
if (msize != null) {
size.x += msize.x + SPACING;
size.y = Math.max(size.y, msize.y);
}
if (size.y > 0)
size.y += VMARGIN * 2;
// System.out.println("Compute size: width="+width+",
// size.x="+size.x); //$NON-NLS-1$ //$NON-NLS-2$
} else {
// position controls
int xloc = x + HMARGIN + SPACING;
int yloc = y + VMARGIN;
if (bsize != null) {
busyLabel.setBounds(xloc,
// yloc + height / 2 - bsize.y / 2,
yloc + (getFontHeight() >= bsize.y ? getFontHeight() : bsize.y) - 1 - bsize.y,
bsize.x, bsize.y);
xloc += bsize.x + SPACING;
}
if (titleLabel.getVisible()) {
int tw = width - HMARGIN * 2 - SPACING * 2;
if (bsize != null)
tw -= bsize.x + SPACING;
if (msize != null)
tw -= msize.x + SPACING;
titleLabel.setBounds(xloc,
// yloc + height / 2 - tsize.y / 2,
yloc, tw, tsize.y);
// System.out.println("tw="+tw); //$NON-NLS-1$
xloc += tw + SPACING;
}
if (msize != null) {
menuHyperlink.setBounds(xloc, yloc
+ getFontHeight() / 2 - msize.y / 2,
msize.x, msize.y);
}
}
return size;
}
public int computeMaximumWidth(Composite parent, boolean changed) {
return computeSize(parent, SWT.DEFAULT, SWT.DEFAULT, changed).x;
}
public int computeMinimumWidth(Composite parent, boolean changed) {
return computeSize(parent, 0, SWT.DEFAULT, changed).x;
}
}
public TitleRegion(Composite parent) {
super(parent, SWT.NULL);
titleLabel = new Label(this, SWT.WRAP);
titleLabel.setVisible(false);
titleCache = new SizeCache();
super.setLayout(new TitleRegionLayout());
hookHoverListeners();
addListener(SWT.Dispose, new Listener() {
public void handleEvent(Event e) {
if (dragImage != null) {
dragImage.dispose();
dragImage = null;
}
}
});
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Control#forceFocus()
*/
public boolean forceFocus() {
return false;
}
private Color getColor(String key) {
return (Color) ((FormHeading) getParent()).colors.get(key);
}
private void hookHoverListeners() {
HoverListener listener = new HoverListener();
addMouseTrackListener(listener);
addMouseMoveListener(listener);
titleLabel.addMouseTrackListener(listener);
titleLabel.addMouseMoveListener(listener);
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
onPaint(e);
}
});
}
private void onPaint(PaintEvent e) {
if (hoverState == STATE_NORMAL)
return;
GC gc = e.gc;
Rectangle carea = getClientArea();
gc.setBackground(getHoverBackground());
int savedAntialias = gc.getAntialias();
FormUtil.setAntialias(gc, SWT.ON);
gc.fillRoundRectangle(carea.x + HMARGIN, carea.y + 2, carea.width
- HMARGIN * 2, carea.height - 4, ARC_WIDTH, ARC_HEIGHT);
FormUtil.setAntialias(gc, savedAntialias);
}
private Color getHoverBackground() {
if (hoverState == STATE_NORMAL)
return null;
Color color = getColor(hoverState == STATE_HOVER_FULL ? IFormColors.H_HOVER_FULL
: IFormColors.H_HOVER_LIGHT);
if (color == null)
color = getDisplay()
.getSystemColor(
hoverState == STATE_HOVER_FULL ? SWT.COLOR_WIDGET_BACKGROUND
: SWT.COLOR_WIDGET_LIGHT_SHADOW);
return color;
}
public void setHoverState(int state) {
if (dragSource == null || this.hoverState == state)
return;
this.hoverState = state;
Color color = getHoverBackground();
titleLabel.setBackground(color != null ? color
: getColor(FormHeading.COLOR_BASE_BG));
if (busyLabel != null)
busyLabel.setBackground(color != null ? color
: getColor(FormHeading.COLOR_BASE_BG));
if (menuHyperlink != null)
menuHyperlink.setBackground(color != null ? color
: getColor(FormHeading.COLOR_BASE_BG));
redraw();
}
/**
* Fully delegates the size computation to the internal layout manager.
*/
public final Point computeSize(int wHint, int hHint, boolean changed) {
return ((TitleRegionLayout) getLayout()).computeSize(this, wHint,
hHint, changed);
}
public final void setLayout(Layout layout) {
// do nothing
}
public Image getImage() {
return image;
}
public void setImage(Image image) {
this.image = image;
}
public void updateImage(Image newImage, boolean doLayout) {
Image theImage = newImage != null ? newImage : this.image;
if (theImage != null) {
ensureBusyLabelExists();
} else if (busyLabel != null) {
if (!busyLabel.isBusy()) {
busyLabel.dispose();
busyLabel = null;
}
}
if (busyLabel != null) {
busyLabel.setImage(theImage);
}
if (doLayout)
layout();
}
public void updateToolTip(String toolTip) {
if (busyLabel != null)
busyLabel.setToolTipText(toolTip);
}
public void setBackground(Color bg) {
super.setBackground(bg);
titleLabel.setBackground(bg);
if (busyLabel != null)
busyLabel.setBackground(bg);
if (menuHyperlink != null)
menuHyperlink.setBackground(bg);
}
public void setForeground(Color fg) {
super.setForeground(fg);
titleLabel.setForeground(fg);
if (menuHyperlink != null)
menuHyperlink.setForeground(fg);
}
public void setText(String text) {
if (text != null)
titleLabel.setText(text);
titleLabel.setVisible(text != null);
layout();
redraw();
}
public String getText() {
return titleLabel.getText();
}
public void setFont(Font font) {
super.setFont(font);
titleLabel.setFont(font);
fontHeight = -1;
fontBaselineHeight = -1;
layout();
}
private void ensureBusyLabelExists() {
if (busyLabel == null) {
busyLabel = new BusyIndicator(this, SWT.NULL);
busyLabel.setBackground(getColor(FormHeading.COLOR_BASE_BG));
HoverListener listener = new HoverListener();
busyLabel.addMouseTrackListener(listener);
busyLabel.addMouseMoveListener(listener);
if (menuManager != null)
busyLabel.setMenu(menuManager.createContextMenu(this));
if (dragSupport)
addDragSupport(busyLabel, dragOperations, dragTransferTypes, dragListener);
IMessageToolTipManager mng = ((FormHeading) getParent())
.getMessageToolTipManager();
if (mng != null)
mng.createToolTip(busyLabel, true);
}
}
private void createMenuHyperlink() {
menuHyperlink = new MenuHyperlink(this, SWT.NULL);
menuHyperlink.setBackground(getColor(FormHeading.COLOR_BASE_BG));
menuHyperlink.setDecorationColor(getForeground());
menuHyperlink.setHoverDecorationColor(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND));
HoverListener listener = new HoverListener();
menuHyperlink.addMouseTrackListener(listener);
menuHyperlink.addMouseMoveListener(listener);
if (dragSupport)
addDragSupport(menuHyperlink, dragOperations, dragTransferTypes, dragListener);
}
/**
* Sets the form's busy state. Busy form will display 'busy' animation in
* the area of the title image.
*
* @param busy
* the form's busy state
*/
public boolean setBusy(boolean busy) {
if (busy)
ensureBusyLabelExists();
else if (busyLabel == null)
return false;
if (busy == busyLabel.isBusy())
return false;
busyLabel.setBusy(busy);
if (busyLabel.getImage() == null) {
layout();
return true;
}
return false;
}
public boolean isBusy() {
return busyLabel != null && busyLabel.isBusy();
}
/*
* Returns the complete height of the font.
*/
public int getFontHeight() {
if (fontHeight == -1) {
Font font = getFont();
GC gc = new GC(getDisplay());
gc.setFont(font);
FontMetrics fm = gc.getFontMetrics();
fontHeight = fm.getHeight();
gc.dispose();
}
return fontHeight;
}
/*
* Returns the height of the font starting at the baseline,
* i.e. without the descent.
*/
public int getFontBaselineHeight() {
if (fontBaselineHeight == -1) {
Font font = getFont();
GC gc = new GC(getDisplay());
gc.setFont(font);
FontMetrics fm = gc.getFontMetrics();
fontBaselineHeight = fm.getHeight() - fm.getDescent();
gc.dispose();
}
return fontBaselineHeight;
}
public IMenuManager getMenuManager() {
if (menuManager == null) {
menuManager = new MenuManager();
Menu menu = menuManager.createContextMenu(this);
setMenu(menu);
titleLabel.setMenu(menu);
if (busyLabel != null)
busyLabel.setMenu(menu);
createMenuHyperlink();
}
return menuManager;
}
public void addDragSupport(int operations, Transfer[] transferTypes,
DragSourceListener listener) {
dragSupport = true;
dragOperations = operations;
dragTransferTypes = transferTypes;
dragListener = listener;
dragSource = addDragSupport(titleLabel, operations, transferTypes,
listener);
addDragSupport(this, operations, transferTypes, listener);
if (busyLabel != null)
addDragSupport(busyLabel, operations, transferTypes, listener);
if (menuHyperlink != null)
addDragSupport(menuHyperlink, operations, transferTypes, listener);
}
private DragSource addDragSupport(Control control, int operations,
Transfer[] transferTypes, DragSourceListener listener) {
DragSource source = new DragSource(control, operations);
source.setTransfer(transferTypes);
source.addDragListener(listener);
source.setDragSourceEffect(new DragSourceEffect(control) {
public void dragStart(DragSourceEvent event) {
event.image = createDragEffectImage();
}
});
return source;
}
private Image createDragEffectImage() {
/*
* if (dragImage != null) { dragImage.dispose(); } GC gc = new GC(this);
* Point size = getSize(); dragImage = new Image(getDisplay(), size.x,
* size.y); gc.copyArea(dragImage, 0, 0); gc.dispose(); return
* dragImage;
*/
return null;
}
public void addDropSupport(int operations, Transfer[] transferTypes,
DropTargetListener listener) {
final DropTarget target = new DropTarget(this, operations);
target.setTransfer(transferTypes);
target.addDropListener(listener);
}
}