code: /*--------------------------------------------------------------*
    Copyright (C) 2006-2015 OpenSim Ltd.
  
    This file is distributed WITHOUT ANY WARRANTY. See the file
    'License' for details on this and other legal matters.
  *--------------------------------------------------------------*/
  
  package org.omnetpp.common.ui;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.eclipse.swt.SWT;
  import org.eclipse.swt.events.MouseEvent;
  import org.eclipse.swt.events.MouseListener;
  import org.eclipse.swt.events.MouseTrackListener;
  import org.eclipse.swt.events.PaintEvent;
  import org.eclipse.swt.events.PaintListener;
  import org.eclipse.swt.events.SelectionEvent;
  import org.eclipse.swt.events.SelectionListener;
  import org.eclipse.swt.graphics.Color;
  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.Canvas;
  import org.eclipse.swt.widgets.Composite;
  import org.eclipse.swt.widgets.Event;
  import org.omnetpp.common.color.ColorFactory;
  
  /**
   * A button which looks like SWT's ToolItem (flat), but text is rendered to the right
   * of the image. SWT's ToolItem puts text below the image and cannot be talked out of it,
   * while Button has the right layout but it is never rendered as flat. Oh well.
   *
   * This is expected to be put on a light background; otherwise colors used for painting
   * will have to be refined (probably it's best to dynamically calculate them, using
   * the RGB class's HSB conversion.)
   *
   * @author Andras
   */
  public class ToolButton extends Canvas {
      private static final int R = 5;
      private static final int BORDER = 4;
  
      private Color normalInnerBorderColor = null;
      private Color normalOuterBorderColor = ColorFactory.GREY90;
  
      private Color activeBackgroundColor = ColorFactory.GREY96;
      private Color activeInnerBorderColor = ColorFactory.ORANGE;
      private Color activeOuterBorderColor = ColorFactory.GREY40;
  
      private Color pushedBackgroundColor = ColorFactory.GREY90;
      private Color pushedOuterBorderColor = ColorFactory.GREY40;
      private Color pushedInnerBorderColor = ColorFactory.GREY95;
  
      private Color disabledBackgroundColor = null;
      private Color disabledOuterBorderColor = ColorFactory.GREY90;
      private Color disabledInnerBorderColor = null;
  
      private List<SelectionListener> selectionListeners = new ArrayList<SelectionListener>();
  
      private Image image;
      private String text;
      private int textWidth = -1;
      private int textHeight= -1;
  
      private static int NORMAL_STATE = 0;
      private static int ACTIVE_STATE = 1;
      private static int PUSHED_STATE = 2;
      private int state = 0;
  
      public ToolButton(Composite parent, int style) {
          super(parent, style);
          setBackground(ColorFactory.WHITE);
  
          addPaintListener(new PaintListener() {
              public void paintControl(PaintEvent e) {
                  _repaint(e.gc);
              }
          });
  
          addMouseTrackListener(new MouseTrackListener() {
              public void mouseEnter(MouseEvent e) {
                  if (isEnabled()) {
                      state = ACTIVE_STATE;
                      redraw();
                  }
              }
              public void mouseExit(MouseEvent e) {
                  if (isEnabled()) {
                      state = NORMAL_STATE;
                      redraw();
                  }
              }
              public void mouseHover(MouseEvent e) {
              }
          });
  
          addMouseListener(new MouseListener() {
              public void mouseDoubleClick(MouseEvent e) {
              }
  
              public void mouseDown(MouseEvent e) {
                  if (isEnabled() && e.button==1) {
                      // Note: when DragSource is installed on the button, the mouseDown event
                      // apparently gets intercepted and delayed ~1s by DragSource
                      state = PUSHED_STATE;
                      redraw();
                  }
              }
  
              public void mouseUp(MouseEvent e) {
                  Point size = ToolButton.this.getSize();
                  Rectangle bounds = new Rectangle(0, 0, size.x, size.y);
                  if (isEnabled() && e.button==1 &&
                          e.widget == ToolButton.this && bounds.contains(e.x, e.y)) {
                      state = ACTIVE_STATE;
                      redraw();
  
                      // fire "button selected" event
                      Event tmp = new Event();
                      tmp.display = e.display;
                      tmp.widget = e.widget;
                      tmp.x = e.x;
                      tmp.y = e.y;
                      tmp.stateMask = e.stateMask;
                      SelectionEvent selectionEvent = new SelectionEvent(tmp);
                      for (SelectionListener listener : selectionListeners)
                          listener.widgetSelected(selectionEvent);
                  }
              }
          });
      }
  
      protected void _repaint(GC gc) {
          Rectangle r = getBounds();
          if (!isEnabled())
              fillBackground(gc, r, disabledBackgroundColor);
          else if (state==ACTIVE_STATE)
              fillBackground(gc, r, activeBackgroundColor);
          else if (state==PUSHED_STATE)
              fillBackground(gc, r, pushedBackgroundColor);
  
          if (image != null)
              gc.drawImage(image, BORDER, (r.height-image.getBounds().height+1)/2); //TODO paint disabled image
          if (text != null) {
              if (getFont() != null)
                  gc.setFont(getFont());
              if (textHeight == -1)
                  updateTextExtent(gc);
              gc.setForeground(isEnabled() ? ColorFactory.BLACK : ColorFactory.GREY50);
              gc.drawText(text, BORDER + (image==null ? 0 : image.getBounds().width+BORDER), (r.height-textHeight+1) / 2);
          }
          if (!isEnabled())
              drawBorder(gc, r, disabledOuterBorderColor, disabledInnerBorderColor);
          else if (state==NORMAL_STATE)
              drawBorder(gc, r, normalOuterBorderColor, normalInnerBorderColor);
          else if (state==ACTIVE_STATE)
              drawBorder(gc, r, activeOuterBorderColor, activeInnerBorderColor);
          else if (state==PUSHED_STATE)
              drawBorder(gc, r, pushedOuterBorderColor, pushedInnerBorderColor);
      }
  
      protected void fillBackground(GC gc, Rectangle r, Color color) {
          if (color != null) {
              gc.setBackground(color);
              gc.fillRoundRectangle(0, 0, r.width-1, r.height-1, R, R);
          }
      }
  
      protected void drawBorder(GC gc, Rectangle r, Color outerColor, Color innerColor) {
          gc.setAntialias(SWT.ON);
          if (outerColor != null) {
              gc.setForeground(outerColor);
              gc.drawRoundRectangle(0, 0, r.width-1, r.height-1, R, R);
          }
          if (innerColor != null) {
              gc.setForeground(innerColor);
              gc.drawRoundRectangle(1, 1, r.width-3, r.height-3, R-2, R-2);
          }
      }
  
      protected void updateTextExtent(GC gc) {
          if (getFont() != null)
              gc.setFont(getFont());
          if (text == null)
              textWidth = textHeight = 0;
          else {
              Point size = gc.textExtent(text);
              textWidth = size.x;
              textHeight = size.y;
          }
      }
  
      public Point computeSize(int wHint, int hHint, boolean changed) {
          checkWidget();
          if (textWidth == -1) {
              GC gc = new GC(this);
              updateTextExtent(gc);
              gc.dispose();
          }
  
          int border = getBorderWidth();
          int width = wHint, height = hHint;
          if (width == SWT.DEFAULT)
              width = border * 2 + BORDER * 3 + (image==null ? 0 : image.getBounds().width) + textWidth;
          if (height == SWT.DEFAULT)
              height = border * 2 + BORDER * 2 + Math.max(image==null ? 0 : image.getBounds().height, textHeight);
          return new Point(width, height);
      }
  
      public Image getImage() {
          checkWidget();
          return image;
      }
  
      public String getText() {
          checkWidget();
          return text;
      }
  
      public void setImage(Image image) {
          checkWidget();
          if (image != null && image.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
          this.image = image;
      }
  
      public void setText(String string) {
          checkWidget();
          text = string;
          textWidth = textHeight = -1;
      }
  
      @Override
      public void setFont(Font font) {
          super.setFont(font);
          textWidth = textHeight = -1;
      }
  
      public void addSelectionListener(SelectionListener listener) {
          checkWidget();
          if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
          if (!selectionListeners.contains(listener))
              selectionListeners.add(listener);
      }
  
      public void removeSelectionListener(SelectionListener listener) {
          checkWidget();
          if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
          selectionListeners.remove(listener);
      }
  
      @Override
      public String toString() {
          return getClass().getSimpleName() + " {" + getText() + "}";
      }
  }
  