Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (c) 2011 Bernhard Pauler, Tim Molderez.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the 3-Clause BSD License
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/BSD-3-Clause
*/
package net.java.balloontip;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.java.balloontip.positioners.BalloonTipPositioner;
import net.java.balloontip.positioners.BasicBalloonTipPositioner;
import net.java.balloontip.positioners.LeftAbovePositioner;
import net.java.balloontip.positioners.LeftBelowPositioner;
import net.java.balloontip.positioners.RightAbovePositioner;
import net.java.balloontip.positioners.RightBelowPositioner;
import net.java.balloontip.styles.BalloonTipStyle;
import net.java.balloontip.styles.RoundedBalloonStyle;
/**
* A balloon tip Swing component that is attached to a JComponent and uses another JComponent as contents
* @author Bernhard Pauler
* @author Tim Molderez
* @author Thierry Blind
*/
public class BalloonTip extends JPanel {
/** Should the balloon be placed above, below, right or left of the attached component? */
public enum Orientation {LEFT_ABOVE, RIGHT_ABOVE, LEFT_BELOW, RIGHT_BELOW}
/** Where should the balloon's tip be located, relative to the attached component
* ; ALIGNED makes sure the balloon's edge is aligned with the attached component */
public enum AttachLocation {ALIGNED, CENTER, NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST}
protected JComponent contents = null;
protected JButton closeButton = null;
protected VisibilityControl visibilityControl = new VisibilityControl();
protected BalloonTipStyle style; // Determines the balloon's looks
protected int padding = 0; // Amount of pixels padding around the contents
protected float opacity = 1.0f; // The balloon tip's opacity (1 is opaque)
protected BalloonTipPositioner positioner; // Determines the balloon tip's position
protected JLayeredPane topLevelContainer = null; // The balloon tip is drawn on this pane
protected JComponent attachedComponent; // The balloon tip is attached to this component
private static Icon defaultCloseIcon = new ImageIcon(BalloonTip.class.getResource("/net/java/balloontip/images/close_default.png"));
private static Icon rolloverCloseIcon = new ImageIcon(BalloonTip.class.getResource("/net/java/balloontip/images/close_rollover.png"));
private static Icon pressedCloseIcon = new ImageIcon(BalloonTip.class.getResource("/net/java/balloontip/images/close_pressed.png"));
// Only show a balloon tip when the component it's attached to is visible
private final ComponentListener componentListener = new ComponentListener() {
public void componentMoved(ComponentEvent e) {
refreshLocation();
}
public void componentResized(ComponentEvent e) {
visibilityControl.setCriteriumAndUpdate("attachedComponentShowing",isAttachedComponentShowing());
refreshLocation();
}
public void componentShown(ComponentEvent e) {
visibilityControl.setCriteriumAndUpdate("attachedComponentShowing",isAttachedComponentShowing());
refreshLocation();
}
public void componentHidden(ComponentEvent e) {
visibilityControl.setCriteriumAndUpdate("attachedComponentShowing",false);
}
};
// Adjust the balloon tip when the top-level container is resized
private final ComponentAdapter topLevelContainerListener = new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
refreshLocation();
}
};
// Adjust the balloon tip's visibility when switching tabs
private ComponentAdapter tabbedPaneListener = null;
// Hide the balloon tip when its tip is outside a viewport
private NestedViewportListener viewportListener = null;
// Behaviour when the balloon tip is clicked
private MouseAdapter clickListener = null;
/**
* Constructor
* The simplest constructor, a balloon tip with some text and a default look
* @param attachedComponent attach the balloon tip to this component (may not be null)
* @param text the contents of the balloon tip (may contain HTML)
*/
public BalloonTip(JComponent attachedComponent, String text) {
this(attachedComponent, text, new RoundedBalloonStyle(5,5,Color.WHITE, Color.BLACK), true);
}
/**
* Constructor
* A simple constructor for a balloon tip containing text, a custom look and optionally a close button
* @param attachedComponent attach the balloon tip to this component (may not be null)
* @param text the contents of the balloon tip (may contain HTML)
* @param style the balloon tip's looks (may not be null)
* @param useCloseButton if true, the balloon tip gets a default close button
*/
public BalloonTip(JComponent attachedComponent, String text, BalloonTipStyle style, boolean useCloseButton) {
this(attachedComponent, new JLabel(text), style, useCloseButton);
}
/**
* Constructor
* @param attachedComponent attach the balloon tip to this component (may not be null)
* @param contents the balloon tip's contents (may be null)
* @param style the balloon tip's looks (may not be null)
* @param useCloseButton if true, the balloon tip gets a close button
*/
public BalloonTip(JComponent attachedComponent, JComponent contents, BalloonTipStyle style, boolean useCloseButton) {
this(attachedComponent, contents, style, Orientation.LEFT_ABOVE, AttachLocation.ALIGNED, 15, 15, useCloseButton);
}
/**
* Constructor
* @param attachedComponent attach the balloon tip to this component (may not be null)
* @param contents the balloon tip's contents (may be null)
* @param style the balloon tip's looks (may not be null)
* @param orientation orientation of the balloon tip
* @param attachLocation location of the balloon's tip within the attached component
* @param horizontalOffset horizontal offset for the balloon's tip
* @param verticalOffset vertical offset for the balloon's tip
* @param useCloseButton if true, the balloon tip gets a close button
*/
public BalloonTip(final JComponent attachedComponent, final JComponent contents, final BalloonTipStyle style, Orientation orientation, AttachLocation attachLocation,
int horizontalOffset, int verticalOffset, final boolean useCloseButton) {
super();
// Set up the appropriate positioner
BasicBalloonTipPositioner positioner = null;
float attachX = 0.0f;
float attachY = 0.0f;
boolean fixedAttachLocation = true;
switch (attachLocation) {
case ALIGNED:
fixedAttachLocation = false;
break;
case CENTER:
attachX = 0.5f;
attachY = 0.5f;
break;
case NORTH:
attachX = 0.5f;
break;
case NORTHEAST:
attachX = 1.0f;
break;
case EAST:
attachX = 1.0f;
attachY = 0.5f;
break;
case SOUTHEAST:
attachX = 1.0f;
attachY = 1.0f;
break;
case SOUTH:
attachX = 0.5f;
attachY = 1.0f;
break;
case SOUTHWEST:
attachY = 1.0f;
break;
case WEST:
attachY = 0.5f;
break;
case NORTHWEST:
break;
}
switch (orientation) {
case LEFT_ABOVE:
positioner = new LeftAbovePositioner(horizontalOffset, verticalOffset);
break;
case LEFT_BELOW:
positioner = new LeftBelowPositioner(horizontalOffset, verticalOffset);
break;
case RIGHT_ABOVE:
positioner = new RightAbovePositioner(horizontalOffset, verticalOffset);
break;
case RIGHT_BELOW:
positioner = new RightBelowPositioner(horizontalOffset, verticalOffset);
break;
}
positioner.enableFixedAttachLocation(fixedAttachLocation);
positioner.setAttachLocation(attachX, attachY);
setup(attachedComponent, contents, style, positioner,
useCloseButton?getDefaultCloseButton():null);
}
/**
* Constructor - the most customizable balloon tip constructor
* @param attachedComponent attach the balloon tip to this component (may not be null)
* @param contents the contents of the balloon tip (may be null)
* @param style the balloon tip's looks (may not be null)
* @param positioner determines the way the balloon tip is positioned (may not be null)
* @param closeButton the close button to be used for the balloon tip (may be null)
*/
public BalloonTip(JComponent attachedComponent, JComponent contents, BalloonTipStyle style, BalloonTipPositioner positioner, JButton closeButton) {
super();
setup(attachedComponent, contents, style, positioner, closeButton);
}
/**
* Sets the contents of this balloon tip
* (Calling this method will fire a "contents" property change event.)
* @param contents a JComponent that represents the balloon tip's contents
* If the contents is null, the balloon tip will not be shown
*/
public void setContents(JComponent contents) {
JComponent oldContents = this.contents;
if (oldContents!=null) {
remove(this.contents);
}
this.contents=contents;
if (contents!=null) {
setPadding(getPadding());
add(this.contents, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
visibilityControl.setCriteriumAndUpdate("hasContents", true);
} else {
visibilityControl.setCriteriumAndUpdate("hasContents", false);
}
// Notify property listeners that the style has changed
firePropertyChange("contents", oldContents, this.contents);
refreshLocation();
}
/**
* Retrieve this balloon tip's contents
* @return the JComponent representing the contents of this balloon tip (can be null)
*/
public JComponent getContents() {
return this.contents;
}
/**
* Set the amount of padding in this balloon tip
* (by attaching an empty border to the balloon tip's contents...)
* @param padding the amount of padding in pixels
*/
public void setPadding(int padding) {
this.padding=padding;
contents.setBorder(BorderFactory.createEmptyBorder(padding, padding, padding, padding));
refreshLocation();
}
/**
* Get the amount of padding in this balloon tip
* @return the amount of padding in pixels
*/
public int getPadding() {
return padding;
}
/**
* Set the balloon tip's style
* (Calling this method will fire a "style" property change event.)
* @param style a BalloonTipStyle (may not be null)
*/
public void setStyle(BalloonTipStyle style) {
BalloonTipStyle oldStyle = this.style;
this.style = style;
setBorder(this.style);
// Notify property listeners that the style has changed
firePropertyChange("style", oldStyle, style);
refreshLocation();
}
/**
* Get the balloon tip's style
* @return the balloon tip's style
*/
public BalloonTipStyle getStyle() {
return style;
}
/**
* Set a new BalloonTipPositioner, repsonsible for the balloon tip's positioning
* (Calling this method will fire a "positioner" property change event.)
* @param positioner a BalloonTipPositioner (may not be null)
*/
public void setPositioner(BalloonTipPositioner positioner) {
BalloonTipPositioner oldPositioner = this.positioner;
this.positioner = positioner;
this.positioner.setBalloonTip(this);
// Notify property listeners that the positioner has changed
firePropertyChange("positioner", oldPositioner, positioner);
refreshLocation();
}
/**
* Retrieve the BalloonTipPositioner that is used by this balloon tip
* @return The balloon tip's positioner
*/
public BalloonTipPositioner getPositioner() {
return positioner;
}
/**
* If you want to permanently close the balloon, you can use this method.
* (It will be called automatically once Java's garbage collector can clean up this balloon tip...)
* Please note, you shouldn't use this instance anymore after calling this method!
* (If you just want to hide the balloon tip, simply use setVisible(false);)
*/
public void closeBalloon() {
forceSetVisible(false);
setCloseButton(null); // Remove the close button
for(MouseListener m : getMouseListeners()) {
removeMouseListener(m);
}
tearDownHelper();
}
/**
* Sets this balloon tip's close button (which will call closeBalloon() when clicked)
* You don't need add any behaviour to the button yourself; this is added for you.
* @param button the new close button; if null, the balloon tip's close button is removed (if it had one)
*/
public void setCloseButton (JButton button) {
setCloseButton(button, true, false);
}
/**
* Sets this balloon tip's close button
* @param button the new close button; if null, the balloon tip's close button is removed (if it had one)
* @param permanentClose if true, the default behaviour of the button is to close the balloon tip permanently by calling closeBalloon()
* if false, the default behaviour is to just hide the balloon tip by calling setVisible(false)
* @param noDefault if true, no default behaviour is added and you'll have to set it yourself
*/
public void setCloseButton (JButton button, boolean permanentClose, boolean noDefault) {
// Remove the current button
if (closeButton!=null) {
for (ActionListener a: closeButton.getActionListeners()) {
closeButton.removeActionListener(a);
}
remove(closeButton);
closeButton = null;
}
// Set the new button
if (button!=null) {
closeButton = button;
if (!noDefault && permanentClose) {
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
closeBalloon();
}
});
} else if (!noDefault && !permanentClose) {
closeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
}
add(closeButton, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
}
refreshLocation();
}
/**
* Retrieve this balloon tip's close button
* @return the close button (null if not present)
*/
public JButton getCloseButton() {
return closeButton;
}
/**
* Creates a default close button (without any behaviour)
* @return the close button
*/
public static JButton getDefaultCloseButton() {
JButton button = new JButton();
button.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
button.setContentAreaFilled(false);
button.setIcon(defaultCloseIcon);
button.setRolloverIcon(rolloverCloseIcon);
button.setPressedIcon(pressedCloseIcon);
return button;
}
/**
* Set the icons for the default close button
* (This only affects balloon tips created after calling this method.)
* @param normal regular icon
* @param pressed icon when clicked
* @param rollover icon when hovering over the button
*/
public static void setDefaultCloseButtonIcons(Icon normal, Icon pressed, Icon rollover) {
defaultCloseIcon = normal;
rolloverCloseIcon = rollover;
pressedCloseIcon = pressed;
}
/**
* Adds a mouse listener that will close this balloon tip when clicked.
* @param permanentClose if true, the default behaviour is to close the balloon tip permanently by calling closeBalloon()
* if false, the default behaviour is to just hide the balloon tip by calling setVisible(false)
*/
public void addDefaultMouseListener(boolean permanentClose) {
removeMouseListener(clickListener);
if (permanentClose) {
clickListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
e.consume();
closeBalloon();
}
};
} else {
clickListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
e.consume();
setVisible(false);
}
};
}
addMouseListener(clickListener);
}
/**
* Change the component this balloon tip is attached to
* (The top-level container will be re-determined during this process;
* if you set it manually, you'll have to set it again...)
* (Calling this method will fire an "attachedComponent" property change event.)
* @param newComponent the new component to attach to (may not be null)
* @exception NullPointerException if parameter newComponent is null
*/
public void setAttachedComponent(JComponent newComponent) {
JComponent oldComponent = this.attachedComponent;
tearDownHelper(); // Remove any listeners related to the old attached component
this.attachedComponent = newComponent;
setupHelper(); // Reinstall the listeners
// Notify property listeners that the style has changed
firePropertyChange("attachedComponent", oldComponent, attachedComponent);
refreshLocation();
}
/**
* Retrieve the component this balloon tip is attached to
* @return The attached component
*/
public JComponent getAttachedComponent() {
return attachedComponent;
}
/**
* Set the container on which this balloon tip should be drawn
* @param tlc the top-level container; must be valid (isValid() must return true) ()
* (may not be null)
*/
public void setTopLevelContainer(JLayeredPane tlc) {
if (topLevelContainer != null) {
topLevelContainer.remove(this);
topLevelContainer.removeComponentListener(topLevelContainerListener);
}
this.topLevelContainer = tlc;
// If the window is resized, we should check if the balloon still fits
topLevelContainer.addComponentListener(topLevelContainerListener);
// We use the popup layer of the top level container (frame or dialog) to show the balloon tip
topLevelContainer.add(this, JLayeredPane.POPUP_LAYER);
}
/**
* Retrieve the container this balloon tip is drawn on
* If the balloon tip hasn't determined this container yet, null is returned
* @return The balloon tip's top level container
*/
public JLayeredPane getTopLevelContainer() {
return topLevelContainer;
}
/**
* Retrieves the rectangle to which this balloon tip is attached
* @return the rectangle to which this balloon tip is attached, in the coordinate system of the balloon tip
*/
public Rectangle getAttachedRectangle() {
Point location = SwingUtilities.convertPoint(attachedComponent, getLocation(), this);
return new Rectangle(location.x, location.y, attachedComponent.getWidth(), attachedComponent.getHeight());
}
/**
* Refreshes the balloon tip's location
* (Is able to update balloon tip's location even if the balloon tip is not shown.)
*/
public void refreshLocation() {
if (topLevelContainer!=null) {
positioner.determineAndSetLocation(getAttachedRectangle());
}
}
/**
* Sets the opacity of this balloon tip and repaints it
* Note: Setting the opacity to 0 won't make isVisible() return false.
* @param opacity the opacity, where 0.0f is completely invisible and 1.0f is opaque
*/
public void setOpacity(float opacity) {
this.opacity = opacity;
repaint();
}
/**
* Get the opacity of this balloon tip
* @return the opacity, where 0.0f is completely invisible and 1.0f is opaque
*/
public float getOpacity() {
return this.opacity;
}
public void paintComponent(Graphics g) {
if (opacity!=1.0f) {
((Graphics2D) g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
}
super.paintComponent(g);
}
/**
* Set this balloon tip's visibility
* @param visible visible if true (and if the listeners associated with this balloon tip have no reason to hide the balloon tip!
* For example, it makes no sense to show balloon tip if the component it's attached to is hidden...); invisible otherwise
*/
public void setVisible(boolean visible) {
visibilityControl.setCriteriumAndUpdate("manual", visible);
}
protected void finalize() throws Throwable {
closeBalloon(); // This will remove all of the listeners a balloon tip uses...
super.finalize();
}
/*
* Sets the balloon tip's visibility by calling super.setVisible()
* (This bypasses the balloon tip's visibility control.)
* @param visible true if the balloon tip should be visible
*/
protected void forceSetVisible(boolean visible) {
super.setVisible(visible);
}
/*
* Helper method that checks whether the attached component is visible or not
* (i.e. its area is greater than 0 and it really is visible..)
* @return true if the component is showing; false otherwise
*/
protected boolean isAttachedComponentShowing() {
return attachedComponent.isShowing()
&& attachedComponent.getWidth() > 0
&& attachedComponent.getHeight() > 0; // The area of the attached component must be > 0 in order to be visible..
}
/*
* Helper method for constructing a BalloonTip
*/
private void setup(final JComponent attachedComponent, JComponent contents, BalloonTipStyle style, BalloonTipPositioner positioner, JButton closeButton) {
this.attachedComponent = attachedComponent;
this.contents = contents;
this.style = style;
this.positioner = positioner;
positioner.setBalloonTip(this);
setBorder(this.style);
setOpaque(false);
setLayout(new GridBagLayout());
setPadding(4);
add(this.contents, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
setCloseButton(closeButton);
// Don't allow to click 'through' the balloon tip
clickListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {e.consume();}
};
addMouseListener(clickListener);
// Attempt to run setupHelper() ...
if (attachedComponent.isValid()) {
setupHelper();
refreshLocation();
} else {
/* We can't determine the top-level container yet.
* We'll just have to wait until the parent is set and try again... */
final AncestorListener attachedComponentParentListener = new AncestorListener() {
public void ancestorAdded(AncestorEvent e) {
setupHelper();
refreshLocation();
e.getComponent().removeAncestorListener(this); // Remove yourself
}
public void ancestorMoved(AncestorEvent e) {}
public void ancestorRemoved(AncestorEvent e) {}
};
attachedComponent.addAncestorListener(attachedComponentParentListener);
}
}
/*
* Helper method for setup() and changeAttachedComponent()
*/
private void setupHelper() {
// Set the pane on which the balloon tip is drawn
if (topLevelContainer == null) {
setTopLevelContainer(attachedComponent.getRootPane().getLayeredPane());
}
// If the attached component is moved/hidden/shown, the balloon tip should act accordingly
attachedComponent.addComponentListener(componentListener);
// Update balloon tip's visibility
forceSetVisible(this.isVisible() && isAttachedComponentShowing());
// Follow the path of parent components to see if there are any we should listen to
Container current = attachedComponent.getParent();
Container previous = attachedComponent;
while (current!=null) {
if (current instanceof JTabbedPane) {
/* Switching tabs only tells the JPanel representing the contents of each tab whether it went invisible or not.
* It doesn't propagate such events to each and every component within each tab.
* Because of this, we'll have to add a listener to the JPanel of this tab. If it goes invisible, so should the balloon tip. */
if (tabbedPaneListener == null) {
tabbedPaneListener = getTabbedPaneListener();
}
previous.addComponentListener(tabbedPaneListener);
} else if (current instanceof JViewport) {
if (viewportListener == null) {
viewportListener = new NestedViewportListener();
}
viewportListener.viewports.add((JViewport) current);
((JViewport) current).addChangeListener(viewportListener);
} else if (current instanceof BalloonTip) {
/* In the rare case where this balloon tip is attached to a component within another balloon tip... */
// Monitor the parent balloon tip's movements and visibility
current.addComponentListener(componentListener);
// Draw this balloon tip one layer higher; otherwise it would be overlapping the parent balloon tip
topLevelContainer.setLayer(this, JLayeredPane.getLayer(this) + 1);
// Quit the loop here; any other parent components that should be listened to are meant for the parent balloon tip
break;
}
previous = current;
current = current.getParent();
}
}
/*
* Helper method for closeBalloon() and changeAttachedComponent()
* Removes a number of listeners attached to the balloon tip.
*/
private void tearDownHelper() {
attachedComponent.removeComponentListener(componentListener);
// Remove any listeners that were attached to parent components
if (tabbedPaneListener!=null || viewportListener!=null) {
Container current = attachedComponent.getParent();
Container previous = attachedComponent;
while (current!=null) {
if (current instanceof JTabbedPane) {
previous.removeComponentListener(tabbedPaneListener);
} else if (current instanceof JViewport) {
((JViewport) current).removeChangeListener(viewportListener);
} else if (current instanceof BalloonTip) {
current.removeComponentListener(componentListener);
break;
}
previous = current;
current = current.getParent();
}
tabbedPaneListener = null;
}
if (topLevelContainer != null) {
topLevelContainer.remove(this);
topLevelContainer.removeComponentListener(topLevelContainerListener);
topLevelContainer = null;
}
if (viewportListener!=null) {
viewportListener.viewports.clear();
viewportListener = null;
}
// Clean up our criterias
visibilityControl.criteria.clear();
}
/*
* Creates a Component Listener that will adjust this balloon tip's visibility when switching tabs
* @return the tabbed pane listener
*/
private ComponentAdapter getTabbedPaneListener() {
return new ComponentAdapter() {
public void componentShown(ComponentEvent e) {
visibilityControl.setCriteriumAndUpdate("tabShowing",true);
/* We must also recheck whether the attached component is visible!
* While this tab *was* invisible, the component might've been resized, hidden, shown, ... ,
* but no events were fired because the tab was hidden! */
visibilityControl.setCriteriumAndUpdate("attachedComponentShowing",isAttachedComponentShowing());
refreshLocation();
}
public void componentHidden(ComponentEvent e) {
visibilityControl.setCriteriumAndUpdate("tabShowing",false);
}
};
}
/*
* If a balloon tip is nested in one or more viewports, this listener ensures
* the balloon tip is hidden if it is no longer visible within the viewports' boundaries
*/
private class NestedViewportListener implements ChangeListener {
private Vector viewports = new Vector();
public void stateChanged(ChangeEvent e) {
refreshLocation();
Point tipLocation = positioner.getTipLocation();
for (JViewport viewport:viewports) {
Rectangle view = new Rectangle(SwingUtilities.convertPoint(viewport, viewport.getLocation(), getTopLevelContainer()), viewport.getSize());
if (tipLocation.y >= view.y-1 // -1 because we still want to allow balloons that are attached to the very top...
&& tipLocation.y <= (view.y + view.height)
&& (tipLocation.x) >= view.x
&& (tipLocation.x) <= (view.x + view.width)) {
visibilityControl.setCriteriumAndUpdate("withinViewport", true);
} else {
visibilityControl.setCriteriumAndUpdate("withinViewport", false);
break;
}
}
}
}
/*
* Manages and controls when a balloon tip should be shown or hidden
*/
protected class VisibilityControl {
private HashMap criteria = new HashMap(); // A list of criteria determining a balloon tip's visibility
/**
* Sets the value of a particular visibility criterium and checks whether the balloon tip should still be visible or not
* @param criterium the visibility criterium
* @param value value of the criterium
*/
public void setCriteriumAndUpdate(String criterium, Boolean value) {
criteria.put(criterium, value);
update();
}
/**
* Makes sure the balloon tip's visibility is updated by checking all visibility criteria
* If any of the visibility criteria is false, the balloon tip should be invisible.
* Only if all criteria are true, the balloon tip can be visible.
*/
public void update() {
Iterator i = criteria.values().iterator();
while (i.hasNext()) {
if (!i.next()) {
forceSetVisible(false);
return;
}
}
forceSetVisible(true);
}
}
}