Package org.eclipse.jface.fieldassist

Source Code of org.eclipse.jface.fieldassist.ControlDecoration

/*******************************************************************************
* Copyright (c) 2006, 2011 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.jface.fieldassist;

import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseAdapter;
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.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.graphics.Region;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;


/**
* ControlDecoration renders an image decoration near a control. It allows
* clients to specify an image and a position for the image relative to the
* control. A ControlDecoration may be assigned description text, which can
* optionally be shown when the user hovers over the image. Clients can decorate
* any kind of control.
* <p>
* Decoration images always appear on the left or right side of the field, never
* above or below it. Decorations can be positioned at the top, center, or
* bottom of either side of the control. Future implementations may provide
* additional positioning options for decorations.
* <p>
* ControlDecoration renders the image adjacent to the specified (already
* created) control, with no guarantee that it won't be clipped or otherwise
* obscured or overlapped by adjacent controls, including another
* ControlDecoration placed in the same location. Clients should ensure that
* there is adequate space adjacent to the control to show the decoration
* properly.
* <p>
* Clients using ControlDecoration should typically ensure that enough margin
* space is reserved for a decoration by altering the layout data margins,
* although this is not assumed or required by the ControlDecoration
* implementation.
* <p>
* This class is intended to be instantiated and used by clients. It is not
* intended to be subclassed by clients.
*
* @since 3.3
*
* @see FieldDecoration
* @see FieldDecorationRegistry
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class ControlDecoration {
  /**
   * Debug flag for tracing
   */
  private static boolean DEBUG = false;

  /**
   * Cached platform flag for dealing with platform-specific issue:
   * https://bugs.eclipse.org/bugs/show_bug.cgi?id=219326 : Shell with custom region and SWT.NO_TRIM still has border
   */
  private static boolean MAC = Util.isMac();

  /**
   * The associated control
   */
  private Control control;

  /**
   * The composite on which to render the decoration and hook mouse events, or
   * null if we are hooking all parent composites.
   */
  private Composite composite;

  /**
   * The associated image.
   */
  private Image image;

  /**
   * The associated description text.
   */
  private String descriptionText;
  /**
   * The position of the decoration.
   */
  private int position;

  /**
   * The decoration's visibility flag
   */
  private boolean visible = true;

  /**
   * Boolean indicating whether the decoration should only be shown when the
   * control has focus
   */
  private boolean showOnlyOnFocus = false;

  /**
   * Boolean indicating whether the decoration should show its description
   * text in a hover when the user hovers over the decoration.
   */
  private boolean showHover = true;

  /**
   * Margin width used between the decorator and the control.
   */
  private int marginWidth = 0;

  /**
   * Registered selection listeners.
   */
  private ListenerList selectionListeners = new ListenerList();

  /**
   * Registered menu detect listeners.
   */
  private ListenerList menuDetectListeners = new ListenerList();

  /**
   * The focus listener
   */
  private FocusListener focusListener;

  /**
   * The dispose listener
   */
  private DisposeListener disposeListener;

  /**
   * The paint listener installed for drawing the decoration
   */
  private PaintListener paintListener;

  /**
   * The mouse listener installed for tracking the hover
   */
  private MouseTrackListener mouseTrackListener;

  /**
   * The mouse move listener installed for tracking the hover
   */
  private MouseMoveListener mouseMoveListener;

  /**
   * The untyped listener installed for notifying external listeners
   */
  private Listener compositeListener;

  /**
   * Control that we last installed a move listener on. We only want one at a
   * time.
   */
  private Control moveListeningTarget = null;

  /**
   * Debug counter used to match add and remove listeners
   */
  private int listenerInstalls = 0;

  /**
   * The current rectangle used for tracking mouse moves
   */
  private Rectangle decorationRectangle;
 
  /**
   * The rectangle of the previously used image.  Used
   * for redrawing in the case where a smaller image replaces
   * a larger one.
   *
   * @since 3.5
   */
  private Rectangle previousDecorationRectangle;

  /**
   * An internal flag tracking whether we have focus. We use this rather than
   * isFocusControl() so that we can set the flag as soon as we get the focus
   * callback, rather than having to do an asyncExec in the middle of a focus
   * callback to ensure that isFocusControl() represents the outcome of the
   * event.
   */
  private boolean hasFocus = false;

  /**
   * The hover used for showing description text
   */
  private Hover hover;

  /**
   * The hover used to show a decoration image's description.
   */
  class Hover {
    private static final String EMPTY = ""; //$NON-NLS-1$

    /**
     * Offset of info hover arrow from the left or right side.
     */
    private int hao = 10;

    /**
     * Width of info hover arrow.
     */
    private int haw = 8;

    /**
     * Height of info hover arrow.
     */
    private int hah = 10;

    /**
     * Margin around info hover text.
     */
    private int hm = 2;

    /**
     * This info hover's shell.
     */
    Shell hoverShell;

    /**
     * The info hover text.
     */
    String text = EMPTY;

    /**
     * The region used to manage the shell shape
     */
    Region region;

    /**
     * Boolean indicating whether the last computed polygon location had an
     * arrow on left. (true if left, false if right).
     */
    boolean arrowOnLeft = true;

    /*
     * Create a hover parented by the specified shell.
     */
    Hover(Shell parent) {
      final Display display = parent.getDisplay();
      hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP
          | SWT.NO_FOCUS | SWT.TOOL);
      hoverShell.setBackground(display
          .getSystemColor(SWT.COLOR_INFO_BACKGROUND));
      hoverShell.setForeground(display
          .getSystemColor(SWT.COLOR_INFO_FOREGROUND));
      hoverShell.addPaintListener(new PaintListener() {
        public void paintControl(PaintEvent pe) {
          pe.gc.drawText(text, hm, hm);
          if (!MAC) {
            pe.gc.drawPolygon(getPolygon(true));
          }
        }
      });
      hoverShell.addMouseListener(new MouseAdapter() {
        public void mouseDown(MouseEvent e) {
          hideHover();
        }
      });
    }

    /*
     * Compute a polygon that represents a hover with an arrow pointer. If
     * border is true, compute the polygon inset by 1-pixel border. Consult
     * the arrowOnLeft flag to determine which side the arrow is on.
     */
    int[] getPolygon(boolean border) {
      Point e = getExtent();
      int b = border ? 1 : 0;
      if (arrowOnLeft) {
        return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b,
            hao + haw, e.y - b, hao + haw / 2, e.y + hah - b, hao,
            e.y - b, 0, e.y - b, 0, 0 };
      }
      return new int[] { 0, 0, e.x - b, 0, e.x - b, e.y - b,
          e.x - hao - b, e.y - b, e.x - hao - haw / 2, e.y + hah - b,
          e.x - hao - haw, e.y - b, 0, e.y - b, 0, 0 };
    }

    /*
     * Dispose the hover, it is no longer needed. Dispose any resources
     * allocated by the hover.
     */
    void dispose() {
      if (!hoverShell.isDisposed()) {
        hoverShell.dispose();
      }
      if (region != null) {
        region.dispose();
      }
    }

    /*
     * Set the visibility of the hover.
     */
    void setVisible(boolean visible) {
      if (visible) {
        if (!hoverShell.isVisible()) {
          hoverShell.setVisible(true);
        }
      } else {
        if (hoverShell.isVisible()) {
          hoverShell.setVisible(false);
        }
      }
    }

    /*
     * Set the text of the hover to the specified text. Recompute the size
     * and location of the hover to hover near the decoration rectangle,
     * pointing the arrow toward the target control.
     */
    void setText(String t, Rectangle decorationRectangle,
        Control targetControl) {
      if (t == null) {
        t = EMPTY;
      }
      if (!t.equals(text)) {
        Point oldSize = getExtent();
        text = t;
        hoverShell.redraw();
        Point newSize = getExtent();
        if (!oldSize.equals(newSize)) {
          // set a flag that indicates the direction of arrow
          arrowOnLeft = decorationRectangle.x <= targetControl
              .getLocation().x;
          setNewShape();
        }
      }

      Point extent = getExtent();
      int y = -extent.y - hah + 1;
      int x = arrowOnLeft ? -hao + haw / 2 : -extent.x + hao + haw / 2;

      hoverShell.setLocation(control.getParent().toDisplay(
          decorationRectangle.x + x, decorationRectangle.y + y));
    }

    /*
     * Return whether or not the hover (shell) is visible.
     */
    boolean isVisible() {
      return hoverShell.isVisible();
    }

    /*
     * Compute the extent of the hover for the current text.
     */
    Point getExtent() {
      GC gc = new GC(hoverShell);
      Point e = gc.textExtent(text);
      gc.dispose();
      e.x += hm * 2;
      e.y += hm * 2;
      return e;
    }

    /*
     * Compute a new shape for the hover shell.
     */
    void setNewShape() {
      Region oldRegion = region;
      region = new Region();
      region.add(getPolygon(false));
      hoverShell.setRegion(region);
      if (oldRegion != null) {
        oldRegion.dispose();
      }

    }
  }

  /**
   * Construct a ControlDecoration for decorating the specified control at the
   * specified position relative to the control. Render the decoration on top
   * of any Control that happens to appear at the specified location.
   * <p>
   * SWT constants are used to specify the position of the decoration relative
   * to the control. The position should include style bits describing both
   * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
   * <code>SWT.RIGHT</code> describe the horizontal placement of the
   * decoration relative to the control, and the constants
   * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
   * <code>SWT.BOTTOM</code> describe the vertical alignment of the
   * decoration relative to the control. Decorations always appear on either
   * the left or right side of the control, never above or below it. For
   * example, a decoration appearing on the left side of the field, at the
   * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
   * specified, the control decoration will be positioned to the left and
   * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
   * </p>
   *
   * @param control
   *            the control to be decorated
   * @param position
   *            bit-wise or of position constants (<code>SWT.TOP</code>,
   *            <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
   *            <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
   */
  public ControlDecoration(Control control, int position) {
    this(control, position, null);

  }

  /**
   * Construct a ControlDecoration for decorating the specified control at the
   * specified position relative to the control. Render the decoration only on
   * the specified Composite or its children. The decoration will be clipped
   * if it does not appear within the visible bounds of the composite or its
   * child composites.
   * <p>
   * SWT constants are used to specify the position of the decoration relative
   * to the control. The position should include style bits describing both
   * the vertical and horizontal orientation. <code>SWT.LEFT</code> and
   * <code>SWT.RIGHT</code> describe the horizontal placement of the
   * decoration relative to the control, and the constants
   * <code>SWT.TOP</code>, <code>SWT.CENTER</code>, and
   * <code>SWT.BOTTOM</code> describe the vertical alignment of the
   * decoration relative to the control. Decorations always appear on either
   * the left or right side of the control, never above or below it. For
   * example, a decoration appearing on the left side of the field, at the
   * top, is specified as SWT.LEFT | SWT.TOP. If no position style bits are
   * specified, the control decoration will be positioned to the left and
   * center of the control (<code>SWT.LEFT | SWT.CENTER</code>).
   * </p>
   *
   * @param control
   *            the control to be decorated
   * @param position
   *            bit-wise or of position constants (<code>SWT.TOP</code>,
   *            <code>SWT.BOTTOM</code>, <code>SWT.LEFT</code>,
   *            <code>SWT.RIGHT</code>, and <code>SWT.CENTER</code>).
   * @param composite
   *            The SWT composite within which the decoration should be
   *            rendered. The decoration will be clipped to this composite,
   *            but it may be rendered on a child of the composite. The
   *            decoration will not be visible if the specified composite or
   *            its child composites are not visible in the space relative to
   *            the control, where the decoration is to be rendered. If this
   *            value is <code>null</code>, then the decoration will be
   *            rendered on whichever composite (or composites) are located in
   *            the specified position.
   */
  public ControlDecoration(Control control, int position, Composite composite) {
    this.position = position;
    this.control = control;
    this.composite = composite;

    addControlListeners();

  }

  /**
   * Adds the listener to the collection of listeners who will be notified
   * when the platform-specific context menu trigger has occurred, by sending
   * it one of the messages defined in the <code>MenuDetectListener</code>
   * interface.
   * <p>
   * The <code>widget</code> field in the SelectionEvent will contain the
   * Composite on which the decoration is rendered that received the click.
   * The <code>x</code> and <code>y</code> fields will be in coordinates
   * relative to the display. The <code>data</code> field will contain the
   * decoration that received the event.
   * </p>
   *
   * @param listener
   *            the listener which should be notified
   *
   * @see org.eclipse.swt.events.MenuDetectListener
   * @see org.eclipse.swt.events.MenuDetectEvent
   * @see #removeMenuDetectListener
   */
  public void addMenuDetectListener(MenuDetectListener listener) {
    menuDetectListeners.add(listener);
  }

  /**
   * Removes the listener from the collection of listeners who will be
   * notified when the platform-specific context menu trigger has occurred.
   *
   * @param listener
   *            the listener which should no longer be notified. This message
   *            has no effect if the listener was not previously added to the
   *            receiver.
   *
   * @see org.eclipse.swt.events.MenuDetectListener
   * @see #addMenuDetectListener
   */
  public void removeMenuDetectListener(MenuDetectListener listener) {
    menuDetectListeners.remove(listener);
  }

  /**
   * Adds the listener to the collection of listeners who will be notified
   * when the decoration is selected, by sending it one of the messages
   * defined in the <code>SelectionListener</code> interface.
   * <p>
   * <code>widgetSelected</code> is called when the decoration is selected
   * (by mouse click). <code>widgetDefaultSelected</code> is called when the
   * decoration is double-clicked.
   * </p>
   * <p>
   * The <code>widget</code> field in the SelectionEvent will contain the
   * Composite on which the decoration is rendered that received the click.
   * The <code>x</code> and <code>y</code> fields will be in coordinates
   * relative to that widget. The <code>data</code> field will contain the
   * decoration that received the event.
   * </p>
   *
   * @param listener
   *            the listener which should be notified
   *
   * @see org.eclipse.swt.events.SelectionListener
   * @see org.eclipse.swt.events.SelectionEvent
   * @see #removeSelectionListener
   */
  public void addSelectionListener(SelectionListener listener) {
    selectionListeners.add(listener);
  }

  /**
   * Removes the listener from the collection of listeners who will be
   * notified when the decoration is selected.
   *
   * @param listener
   *            the listener which should no longer be notified. This message
   *            has no effect if the listener was not previously added to the
   *            receiver.
   *
   * @see org.eclipse.swt.events.SelectionListener
   * @see #addSelectionListener
   */
  public void removeSelectionListener(SelectionListener listener) {
    selectionListeners.remove(listener);
  }

  /**
   * Dispose this ControlDecoration. Unhook any listeners that have been
   * installed on the target control. This method has no effect if the
   * receiver is already disposed.
   */
  public void dispose() {
    if (control == null) {
      return;
    }
    if (hover != null) {
      hover.dispose();
      hover = null;
    }
    removeControlListeners();
    control = null;
  }

  /**
   * Get the control that is decorated by the receiver.
   *
   * @return the Control decorated by the receiver. May be <code>null</code>
   *         if the control has been uninstalled.
   */
  public Control getControl() {
    return control;
  }

  /**
   * Add any listeners needed on the target control and on the composite where
   * the decoration is to be rendered.
   */
  private void addControlListeners() {
    disposeListener = new DisposeListener() {
      public void widgetDisposed(DisposeEvent event) {
        dispose();
      }
    };
    printAddListener(control, "DISPOSE"); //$NON-NLS-1$
    control.addDisposeListener(disposeListener);

    focusListener = new FocusListener() {
      public void focusGained(FocusEvent event) {
        hasFocus = true;
        if (showOnlyOnFocus) {
          update();
        }
      }

      public void focusLost(FocusEvent event) {
        hasFocus = false;
        if (showOnlyOnFocus) {
          update();
        }
      }
    };
    printAddListener(control, "FOCUS"); //$NON-NLS-1$
    control.addFocusListener(focusListener);

    // Listener for painting the decoration
    paintListener = new PaintListener() {
      public void paintControl(PaintEvent event) {
        Control control = (Control) event.widget;
        Rectangle rect = getDecorationRectangle(control);
        if (shouldShowDecoration()) {
          event.gc.drawImage(getImage(), rect.x, rect.y);
        }
      }
    };

    // Listener for tracking the end of a hover. Only installed
    // after a hover begins.
    mouseMoveListener = new MouseMoveListener() {
      public void mouseMove(MouseEvent event) {
        if (showHover) {
          if (!decorationRectangle.contains(event.x, event.y)) {
            hideHover();
            // No need to listen any longer
            printRemoveListener(event.widget, "MOUSEMOVE"); //$NON-NLS-1$
            ((Control) event.widget)
                .removeMouseMoveListener(mouseMoveListener);
            moveListeningTarget = null;
          }
        }
      }
    };

    // Listener for tracking the beginning of a hover. Always installed.
    mouseTrackListener = new MouseTrackListener() {
      public void mouseExit(MouseEvent event) {
        // Just in case we didn't catch it before.
        Control target = (Control) event.widget;
        if (target == moveListeningTarget) {
          printRemoveListener(target, "MOUSEMOVE"); //$NON-NLS-1$
          target.removeMouseMoveListener(mouseMoveListener);
          moveListeningTarget = null;
        }
        hideHover();
      }

      public void mouseHover(MouseEvent event) {
        if (showHover) {
          decorationRectangle = getDecorationRectangle((Control) event.widget);
          if (decorationRectangle.contains(event.x, event.y)) {
            showHoverText(getDescriptionText());
            Control target = (Control) event.widget;
            if (moveListeningTarget == null) {
              printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
              target.addMouseMoveListener(mouseMoveListener);
              moveListeningTarget = target;
            } else if (target != moveListeningTarget) {
              printRemoveListener(moveListeningTarget,
                  "MOUSEMOVE"); //$NON-NLS-1$
              moveListeningTarget
                  .removeMouseMoveListener(mouseMoveListener);
              printAddListener(target, "MOUSEMOVE"); //$NON-NLS-1$
              target.addMouseMoveListener(mouseMoveListener);
              moveListeningTarget = target;
            } else {
              // It is already installed on this control.
            }
          }
        }
      }

      public void mouseEnter(MouseEvent event) {
        // Nothing to do until a hover occurs.
      }
    };

    compositeListener = new Listener() {
      public void handleEvent(Event event) {
        // Don't forward events if decoration is not showing
        if (!visible) {
          return;
        }
        // Notify listeners if any are registered.
        switch (event.type) {
        case SWT.MouseDown:
          if (!selectionListeners.isEmpty())
            notifySelectionListeners(event);
          break;
        case SWT.MouseDoubleClick:
          if (!selectionListeners.isEmpty())
            notifySelectionListeners(event);
          break;
        case SWT.MenuDetect:
          if (!menuDetectListeners.isEmpty())
            notifyMenuDetectListeners(event);
          break;
        }
      }
    };

    // We do not know which parent in the control hierarchy
    // is providing the decoration space, so hook all the way up, until
    // the shell or the specified parent composite is reached.
    Composite c = control.getParent();
    while (c != null) {
      installCompositeListeners(c);
      if (composite != null && composite == c) {
        // We just installed on the specified composite, so stop.
        c = null;
      } else if (c instanceof Shell) {
        // We just installed on a shell, so don't go further
        c = null;
      } else {
        c = c.getParent();
      }
    }
    // force a redraw of the decoration area so our paint listener
    // is notified.
    update();
  }

  /*
   * Install the listeners used to paint and track mouse events on the
   * composite.
   */
  private void installCompositeListeners(Composite c) {
    if (!c.isDisposed()) {
      printAddListener(c, "PAINT"); //$NON-NLS-1$
      c.addPaintListener(paintListener);
      printAddListener(c, "MOUSETRACK"); //$NON-NLS-1$
      c.addMouseTrackListener(mouseTrackListener);
      printAddListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
      c.addListener(SWT.MenuDetect, compositeListener);
      printAddListener(c, "SWT.MouseDown"); //$NON-NLS-1$
      c.addListener(SWT.MouseDown, compositeListener);
      printAddListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
      c.addListener(SWT.MouseDoubleClick, compositeListener);
    }
  }

  /*
   * Remove the listeners used to paint and track mouse events on the
   * composite.
   */
  private void removeCompositeListeners(Composite c) {
    if (!c.isDisposed()) {
      printRemoveListener(c, "PAINT"); //$NON-NLS-1$
      c.removePaintListener(paintListener);
      printRemoveListener(c, "MOUSETRACK"); //$NON-NLS-1$
      c.removeMouseTrackListener(mouseTrackListener);
      printRemoveListener(c, "SWT.MenuDetect"); //$NON-NLS-1$
      c.removeListener(SWT.MenuDetect, compositeListener);
      printRemoveListener(c, "SWT.MouseDown"); //$NON-NLS-1$
      c.removeListener(SWT.MouseDown, compositeListener);
      printRemoveListener(c, "SWT.MouseDoubleClick"); //$NON-NLS-1$
      c.removeListener(SWT.MouseDoubleClick, compositeListener);
    }
  }

  private void notifySelectionListeners(Event event) {
    if (!(event.widget instanceof Control)) {
      return;
    }
    if (getDecorationRectangle((Control) event.widget).contains(event.x,
        event.y)) {
      SelectionEvent clientEvent = new SelectionEvent(event);
      clientEvent.data = this;
      if (getImage() != null) {
        clientEvent.height = getImage().getBounds().height;
        clientEvent.width = getImage().getBounds().width;
      }
      Object[] listeners;
      switch (event.type) {
      case SWT.MouseDoubleClick:
        if (event.button == 1) {
          listeners = selectionListeners.getListeners();
          for (int i = 0; i < listeners.length; i++) {
            ((SelectionListener) listeners[i])
                .widgetDefaultSelected(clientEvent);
          }
        }
        break;
      case SWT.MouseDown:
        if (event.button == 1) {
          listeners = selectionListeners.getListeners();
          for (int i = 0; i < listeners.length; i++) {
            ((SelectionListener) listeners[i])
                .widgetSelected(clientEvent);
          }
        }
        break;
      }
    }
  }

  private void notifyMenuDetectListeners(Event event) {
    if (getDecorationRectangle(null).contains(event.x, event.y)) {
      MenuDetectEvent clientEvent = new MenuDetectEvent(event);
      clientEvent.data = this;
      Object[] listeners = menuDetectListeners.getListeners();
      for (int i = 0; i < listeners.length; i++) {
        ((MenuDetectListener) listeners[i]).menuDetected(clientEvent);

      }
    }
  }

  /**
   * Show the specified text using the same hover dialog as is used to show
   * decorator descriptions. When {@link #setShowHover(boolean)} has been set
   * to <code>true</code>, a decoration's description text will be shown in
   * an info hover over the field's control whenever the mouse hovers over the
   * decoration. This method can be used to show a decoration's description
   * text at other times (such as when the control receives focus), or to show
   * other text associated with the field. The hover will not be shown if the
   * decoration is hidden.
   *
   * @param text
   *            the text to be shown in the info hover, or <code>null</code>
   *            if no text should be shown.
   */
  public void showHoverText(String text) {
    if (control == null) {
      return;
    }
    showHoverText(text, control);
  }

  /**
   * Hide any hover popups that are currently showing on the control. When
   * {@link #setShowHover(boolean)} has been set to <code>true</code>, a
   * decoration's description text will be shown in an info hover over the
   * field's control as long as the mouse hovers over the decoration, and will
   * be hidden when the mouse exits the decoration. This method can be used to
   * hide a hover, whether it was shown explicitly using
   * {@link #showHoverText(String)}, or was showing because the user was
   * hovering in the decoration.
   * <p>
   * This message has no effect if there is no current hover.
   *
   */
  public void hideHover() {
    if (hover != null) {
      hover.setVisible(false);
    }
  }

  /**
   * Show the control decoration. This message has no effect if the decoration
   * is already showing. If {@link #setShowOnlyOnFocus(boolean)} is set to
   * <code>true</code>, the decoration will only be shown if the control
   * has focus.
   */
  public void show() {
    if (!visible) {
      visible = true;
      update();
    }
  }

  /**
   * Hide the control decoration and any associated hovers. This message has
   * no effect if the decoration is already hidden.
   */
  public void hide() {
    if (visible) {
      visible = false;
      hideHover();
      update();
    }
  }

  /**
   * Get the description text that may be shown in a hover for this
   * decoration.
   *
   * @return the text to be shown as a description for the decoration, or
   *         <code>null</code> if none has been set.
   */
  public String getDescriptionText() {
    return descriptionText;
  }

  /**
   * Set the description text that may be shown in a hover for this
   * decoration. Update the rendered decoration.
   *
   * @param text
   *            the text to be shown as a description for the decoration, or
   *            <code>null</code> if none has been set.
   */
  public void setDescriptionText(String text) {
    this.descriptionText = text;
    update();
  }

  /**
   * Get the image shown in this control decoration.
   *
   * @return the image to be shown adjacent to the control, or
   *         <code>null</code> if one has not been set.
   */
  public Image getImage() {
    return image;
  }

  /**
   * Set the image shown in this control decoration. Update the rendered
   * decoration.
   *
   * @param image
   *            the image to be shown adjacent to the control. Should never be
   *            <code>null</code>.
   */
  public void setImage(Image image) {
    previousDecorationRectangle = getDecorationRectangle(control.getShell());
    this.image = image;
    update();
  }

  /**
   * Get the boolean that controls whether the decoration is shown only when
   * the control has focus. The default value of this setting is
   * <code>false</code>.
   *
   * @return <code>true</code> if the decoration should only be shown when
   *         the control has focus, and <code>false</code> if it should
   *         always be shown. Note that if the control is not capable of
   *         receiving focus (<code>SWT.NO_FOCUS</code>), then the
   *         decoration will never show when this value is <code>true</code>.
   */
  public boolean getShowOnlyOnFocus() {
    return showOnlyOnFocus;
  }

  /**
   * Set the boolean that controls whether the decoration is shown only when
   * the control has focus. The default value of this setting is
   * <code>false</code>.
   *
   * @param showOnlyOnFocus
   *            <code>true</code> if the decoration should only be shown
   *            when the control has focus, and <code>false</code> if it
   *            should always be shown. Note that if the control is not
   *            capable of receiving focus (<code>SWT.NO_FOCUS</code>),
   *            then the decoration will never show when this value is
   *            <code>true</code>.
   */
  public void setShowOnlyOnFocus(boolean showOnlyOnFocus) {
    this.showOnlyOnFocus = showOnlyOnFocus;
    update();
  }

  /**
   * Get the boolean that controls whether the decoration's description text
   * should be shown in a hover when the user hovers over the decoration. The
   * default value of this setting is <code>true</code>.
   *
   * @return <code>true</code> if a hover popup containing the decoration's
   *         description text should be shown when the user hovers over the
   *         decoration, and <code>false</code> if a hover should not be
   *         shown.
   */
  public boolean getShowHover() {
    return showHover;
  }

  /**
   * Set the boolean that controls whether the decoration's description text
   * should be shown in a hover when the user hovers over the decoration. The
   * default value of this setting is <code>true</code>.
   *
   * @param showHover
   *            <code>true</code> if a hover popup containing the
   *            decoration's description text should be shown when the user
   *            hovers over the decoration, and <code>false</code> if a
   *            hover should not be shown.
   */
  public void setShowHover(boolean showHover) {
    this.showHover = showHover;
    update();
  }

  /**
   * Get the margin width in pixels that should be used between the decorator
   * and the horizontal edge of the control. The default value of this setting
   * is <code>0</code>.
   *
   * @return the number of pixels that should be reserved between the
   *         horizontal edge of the control and the adjacent edge of the
   *         decoration.
   */
  public int getMarginWidth() {
    return marginWidth;
  }

  /**
   * Set the margin width in pixels that should be used between the decorator
   * and the horizontal edge of the control. The default value of this setting
   * is <code>0</code>.
   *
   * @param marginWidth
   *            the number of pixels that should be reserved between the
   *            horizontal edge of the control and the adjacent edge of the
   *            decoration.
   */
  public void setMarginWidth(int marginWidth) {
    previousDecorationRectangle = getDecorationRectangle(control.getShell());
    this.marginWidth = marginWidth;
    update();
  }

  /**
   * Something has changed, requiring redraw. Redraw the decoration and update
   * the hover text if appropriate.
   */
  protected void update() {
    if (control == null || control.isDisposed()) {
      return;
    }
    Rectangle rect = getDecorationRectangle(control.getShell());
    // If this update is happening due to an image reset, we need to make
    // sure we clear the area from the old image.
    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=212501
    if (previousDecorationRectangle != null) {
      rect = rect.union(previousDecorationRectangle);
    }
    // Redraw this rectangle in all children
    control.getShell()
        .redraw(rect.x, rect.y, rect.width, rect.height, true);
    control.getShell().update();
    if (hover != null && getDescriptionText() != null) {
      hover.setText(getDescriptionText(), getDecorationRectangle(control
          .getParent()), control);
    }
    previousDecorationRectangle = null;
  }

  /*
   * Show the specified text in the hover, positioning the hover near the
   * specified control. The hover will only be shown if the control is
   * visible.
   *
   * The caller has already established that the control is not null.
   */
  private void showHoverText(String text, Control hoverNear) {
    // We do not show the hover if the control is disposed or
    // invisible.
    if (control.isDisposed() || !control.isVisible())
      return;

    // If we aren't to show a hover, don't do anything.
    if (!showHover) {
      return;
    }

    // If we are not visible, don't show the hover.
    if (!visible) {
      return;
    }
    // If there is no text, any existing hover should be hidden, and
    // there is nothing more to do.
    if (text == null || text.length() == 0) {
      hideHover();
      return;
    }
   
    // Now we can hover!
    // Create the hover if it's not already showing
    if (hover == null) {
      hover = new Hover(hoverNear.getShell());
    }
    hover.setText(text, getDecorationRectangle(control.getParent()),
        control);
    hover.setVisible(true);
  }

  /*
   * Remove any listeners installed on the controls.
   */
  private void removeControlListeners() {
    if (control == null) {
      return;
    }
    printRemoveListener(control, "FOCUS"); //$NON-NLS-1$
    control.removeFocusListener(focusListener);
    focusListener = null;

    printRemoveListener(control, "DISPOSE"); //$NON-NLS-1$
    control.removeDisposeListener(disposeListener);
    disposeListener = null;

    Composite c = control.getParent();
    while (c != null) {
      removeCompositeListeners(c);
      if (composite != null && composite == c) {
        // We previously installed listeners only to the specified
        // composite, so stop.
        c = null;
      } else if (c instanceof Shell) {
        // We previously installed listeners only up to the first Shell
        // encountered, so stop.
        c = null;
      } else {
        c = c.getParent();
      }
    }
    paintListener = null;
    mouseTrackListener = null;
    compositeListener = null;

    // We may have a remaining mouse move listener installed
    if (moveListeningTarget != null) {
      printRemoveListener(moveListeningTarget, "MOUSEMOVE"); //$NON-NLS-1$
      moveListeningTarget.removeMouseMoveListener(mouseMoveListener);
      moveListeningTarget = null;
      mouseMoveListener = null;
    }
    if (DEBUG) {
      if (listenerInstalls > 0) {
        System.out.println("LISTENER LEAK>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
      } else if (listenerInstalls < 0) {
        System.out
            .println("REMOVED UNREGISTERED LISTENERS>>>CHECK TRACE ABOVE"); //$NON-NLS-1$
      } else {
        System.out.println("ALL INSTALLED LISTENERS WERE REMOVED."); //$NON-NLS-1$
      }
    }
  }

  /**
   * Return the rectangle in which the decoration should be rendered, in
   * coordinates relative to the specified control. If the specified control
   * is null, return the rectangle in display coordinates.
   *
   * @param targetControl
   *            the control whose coordinates should be used
   * @return the rectangle in which the decoration should be rendered
   */
  protected Rectangle getDecorationRectangle(Control targetControl) {
    if (getImage() == null || control == null) {
      return new Rectangle(0, 0, 0, 0);
    }
    // Compute the bounds first relative to the control's parent.
    Rectangle imageBounds = getImage().getBounds();
    Rectangle controlBounds = control.getBounds();
    int x, y;
    // Compute x
    if ((position & SWT.RIGHT) == SWT.RIGHT) {
      x = controlBounds.x + controlBounds.width + marginWidth;
    } else {
      // default is left
      x = controlBounds.x - imageBounds.width - marginWidth;
    }
    // Compute y
    if ((position & SWT.TOP) == SWT.TOP) {
      y = controlBounds.y;
    } else if ((position & SWT.BOTTOM) == SWT.BOTTOM) {
      y = controlBounds.y + control.getBounds().height
          - imageBounds.height;
    } else {
      // default is center
      y = controlBounds.y
          + (control.getBounds().height - imageBounds.height) / 2;
    }

    // Now convert to coordinates relative to the target control.
    Point globalPoint = control.getParent().toDisplay(x, y);
    Point targetPoint;
    if (targetControl == null) {
      targetPoint = globalPoint;
    } else {
      targetPoint = targetControl.toControl(globalPoint);
    }
    return new Rectangle(targetPoint.x, targetPoint.y, imageBounds.width,
        imageBounds.height);
  }

  /*
   * Return true if the decoration should be shown, false if it should not.
   */
  private boolean shouldShowDecoration() {
    if (!visible) {
      return false;
    }
    if (control == null || control.isDisposed() || getImage() == null) {
      return false;
    }

    if (!control.isVisible()) {
      return false;
    }
    if (showOnlyOnFocus) {
      return hasFocus;
    }
    return true;
  }

  /*
   * If in debug mode, print info about adding the specified listener.
   */
  private void printAddListener(Widget widget, String listenerType) {
    listenerInstalls++;
    if (DEBUG) {
      System.out
          .println("Added listener>>>" + listenerType + " to>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$
    }
  }

  /*
   * If in debug mode, print info about adding the specified listener.
   */
  private void printRemoveListener(Widget widget, String listenerType) {
    listenerInstalls--;
    if (DEBUG) {
      System.out
          .println("Removed listener>>>" + listenerType + " from>>>" + widget); //$NON-NLS-1$//$NON-NLS-2$
    }
  }
 

  /**
   * Return a boolean indicating whether the decoration is visible. This
   * method considers the visibility state of the decoration (
   * {@link #hide()} and {@link #show()}), the visibility state of the
   * associated control ({@link Control#isVisible()}), and the focus state
   * of the control if applicable ({@link #setShowOnlyOnFocus(boolean)}.
   * When this method returns <code>true</code>, it means that the decoration
   * should be visible. However, this method does not consider the case where
   * the decoration should be visible, but is obscured by another window or
   * control, or positioned off the screen. In these cases, the decoration
   * will still be considered visible.
   *
   * @return <code>true</code> if the decoration is visible, and
   *         <code>false</code> if it is not.
   *
   * @see #setShowOnlyOnFocus(boolean)
   * @see #hide()
   * @see #show()
     *
   * @since 3.6
   */
  public boolean isVisible() {
    return shouldShowDecoration();
  }
}
TOP

Related Classes of org.eclipse.jface.fieldassist.ControlDecoration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.