| /******************************************************************************* |
| * 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() - 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; |
| } |
| } |
| }); |
| } |
| |
| 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); |
| } |
| } |