/******************************************************************************* * Copyright (c) 2009 the CHISEL group and contributors. * 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: * Del Myers -- initial API and implementation *******************************************************************************/ package org.eclipse.zest.custom.sequence.figures; import java.util.Iterator; import org.eclipse.draw2d.ActionEvent; import org.eclipse.draw2d.ActionListener; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.FigureUtilities; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; /** * A figure that draws a box around a group. * @author Del Myers */ public class ActivationGroupFigure extends Shape { /** * */ private static final String ELLIPSIS = "..."; private Rectangle clickBounds; private String text; private String subStringText; private boolean down; private int labelOffset; private class MouseActionListener implements MouseListener { public void mouseDoubleClicked(MouseEvent me) { ActivationGroupFigure.this.mouseDoubleClicked(me); } public void mousePressed(MouseEvent me) { ActivationGroupFigure.this.mousePressed(me); } public void mouseReleased(MouseEvent me) { ActivationGroupFigure.this.mouseReleased(me); } } /** * */ public ActivationGroupFigure() { addMouseListener(new MouseActionListener()); clickBounds = new Rectangle(); down = true; } public void addActionListener(ActionListener listener) { addListener(ActionListener.class, listener); } public void removeActionListener(ActionListener listener) { removeListener(ActionListener.class, listener); } public void setText(String text) { this.text = text; recalculateTextExtents(); } /** * @param me */ protected void mouseReleased(MouseEvent me) { } /** * @param me */ protected void mousePressed(MouseEvent me) { if (me.button == 1) { //check the region. if (clickBounds != null && clickBounds.contains(me.x, me.y)) { down = !down; resetClickBounds(); repaint(); fireActionPerformed(); } else { Rectangle clientArea = getClientArea(); Rectangle activeArea = new Rectangle( clientArea.getLocation(), getLabelSize() ); if (!activeArea.contains(me.getLocation())) { me.consume(); } } } } public boolean isExpanded() { return down; } /** * */ @SuppressWarnings("unchecked") private void fireActionPerformed() { Iterator listeners = getListeners(ActionListener.class); ActionEvent event = new ActionEvent(this); while (listeners.hasNext()) { ((ActionListener)listeners.next()).actionPerformed(event); } } /** * @param me */ protected void mouseDoubleClicked(MouseEvent me) { } /** * @return the text */ public String getText() { return text; } /** * @return the subStringText */ public String getSubStringText() { return subStringText; } public Dimension getLabelSize() { return FigureUtilities.getStringExtents(subStringText, getFont()).expand(14, 4); } public Dimension getPreferredLabelSize() { return FigureUtilities.getStringExtents(getText(), getFont()).expand(14, 4); } /** * */ private void recalculateTextExtents() { subStringText = text; Font font = getFont(); if (text != null && font != null) { Dimension te = FigureUtilities.getTextExtents(text, font); if (te.width >= bounds.width) { Dimension ee = FigureUtilities.getTextExtents(ELLIPSIS, font); int subStringLength = getLargestSubstringConfinedTo(text, font, getBounds().width - ee.width-12); subStringText = new String(text.substring(0, subStringLength) + ELLIPSIS); } } } private int getLargestSubstringConfinedTo(String s, Font f, int availableWidth) { FontMetrics metrics = FigureUtilities.getFontMetrics(f); int min, max; float avg = metrics.getAverageCharWidth(); min = 0; max = s.length() + 1; //The size of the current guess int guess = 0, guessSize = 0; while ((max - min) > 1) { //Pick a new guess size // New guess is the last guess plus the missing width in pixels // divided by the average character size in pixels guess = guess + (int)((availableWidth - guessSize) / avg); if (guess >= max) guess = max - 1; if (guess <= min) guess = min + 1; //Measure the current guess guessSize = FigureUtilities.getTextExtents(s.substring(0, guess), f).width; if (guessSize < availableWidth) //We did not use the available width min = guess; else //We exceeded the available width max = guess; } return min; } @Override protected final void fillShape(Graphics graphics) { Dimension labelSize = getLabelSize(); graphics.setLineWidth(getLineWidth()); Rectangle labelBounds = new Rectangle(new Point(getBounds().x+labelOffset, getBounds().y), labelSize); PointList points = new PointList(new int[]{ labelBounds.x, labelBounds.y, labelBounds.x+labelBounds.width+4, labelBounds.y, labelBounds.x+labelBounds.width+4, labelBounds.y+labelBounds.height, labelBounds.x+labelBounds.width, labelBounds.y+labelBounds.height+2, labelBounds.x, labelBounds.y+labelBounds.height+2 }); graphics.fillPolygon(points); outlineShape(graphics); } @Override protected final void outlineShape(Graphics graphics) { Rectangle bounds = getBounds(); graphics.setLineWidth(getLineWidth()); Color foreground = getForegroundColor(); if (foreground == null) { foreground = ColorConstants.black; } graphics.setForegroundColor(foreground); graphics.drawRectangle(bounds.x, bounds.y, bounds.width-1, bounds.height-1); PointList list = new PointList(); // list.addPoint(0,0); list.addPoint(clickBounds.x, clickBounds.y); //draw the click region. if (down) { // list.addPoint(4, 8); // list.addPoint(8,0); list.addPoint(clickBounds.x + clickBounds.width/2, clickBounds.y+clickBounds.height-1); list.addPoint(clickBounds.x + clickBounds.width-1, clickBounds.y); } else { // list.addPoint(8, 4); // list.addPoint(0, 8); list.addPoint(clickBounds.x, clickBounds.y+clickBounds.height-1); list.addPoint(clickBounds.x + clickBounds.width-1, clickBounds.y+clickBounds.height/2); } // list.translate(clickBounds.x, clickBounds.y); Color background = getBackgroundColor(); graphics.setBackgroundColor(ColorConstants.black); graphics.fillPolygon(list); graphics.setBackgroundColor(background); //draw the text with ellipses graphics.drawText(subStringText, bounds.x+2+clickBounds.width+labelOffset, bounds.y+2); Dimension labelSize = getLabelSize(); Rectangle labelBounds = new Rectangle(new Point(getBounds().x+labelOffset, getBounds().y), labelSize); PointList points = new PointList(new int[]{ labelBounds.x, labelBounds.y, labelBounds.x+labelBounds.width+4, labelBounds.y, labelBounds.x+labelBounds.width+4, labelBounds.y+labelBounds.height, labelBounds.x+labelBounds.width, labelBounds.y+labelBounds.height+2, labelBounds.x, labelBounds.y+labelBounds.height+2 }); graphics.drawPolygon(points); } @Override public void setBounds(Rectangle rect) { super.setBounds(rect); resetClickBounds(); recalculateTextExtents(); } /** * */ private void resetClickBounds() { int yPad = 4; if (down) { yPad = 6; } clickBounds = new Rectangle(getBounds().x+2+labelOffset, getBounds().y+yPad, 9,9); } @Override public boolean containsPoint(int x, int y) { Rectangle labelRectangle = new Rectangle(new Point(getBounds().x+labelOffset, getBounds().y), getLabelSize()); return labelRectangle.contains(x, y); } /** * @param b */ public void setArrowDown(boolean b) { this.down = b; } /** * @param i */ public void setLabelOffset(int i) { this.labelOffset = i; } }