/******************************************************************************* * <copyright> * * Copyright (c) 2005, 2013 SAP AG. * 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: * SAP AG - initial API, implementation and documentation * Bug 336488 - DiagramEditor API * mwenz - Bug 373298 - Possible Resource leaks in Graphiti * fvelasco - Bug 396247 - ImageDescriptor changes * mwenz - Bug 397303 - Accessibility issue with Graphiti diagram in High Contrast Mode * pjpaulin - Bug 352120 - Now uses IDiagramContainerUI interface * * </copyright> * *******************************************************************************/ package org.eclipse.graphiti.ui.internal.contextbuttons; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.XYLayout; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.EditPart; import org.eclipse.graphiti.internal.contextbuttons.IContextButtonPadDeclaration; import org.eclipse.graphiti.internal.contextbuttons.IContextButtonPadDeclaration.PadStyle; import org.eclipse.graphiti.internal.contextbuttons.PositionedContextButton; import org.eclipse.graphiti.ui.editor.DiagramBehavior; import org.eclipse.graphiti.ui.internal.IResourceRegistry; import org.eclipse.graphiti.ui.internal.figures.GFFigureUtil; import org.eclipse.graphiti.ui.internal.util.DataTypeTransformation; import org.eclipse.graphiti.util.IColorConstant; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseTrackAdapter; import org.eclipse.swt.events.MouseTrackListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Path; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; /** * A Shape depicting a context button pad. The context button pad contains * several {@link ContextButton} as children. The visual definition of the * context button pad is provided mostly by the * {@link IContextButtonPadDeclaration}, which is set in the constructor, and * not calculated in this class. * * @noinstantiate This class is not intended to be instantiated by clients. * @noextend This class is not intended to be subclassed by clients. */ public class ContextButtonPad extends Shape implements ITransparencyProvider { /** * The default duration of the animation in milliseconds. */ private static final int DEFAULT_ANIMATION_DURATION = 200; /** * The start transparency of the animation in the range 0.0 to 1.0. */ private static final double ANIMATION_START_TRANSPARENCY = 0.0; /** * The end transparency of the animation in the range 0.0 to 1.0. */ private static final double ANIMATION_END_TRANSPARENCY = 1.0; /** * The context button pad declaration as described in * {@link #getDeclaration()}. */ private IContextButtonPadDeclaration declaration; /** * The zoom-level as described in {@link #getZoomLevel()}. */ private double zoomLevel; /** * The container as described in {@link #getContainer()}. */ private DiagramBehavior diagramBehavior; /** * The edit-part as described in {@link #getEditPart()}. */ private EditPart editPart; private Path pathOuterLine; private Path pathMiddleLine; private Path pathInnerLine; private Path pathFill; private List<Rectangle> containmentRectangles; private List<Rectangle> overlappingContainmentRectangles; private double currentTransparency = ANIMATION_END_TRANSPARENCY; private long animationDuration = DEFAULT_ANIMATION_DURATION; // ============================= listener ================================= /** * The mouse move listener hides the context button pad, when the mouse * leaves the 'mouse relevant' area of the context button pad (see * {@link #isMouseInOverlappingArea()}). */ private MouseMoveListener mouseMoveListener = new MouseMoveListener() { public void mouseMove(MouseEvent e) { if (!isMouseInOverlappingArea()) { getContextButtonManagerForPad().hideContextButtonsInstantly(); } } }; /** * The mouse track listener hides the context button pad, when the mouse * leaves the diagram control (e.g. when the mouse goes to the top-menu or * other views/editors). */ private MouseTrackListener mouseTrackListener = new MouseTrackAdapter() { @Override public void mouseExit(MouseEvent e) { // TODO: discuss with Christian // This functionality will hide the context button pad, whenever the // mouse leaves the // diagram control (e.g. when the mouse goes to the top-menu or // other views/editors). // Unfortunately tooltips are also other controls, so the context // button pad would // be hidden, if the mouse is on the tooltip of the shape or of the // buttons. // Both behaviors are reasonable, but for now we decided not to hide // the // context button pad when the mouse leaves the control // getContextButtonManager().hideContextButtonsInstantly(); } }; private IResourceRegistry resourceRegistry; private ContextButtonManagerForPad contextButtonManagerForPad; // ============================ constructors ============================== /** * Creates a new ContextButtonPad and calls {@link #initialize()}. * * @param contextButtonManagerForPad * * @param declaration * The context button pad declaration as described in * {@link #getDeclaration()}. * @param zoomLevel * The zoom-level as described in {@link #getZoomLevel()}. * @param container * The container as described in {@link #getContainer()}. * @param editPart * The edit-part as described in {@link #getEditPart()}. * @param resourceRegistry */ public ContextButtonPad(ContextButtonManagerForPad contextButtonManagerForPad, IContextButtonPadDeclaration declaration, double zoomLevel, DiagramBehavior diagramBehavior, EditPart editPart, IResourceRegistry resourceRegistry) { this.declaration = declaration; this.zoomLevel = zoomLevel; this.diagramBehavior = diagramBehavior; this.editPart = editPart; this.resourceRegistry = resourceRegistry; this.contextButtonManagerForPad = contextButtonManagerForPad; initialize(); } // ====================== getter/setter for fields ======================== /** * Returns the declaration, which provides the visual definition and the * context buttons for this context button pad. It is set in the constructor * and not changed afterwards. * * @return The declaration, which provides the visual definition and the * context buttons for this context button pad. */ public final IContextButtonPadDeclaration getDeclaration() { return declaration; } /** * Returns the zoom-level, which shall be used when rendering the context * button pad. It is set in the constructor and not changed afterwards. The * values provided by the declaration (see {@link #getDeclaration()}) are * always using a zoom-level 1.0, so they have to be adjusted by the * zoom-level. * * @return The zoom-level, which shall be used when rendering the context * button pad. */ public final double getZoomLevel() { return zoomLevel; } /** * Returns the container for which the context button pad belongs. It can be * used to access the environment. It is set in the constructor and not * changed afterwards. * * @return The container, which can be used to access the environment. */ public final DiagramBehavior getDiagramBehavior() { return diagramBehavior; } /** * Returns the edit-part for which the context button pad is showing. It is * used by the context buttons, which work on/with the edit-part. It is set * in the constructor and not changed afterwards. * * @return The container, which can be used to access the environment. */ public final EditPart getEditPart() { return editPart; } // =========================== initialization ============================= /** * Initializes several fields of this class. This method is called in the * constructor and calls {@link #initializePathes()}, * {@link #initializeContainments()} and {@link #createContextButtons()}. */ private void initialize() { initializeContainments(); createContextButtons(); } /** * Initializes the lists {@link #containmentRectangles} and * {@link #overlappingContainmentRectangles}, by transforming the rectangles * provided by the declaration (see {@link #getDeclaration()}) using * {@link #transformGenericRectangle(java.awt.Rectangle, int)}. Then it sets * the bounds of this figure dependent on the * {@link #overlappingContainmentRectangles}. */ private void initializeContainments() { containmentRectangles = new ArrayList<Rectangle>(); overlappingContainmentRectangles = new ArrayList<Rectangle>(); for (java.awt.Rectangle rectangle : getDeclaration().getContainmentRectangles()) { Rectangle transformedRectangle = transformGenericRectangle(rectangle, 0); containmentRectangles.add(transformedRectangle); } for (java.awt.Rectangle rectangle : getDeclaration().getOverlappingContainmentRectangles()) { Rectangle transformedRectangle = transformGenericRectangle(rectangle, 0); overlappingContainmentRectangles.add(transformedRectangle); } if (overlappingContainmentRectangles.size() > 0) { // always true Rectangle unionRectangle = overlappingContainmentRectangles.get(0).getCopy(); for (Rectangle containmentRectangle : overlappingContainmentRectangles) { unionRectangle.union(containmentRectangle); } unionRectangle.expand(2, 2); // expand slightly to avoid rounding // problems setBounds(unionRectangle); } } /** * Creates a visual {@link ContextButton} for each logical * {@link PositionedContextButton}, which are provided by the declaration * {@link #getDeclaration()}. Those context buttons are then added to the * context button pad at the correct location. */ private void createContextButtons() { List<PositionedContextButton> positionedButtons = getDeclaration().getPositionedContextButtons(); setLayoutManager(new XYLayout()); for (PositionedContextButton positionedButton : positionedButtons) { Rectangle position = transformGenericRectangle(positionedButton.getPosition(), 0); // translate position relative to bounds (after the bounds are set!) position.translate(-getBounds().getTopLeft().x, -getBounds().getTopLeft().y); ContextButton cb = new ContextButton(diagramBehavior.getDiagramTypeProvider().getProviderId(), positionedButton, this); add(cb, position); } } IResourceRegistry getResourceRegistry() { return resourceRegistry; } /** * Initializes the fields for the paths {@link #pathOuterLine}, * {@link #pathMiddleLine}, {@link #pathInnerLine} and {@link #pathFill}. * This is done by calling {@link #createPath(int)} with ascending values * for 'shrink-lines'. As a result those four paths all have the same * outline, but each path becomes one line smaller so that they lie directly * in each other. */ private void createPathes() { pathOuterLine = createPath(0); pathMiddleLine = createPath(1); pathInnerLine = createPath(2); pathFill = createPath(3); } /** * Disposes all paths, which were created in {@link #createPathes()}. */ private void disposePathes() { pathOuterLine.dispose(); pathMiddleLine.dispose(); pathInnerLine.dispose(); pathFill.dispose(); pathOuterLine = null; pathMiddleLine = null; pathInnerLine = null; pathFill = null; } /** * Creates and returns a path outlining the context button pad. The path * includes the three rectangles top, right and bottom, which are provided * by the declaration (see {@link #getDeclaration()}). A parameter * shrink-lines is used to shrink the path by the given number of lines to * the inside. * <p> * Basically this method just forwards to * {@link #createPath(Rectangle, Rectangle, Rectangle, int)}, but before it * adjusts the rectangles and corner by the zoom-level and shrink-lines. * * @param shrinkLines * The number of lines to shrink the path to the inside. * @return A path outlining the context button pad. */ private Path createPath(int shrinkLines) { double zoom = getZoomLevel(); int lw = shrinkLines * ((int) (getDeclaration().getPadLineWidth() * zoom)); // adjust corner for the inner path (formula found by experimenting) double corner = (getDeclaration().getPadCornerRadius() * zoom); corner = Math.max(1, corner - (lw + corner / 64)); Rectangle topAdjusted = transformGenericRectangle(getDeclaration().getTopPad(), shrinkLines); Rectangle rightAdjusted = transformGenericRectangle(getDeclaration().getRightPad(), shrinkLines); Rectangle bottomAdjusted = transformGenericRectangle(getDeclaration().getBottomPad(), shrinkLines); Path path = createPath(topAdjusted, rightAdjusted, bottomAdjusted, (int) corner); return path; } /** * Creates and returns a path including the given rectangles and uses the * given corner-radius. Note, that all those values are already adjusted to * the given zoom-level and 'shrink-lines' (see {@link #createPath(int)}). * * @param topOutside * The outside of the top rectangle to include. The path remains * inside the rectangle even for a big line-width. * @param rightOutside * The outside of the right rectangle to include. The path * remains inside the rectangle even for a big line-width. * @param bottomOutside * The outside of the bottom rectangle to include. The path * remains inside the rectangle even for a big line-width. * @param corner * The corner radius to use for the path. * @return A path surrounding the given rectangles and uses the given * corner-radius. */ private Path createPath(Rectangle topOutside, Rectangle rightOutside, Rectangle bottomOutside, int corner) { Path path = new Path(null); // currently we assume, that the inner corner radius is always half the // outer corner radius int innerCorner = corner / 2; // first shrink all rectangles by the half line-width, so that painting // remains inside the given 'outside' rectangles Rectangle top = GFFigureUtil.getAdjustedRectangle(topOutside, 1.0, (int) (getDeclaration().getPadLineWidth() * getZoomLevel())); Rectangle right = GFFigureUtil.getAdjustedRectangle(rightOutside, 1.0, (int) (getDeclaration() .getPadLineWidth() * getZoomLevel())); Rectangle bottom = GFFigureUtil.getAdjustedRectangle(bottomOutside, 1.0, (int) (getDeclaration() .getPadLineWidth() * getZoomLevel())); // differenciate the pad styles boolean hasTop = top != null; boolean hasRight = right != null; boolean hasStandardTop = hasTop && getDeclaration().getTopPadStyle().equals(PadStyle.STANDARD); boolean hasStandardRight = hasRight && getDeclaration().getRightPadStyle().equals(PadStyle.STANDARD); boolean hasAppendageTop = hasTop && getDeclaration().getTopPadStyle().equals(PadStyle.APPENDAGE); boolean hasAppendageRight = hasRight && getDeclaration().getRightPadStyle().equals(PadStyle.APPENDAGE); // create path if (hasStandardTop) { // curved line around top(top-right) -> top(top-left) -> // top(bottom-left) path.addArc(top.getTopRight().x - corner, top.getTopRight().y, corner, corner, 0, 90); path.addArc(top.getTopLeft().x, top.getTopLeft().y, corner, corner, 90, 90); path.addArc(top.getBottomLeft().x, top.getBottomLeft().y - corner, corner, corner, 180, 90); } else if (hasAppendageTop) { // curved line around top(top-left) -> top(bottom-left) int appendageCorner = Math.min(corner, top.height * 2); // adjust // for small // sizes path.addArc(top.getTopLeft().x, top.getTopLeft().y, appendageCorner, appendageCorner, 90, 90); path.lineTo(top.getBottomLeft().x, top.getBottomLeft().y); } else { // !hasTop // curved line around right(top-left) path.addArc(right.getTopLeft().x, right.getTopLeft().y, corner, corner, 90, 90); } if (hasTop && hasRight) { // inside open curve connecting top and right path.addArc(right.getLeft().x - innerCorner, top.getBottom().y, innerCorner, innerCorner, 90, -90); } if (hasStandardRight) { if (bottom == null) { // curved line around right(bottom-left) path.addArc(right.getBottomLeft().x, right.getBottomLeft().y - corner, corner, corner, 180, 90); } else { // inside open curve connection right and bottom path.addArc(right.getLeft().x - innerCorner, bottom.getTop().y - innerCorner, innerCorner, innerCorner, 0, -90); // curved line around bottom(top-left) -> bottom(bottom-left) -> // bottom(bottom-right) path.addArc(bottom.getTopLeft().x, bottom.getTopLeft().y, corner, corner, 90, 90); path.addArc(bottom.getBottomLeft().x, bottom.getBottomLeft().y - corner, corner, corner, 180, 90); path.addArc(bottom.getBottomRight().x - corner, bottom.getBottomRight().y - corner, corner, corner, 270, 90); // outside open curve connection bottom and right path.addArc(bottom.getRight().x, right.getBottom().y, corner, corner, 180, -90); } // curved line around right(bottom-right) -> right(top-right) path.addArc(right.getBottomRight().x - corner, right.getBottomRight().y - corner, corner, corner, 270, 90); path.addArc(right.getTopRight().x - corner, right.getTopRight().y, corner, corner, 0, 90); } else if (hasAppendageRight) { // curved line around right(bottom-left) -> right(bottom-right) int appendageCorner = Math.min(corner, right.width * 2); // adjust // for // small // sizes path.lineTo(right.getBottomLeft().x, right.getBottomLeft().y); path.addArc(right.getBottomRight().x - appendageCorner, right.getBottomRight().y - appendageCorner, appendageCorner, appendageCorner, 270, 90); } else { // !hasRight // close curved rectangle around top (bottom-right) path.addArc(top.getBottomRight().x - corner, top.getBottomRight().y - corner, corner, corner, 270, 90); } if (hasStandardTop && hasStandardRight) { // outside open curve connecting right and top (appendages have // direct line) path.addArc(top.getRight().x, right.getTop().y - corner, corner, corner, 270, -90); } path.close(); return path; } /** * Returns a rectangle, which is calculating from the given rectangle by * shrinking it the given number of lines and scaling it with the * zoom-level. Note, that this method also makes a transformation from * java.awt.Rectangle to org.eclipse.draw2d.geometry.Rectangle. * * @param source * The source rectangle from which to calculate the result. * @param shrinkLines * The number of lines to shrink the rectangle. * @return A rectangle, which is calculating from the given rectangle by * shrinking it the given number of lines and scaling it with the * zoom-level. */ private Rectangle transformGenericRectangle(java.awt.Rectangle source, int shrinkLines) { if (source == null) { return null; } double zoom = getZoomLevel(); int lw = shrinkLines * ((int) (getDeclaration().getPadLineWidth() * zoom)); Rectangle target = new Rectangle(source.x, source.y, source.width, source.height); target.scale(zoom); // shrink, but take care not to end up with a negative width or height int widthShrink = Math.min(target.width / 2, lw); int heightShrink = Math.min(target.height / 2, lw); target.shrink(widthShrink, heightShrink); return target; } // =========================== event handling ============================= /** * Registers the listeners, when the context button pad is shown (when it is * added to its parent). */ @Override public void addNotify() { super.addNotify(); Control control = diagramBehavior.getDiagramContainer().getGraphicalViewer().getControl(); control.addMouseMoveListener(mouseMoveListener); control.addMouseTrackListener(mouseTrackListener); // TODO: discuss with Christian // If the animation is running, then dragging the shape does not work // correctly. The dragging is interrupted, when the context button pad // disappears. // It is still unclear why this happens. // Workaround: switch off animation or set a very short animation time // Another problem is, that the pad sometimes (rarely) flickers, // which becomes more visible with short animation times. // doAnimation(); } /** * Deregisters the listeners, when the context button pad is hidden (when it * is removed from its parent). */ @Override public void removeNotify() { Control control = diagramBehavior.getDiagramContainer().getGraphicalViewer().getControl(); control.removeMouseMoveListener(mouseMoveListener); control.removeMouseTrackListener(mouseTrackListener); super.removeNotify(); // Notify the ContextButton children of this pad to free its resources // (holds a font). Introduced to fix bug 373298 List<?> childrenList = getChildren(); for (Object object : childrenList) { if (object instanceof ContextButton) { ((ContextButton) object).dispose(); } } } // ============================== painting ================================ /** * Outlines this Shape on the given Graphics. This will draw the paths * {@link #pathInnerLine}, {@link #pathMiddleLine} and * {@link #pathOuterLine}. * * @param graphics * The Graphics on which to outline this Shape. */ @Override protected void outlineShape(Graphics graphics) { int lw = (int) (getZoomLevel() * getDeclaration().getPadLineWidth()); graphics.setLineWidth(lw); Color innerLineSwtColor; Color middleLineSwtColor; Color outerLineSwtColor; Display display = Display.getCurrent(); if (display == null) { display = Display.getDefault(); } if (display.getHighContrast()) { innerLineSwtColor = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND); middleLineSwtColor = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND); outerLineSwtColor = display.getSystemColor(SWT.COLOR_WIDGET_FOREGROUND); } else { IColorConstant padInnerLineColor = getDeclaration().getPadInnerLineColor(); innerLineSwtColor = DataTypeTransformation.toSwtColor(resourceRegistry, padInnerLineColor); IColorConstant padMiddleLineColor = getDeclaration().getPadMiddleLineColor(); middleLineSwtColor = DataTypeTransformation.toSwtColor(resourceRegistry, padMiddleLineColor); IColorConstant padOuterLineColor = getDeclaration().getPadOuterLineColor(); outerLineSwtColor = DataTypeTransformation.toSwtColor(resourceRegistry, padOuterLineColor); } graphics.setForegroundColor(innerLineSwtColor); graphics.drawPath(pathInnerLine); graphics.setForegroundColor(middleLineSwtColor); graphics.drawPath(pathMiddleLine); graphics.setForegroundColor(outerLineSwtColor); graphics.drawPath(pathOuterLine); } /** * First initializes the given Graphics with settings like alpha-value, * antialias-value, ... Afterwards calls * <code>super.paintFigure(graphics)</code> to continue with the default * painting mechanisms. * * @param graphics * The Graphics on which to paint. */ @Override public void paintFigure(Graphics graphics) { graphics.setAntialias(SWT.ON); // double padDefaultOpacity = getDeclaration().getPadDefaultOpacity(); // int endAlpha = (int) (padDefaultOpacity * 255.0); graphics.setAlpha((int) (currentTransparency * getDeclaration().getPadDefaultOpacity() * 255)); createPathes(); super.paintFigure(graphics); disposePathes(); } /** * Fills this Shape on the given Graphics. This will draw and fill the path * {@link #pathFill}. * * @param graphics * The Graphics on which to fill this Shape. */ @Override protected void fillShape(Graphics graphics) { int lw = (int) (getZoomLevel() * getDeclaration().getPadLineWidth()); graphics.setLineWidth(lw); Color swtColor; Display display = Display.getCurrent(); if (display == null) { display = Display.getDefault(); } if (display.getHighContrast()) { swtColor = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND); } else { IColorConstant padFillColor = getDeclaration().getPadFillColor(); swtColor = DataTypeTransformation.toSwtColor(resourceRegistry, padFillColor); } graphics.setForegroundColor(swtColor); graphics.setBackgroundColor(swtColor); graphics.drawPath(pathFill); graphics.fillPath(pathFill); } /** * Increases the current transparency (see {@link #getCurrentTransparency()} * ) in a loop and forces a repaint after each increase. As a result this * will seem to the user as if the context button pad slowly 'fades in' from * invisible to visible. */ @SuppressWarnings("unused") private void doAnimation() { // set start values for animation long startTime = System.currentTimeMillis(); long stepsDone = 0; currentTransparency = ANIMATION_START_TRANSPARENCY; while (isPadShowing() && (currentTransparency < ANIMATION_END_TRANSPARENCY)) { // repaint context button pad with current transparency forceRepaint(); stepsDone++; // increase current transparency (self adjusting algorithm) long elapsedTime = Math.max(1, System.currentTimeMillis() - startTime); long restTime = Math.max(1, animationDuration - elapsedTime); double restSteps = (((double) (stepsDone * restTime)) / elapsedTime); double deltaTransparency = (ANIMATION_END_TRANSPARENCY - currentTransparency) / restSteps; currentTransparency += deltaTransparency; if (elapsedTime > animationDuration) { // safeguard in case of // rounding errors currentTransparency = ANIMATION_END_TRANSPARENCY; } } // finally paint using the end transparency currentTransparency = ANIMATION_END_TRANSPARENCY; forceRepaint(); } /** * Forces a repaint of this figure, by first calling repaint() and then * waiting until all UI events are processed. */ private void forceRepaint() { if (isPadShowing()) { repaint(); while (Display.getCurrent().readAndDispatch()) { // do nothing } } } // ========================== helper methods ============================== /** * Returns true, if the mouse is in the overlapping area of the context * button pad. The overlapping area is the union of all overlapping * rectangles (see * {@link IContextButtonPadDeclaration#getOverlappingContainmentRectangles()} * ). */ public boolean isMouseInOverlappingArea() { // determine mouse location in correct coordinates Point editorMouseLocation = new Point(diagramBehavior.getMouseLocation()); Point viewPortMouseLocation = diagramBehavior.calculateRealMouseLocation(editorMouseLocation); Point mouseLocation = viewPortMouseLocation.scale(getZoomLevel()); // hide if mouse location outside overlapping containment rectangles boolean containsPointOverlapping = containsPointOverlapping(mouseLocation.x, mouseLocation.y); if (!containsPointOverlapping) { getContextButtonManagerForPad().hideContextButtonsInstantly(); return true; } return containsPointOverlapping; } public ContextButtonManagerForPad getContextButtonManagerForPad() { return contextButtonManagerForPad; } /** * Returns true, if the given point is contained inside one of the visible * parts of the context button pad. Note, that this is a much smaller area * than the bounds of this shape, because the bounds are the outer invisible * rectangle around all visible parts of the context button pad. * <p> * Technically this is the union of all containment rectangles (see * {@link IContextButtonPadDeclaration#getContainmentRectangles()}). * * @param x * The x-coordinate of the point to check. * @param y * The y-coordinate of the point to check. * @return true, if the given point is contained inside one of the visible * parts of the context button pad. */ @Override public boolean containsPoint(int x, int y) { boolean ret = false; for (Rectangle rectangle : containmentRectangles) { if (rectangle.contains(x, y)) { ret = true; break; } } return ret; } /** * Returns true, if the given point is contained inside the overlapping area * of all visible parts of the context button pad. Note, that this is a much * smaller area than the bounds of this shape, because the bounds are the * outer invisible rectangle around all visible parts of the context button * pad. * * @param x * The x-coordinate of the point to check. * @param y * The y-coordinate of the point to check. * @return true, if the given point is contained inside the overlapping area * of all visible parts of the context button pad. */ public boolean containsPointOverlapping(int x, int y) { boolean ret = false; for (Rectangle rectangle : overlappingContainmentRectangles) { if (rectangle.contains(x, y)) { ret = true; break; } } return ret; } /** * Returns the current transparency as described in * {@link ITransparencyProvider}. * * @return The current transparency as described in * {@link ITransparencyProvider}. */ public double getCurrentTransparency() { return currentTransparency; } /** * Returns true, if the pad is currently showing. * * @return true, if the pad is currently showing. */ private boolean isPadShowing() { // returns true in the time, between addNotify() and removeNotify() boolean hasParent = getParent() != null; return hasParent; } }