Open source Java projects: Balloontip for Java
You can save time and reduce other costs associated with Java
development by taking advantage of open source Java projects. In this
new series, Jeff Friesen introduces a variety of lesser known open
source Java projects that can benefit your Java software. Jeff begins
the series by looking at Bernhard Pauler's balloontip project, which you
can use to add Windows XP-style informational "balloons" to your
Swing-based Java applications.
| Open source licenses |
| Open
source software is usually subject to some form of license, which
determines how the software can be used. The balloontip project is
released under the GNU Lesser General Public License (LGPL). See this
article's Resources section to learn more about the LGPL. |
A balloon tip is a small window that presents an informational
message to remind or otherwise notify application users of significant
events. Its name is derived from the fact that balloon tips have a
similar appearance to the speech balloons found in comic strips.
Windows XP supports balloon tips in the context of its taskbar
notification area and this area's icons. You can see this in Figure 1,
where XP displays a balloon tip above and partly over a notification
area icon after something significant has happened.
The balloon tip in Figure 1 is positioned in such a way as to identify
its associated notification area icon. The figure also reveals a Close
button to close the balloon tip window, and a small "i" icon, which
classifies the balloon tip as informational.

Figure 1. This balloon tip identifies a new high-speed Internet connection
Bernhard Pauler's open source balloontip project
introduces XP-like balloon tips to Swing GUIs. In this first article in
the new "Open source Java projects" series, I'll show you how to obtain
and install the balloontip software. I'll also walk you through
balloontip's example application, explore the balloontip API, and
explain where balloon tips are useful in Swing-based Java development.
Obtain and install balloontip
The balloontip project's Balloon Tips for Java page, hosted on Java.net,
| A shorter path home |
| On my XP platform, C:\unzipped\BalloonTip_2007-05-15\BalloonTip was the initial path to the BalloonTip
home directory. Because I like to work at the command line, I found it
tedious to navigate this path. To remedy this situation, I moved the
home directory to my C:\jp (Java Projects) directory, resulting in C:\jp\BalloonTip as a simpler path. If you also like to work at the command line, you might want to do something similar on your platform. |
introduces balloontip. Among other things, you'll find a link to the latest version of the software on this page.
At the time of writing this article, the latest version available is BalloonTip_2007-05-15.zip. After downloading and unzipping this archive, I discovered a BalloonTip_2007-05-15 directory containing BalloonTip as the home directory.
The BalloonTip home directory contains classes and src subdirectories. It also contains README.txt, which briefly describes how to run the BalloonTipTestDrive example application. The complete directory structure is shown in Listing 1.
Listing 1. Balloontip project directory structure
BalloonTip
classes
net
java
balloontip
examples
BalloonTipTestDrive$1.class
BalloonTipTestDrive$2.class
BalloonTipTestDrive$3.class
BalloonTipTestDrive.class
images
closebutton_default.png
closebutton_pressed.png
closebutton_rollover.png
frameicon.png
infoicon.gif
utils
WindowUtils.class
BalloonTip$1.class
BalloonTip$2.class
BalloonTip$3.class
BalloonTip.class
EdgedBalloonBorder.class
RoundedBalloonBorder.class
src
net
java
balloontip
examples
BalloonTipTestDrive.java
images
closebutton_default.png
closebutton_pressed.png
closebutton_rollover.png
frameicon.png
infoicon.gif
utils
WindowUtils.java
BalloonTip.java
EdgedBalloonBorder.java
RoundedBalloonBorder.java
README.txt
The classes hierarchy provides Java SE 6-compiled code for the net.java.balloontip, net.java.balloontip.examples, and net.java.balloontip.utils packages. Only the former package and the three closebutton PNG images constitute the balloontip API.
| Make a JAR file |
| Follow these steps to create a JAR file if you find it more convenient to work with a JAR file than a classes hierarchy of class files: 1. Make sure that the classes directory is the current directory.
- Invoke
jar cf balloontip.jar netto create the JAR file.
This command creates a balloontip.jar JAR file containing the entire directory structure located beneath the classes directory. |
The src hierarchy provides all source code and a duplicate
of the various image files. The source code for the balloontip API
consists of BalloonTip.java, EdgedBalloonBorder.java, and RoundedBalloonBorder.java.
After the archive is unzipped (and BalloonTip possibly moved to another location), complete the installation by pointing the CLASSPATH environment variable to the classes directory (unless you create a JAR file that you specify via the java command's -cp option).
Test drive balloon tips
The BalloonTipTestDrive
example application demonstrates balloon tips associated with a text
field component. You should download it now if you haven't already done
so. Once you've downloaded the test drive package, specify java net.java.balloontip.examples.BalloonTipTestDrive to run the program. A GUI like the one shown in Figure 2 appears.

Figure 2. Specify exactly five characters to see the balloon tip
The GUI presents labels, two radio buttons for determining the balloon
tip's border (rounded or edged -- the rounded-border version is shown in
Figure 2), and a text field. It also presents a balloon tip when you
enter exactly five characters in the text field.
As you experiment with BalloonTipTestDrive, you will notice
that the balloon tip disappears when you enter more than five
characters in the text field. Backspacing to five characters causes the
balloon tip to reappear.
The balloon tip also disappears when the text field loses focus. Focus
is lost when you select a radio button or the balloon tip's Close
button. Focus is also lost when you minimize and restore the application
(at least under XP) or select another running application's main
window.
Explore the balloontip API
| Use balloontip's borders with other Swing components |
| Because RoundedBalloonBorder and EdgedBalloonBorder implement the javax.swing.border.Border interface and are marked public, you can use these classes to establish borders (via JComponent's public void setBorder(Border border) method) for other Swing components. |
A balloon tip is an instance of the net.java.balloontip.BalloonTip class. This class subclasses JPanel, uses a JLabel component to render balloon tip contents, and uses a JButton component (whose action listener hides the balloon tip) as the Close button.
BalloonTip internally works with two other classes located in the same package: RoundedBalloonBorder and EdgedBalloonBorder. These classes are used to establish a border for the balloon tip.
Methods for creating balloon tip components
The BalloonTip class presents a small API consisting of six
methods. Two of these methods are factory methods for creating balloon
tip components. The factory method for creating a rounded balloon tip
has the signature shown in Listing 2.
Listing 2. createRoundedBalloonTip(...)
public static BalloonTip createRoundedBalloonTip(Component attachedComponent,
Color borderColor,
Color fillColor,
int borderWidth,
int horizontalOffset,
int verticalOffset,
int arcWidth,
int arcHeight,
boolean useCloseButton)
The attachedComponent parameter identifies the component to
which the balloon tip is associated. Whenever the balloon tip is made
visible, it will appear in close proximity to the component, as
previously illustrated in Figure 2.
| Watch out for NullPointerExceptions! |
| A NullPointerException is thrown if attachedComponent does not have a JDialog, JFrame, or JInternalFrame ancestor -- each factory method calls an internal method that adds the balloon tip to JDialog's JFrame's, or JInternalFrame's layered pane. |
The borderColor and fillColor parameters
identify the colors used to paint the border and interior of the balloon
tip. The arguments passed to these parameters are forwarded to the RoundedBalloonBorder and EdgedBalloonBorder classes, which take care of painting.
The next five parameters identify the number of pixels between the
border and the balloon tip contents, the length and location of the
tip's triangular anchor (which points to the associated component), and
the size of the border's rounded corners. Figure 3 clarifies these
parameters (image courtesy of Bernhard Pauler).

The final useCloseButton parameter determines whether or not a close button appears on the balloon tip. Pass true to this parameter to display this button. If the button is present, clicking it hides the balloon tip.
The second factory method is somewhat simpler than the first one.
Because this latter method is responsible for creating balloon tips
without rounded corners, it does not require arcWidth and arcHeight parameters. Its signature is shown in Listing 3.
Listing 3. createEdgedBalloonTip(...)
public static BalloonTip createEdgedBalloonTip(Component attachedComponent,
Color borderColor,
Color fillColor,
int borderWidth,
int horizontalOffset,
int verticalOffset,
boolean useCloseButton)
This method's parameters are equivalent to the same-named parameters in
the first factory method. The only difference between these factory
methods is that the latter method creates a narrower border with square
corners. This alternative border's layout is shown in Figure 4 (image
courtesy of Bernhard Pauler).

The following excerpt from BalloonTipTestDrive.java shows
how the example application uses these factory methods to create its
rounded and edged balloon tips -- a factory method is invoked when its
associated radio button is selected.
Listing 4. Creating rounded and edged balloon tips
BalloonTip balloonTip;
// ...
if (buttonForRoundedLook.isSelected()) {
balloonTip = BalloonTip.createRoundedBalloonTip(textField, Color.BLACK, new Color(255, 255, 225),
10, 15, 20, 7, 7, true);
}
else
if (buttonForEdgedLook.isSelected()) {
balloonTip = BalloonTip.createEdgedBalloonTip(textField, Color.BLACK, new Color(255, 255, 225),
10, 15, 20, true);
}
Methods for configuring balloon tip components
Three of the remaining BalloonTip methods configure the
balloon tip in terms of the underlying label's text, icon, and icon-text
gap. These methods are described below:
public void setText(String text)specifies the balloon tip's text. Because this method invokesJLabel'ssetText()method, HTML can be used.public void setIcon(Icon icon)specifies the balloon tip's icon. This method invokesJLabel'ssetIcon()method.public void setIconTextGap(int iconTextGap)specifies how many pixels separate the icon from the text. This method invokesJLabel'ssetIconTextGap()method.
The BalloonTipTestDrive.java excerpt below uses setText(), setIcon(), and setIconTextGap() to configure a balloon tip with Nice! You inserted 5 characters! as its text, infoicon.gif as its icon, and 10 as its icon-text gap:
balloonTip.setText("Nice! You inserted 5 characters!");
balloonTip.setIcon(new ImageIcon(BalloonTipTestDrive.class.getResource
("/net/java/balloontip/images/infoicon.gif")));
balloonTip.setIconTextGap(10);
The final BalloonTip method, which is described below,
determines the balloon tip's visibility and (if the balloon tip is about
to be displayed) its above and left-aligned position relative to its
attached component:
public void setVisible(boolean show)shows or hides the balloon tip. Iftrueis passed, the balloon tip's position is established prior to being made visible.
This method is demonstrated by two BalloonTipTestDrive.java
excerpts. The first excerpt installs a key listener on the text field
to determine whether to show or hide a balloon tip on each key release.
The balloon tip will only be shown if exactly five characters are
present in the text field.
Listing 5. textField.addKeyListener(new KeyAdapter()
textField.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
if (textField.getText().length() == 5) {
balloonTip.setVisible(true);
} else {
balloonTip.setVisible(false);
}
}
});
The second excerpt installs a focus listener on the text field to hide
the balloon tip when this component loses the focus. A side effect of
this listener is that it prevents the action listener of the balloon
tip's Close button from being invoked to hide the balloon tip.
Listing 6. textField.addFocusListener(new FocusAdapter()
textField.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
balloonTip.setVisible(false);
}
});
When are balloon tips useful?
Suppose your GUI prevents users from shifting focus away from the
current text field until an appropriate value is entered. To avoid user
confusion when the focus shift fails, the GUI can show a balloon tip
reminding the user to enter an appropriate value. Check out Figure 5 for
an example.

Figure 5 reveals the user having entered 0 into the top text field, and
having attempted to switch focus to the bottom text field. Because 0 is
not an appropriate value, the focus shift is prevented and a balloon tip
is shown to remind the user to enter something valid.
The GUI in Figure 5 is created by VerifyAge1 -- see this article's code archive (in the Resources section) for the application's source code. The source code excerpt in Listing 7 shows how VerifyAge1 uses the balloontip API to integrate a balloon tip into its GUI.
Listing 7. integrating a balloon tip into a GUI
// Create the upper half of the GUI.
pnl = new JPanel (new FlowLayout (FlowLayout.LEFT));
pnl.setBorder (BorderFactory.createEmptyBorder (75, 0, 0, 0));
pnl.add (new JLabel ("Enter your age (1 - 130):"));
final JTextField txtAgeIn = new JTextField (10);
pnl.add (txtAgeIn);
txtAgeIn.addFocusListener (new FocusAdapter ()
{
public void focusLost (FocusEvent fe)
{
// Copy age from upper text field to
// lower text field when upper text
// field loses the focus.
txtAgeOut.setText (txtAgeIn.getText ());
}
});
// txtAgeIn's input verifier prevents focus from shifting away from this
// text field until the verify() method returns true.
txtAgeIn.setInputVerifier (new InputVerifier ()
{
public boolean verify (JComponent com)
{
JTextField txt = (JTextField) com;
try
{
// Parse digit characters from text
// field into an integer.
int value =
Integer.parseInt (txt.getText ());
// Integer out of range.
if (value <= 0 || value > 130)
{
bt.setVisible (true);
return false;
}
}
catch (NumberFormatException nfe)
{
// Non-digit characters detected.
bt.setVisible (true);
return false;
}
// Input is valid. It's okay to yield
// focus.
bt.setVisible (false);
return true;
}
});
getContentPane ().add (pnl, BorderLayout.NORTH);
bt = BalloonTip.createRoundedBalloonTip (txtAgeIn, Color.BLACK,
new Color (255, 255, 225), 10,
15, 20, 7, 7, false);
bt.setText ("Enter 1 - 130");
bt.setVisible (false);
This code fragment creates the GUI's top text field and its associated
balloon tip. An input verifier is attached to the text field to prevent a
focus shift when the verifier's public boolean verify(JComponent com) method returns false.
Problems and solutions
The code fragment in Listing 7 reveals two interesting things about balloon tips:
- Because
BalloonTipadds a balloon tip to aJFrame's,JDialog's, orJInternalFrame's layered pane, it requires sufficient screen space to avoid being clipped. This is the reason forpnl.setBorder (BorderFactory.createEmptyBorder (75, 0, 0, 0));. - When
a balloon tip is added to a layered pane, it is immediately shown when
the GUI is made visible. Because this is not appropriate when the GUI is
first revealed, the balloon tip is hidden via the finalbt.setVisible (false);call before the GUI is made visible.
The need to reserve extra GUI space so that a balloon tip is not clipped
against its containing layered pane is problematic. Although you could
address this problem by placing a logo and/or help instructions in the
extra space, Figure 6 reveals a better solution.

Figure 6 reveals the output from a VerifyAge2 application.
This application presents a balloon tip via an undecorated dialog box
that is dynamically positioned over the text field. To see how this
works, check out the VerifyAge2.java source code in this article's code archive.
| What is an undecorated dialog box? |
| An undecorated dialog box has no title bar or border. The same is true for any undecorated window. |
Figure 6 also reveals a second problem associated with balloon tips:
part of the application's main window is hidden by the dialog. This lack
of transparency is due to Java not supporting transparent windows (at
least on XP). Fortunately, this problem can be easily fixed: the next
article in this series shows how.
Note: Balloon tips and the system tray
You might be wondering if you can use the balloontip API with the System
Tray API to display balloon tips in the vicinity of tray icons (see
Figure 1). For this to happen, three problems need to be solved:
- Java does not support transparent windows on platforms such
as Windows XP -- you saw the result of this lack of support in Figure 6. - The
balloontip API currently orients balloon tips to the right. Because
platforms such as Windows XP place the system tray on the right side of
the task bar, orienting a balloon tip to the right could result in part
of the tip being clipped. - The System Tray API does not expose
tray icon positions -- it does not even expose components that could be
interrogated to obtain positions.
In the next article in this series, I'll introduce a solution to the
first problem. Solving the second problem involves making changes to the
balloontip API so that balloon tips are left-oriented. I have yet to
find a solution to the third problem.
In conclusion
The balloontip project suffers from a couple of weaknesses. One weakness
is the lack of documentation. Although one might argue that a
six-method API does not need to be documented, I believe that good
Javadoc would make the balloontip API easier to use -- you would not
necessarily have to explore the source code to learn how to use this
API.
Another weakness is the API's incompleteness. Although the balloontip
API provides setter methods for establishing a balloon tip's text, icon,
and icon-text gap, there are no equivalent getter methods that return
these items. Methods to get/set the attached component (and other
factory method arguments) might also be useful.
Overall, the expression "Good things come in small packages" is
applicable to balloontip. This project's simplicity makes it fairly easy
to enhance a Swing GUI to assist the user, as evidenced by the VerifyAge1 and VerifyAge2
applications. Also, the LGPL license allows commercial code to benefit
from this project. Consider using balloontip for your next Java project
involving a Swing GUI!
Addendum: A new version of balloontip
Shortly after completing this article, I discovered a new version of balloontip. This version is bundled in the BalloonTip_2007-12-30.zip archive and introduces the following new features:
- A balloon tip can be displayed above or below and on either side of the component to which it is attached by using
BalloonTip's newAlignmentenumeration and enhancedcreateRoundedBalloonTip()andcreateEdgedBalloonTip()factory methods. - The location of the balloon tip's triangular anchor can be moved to the center or a corner of the attached component by using
BalloonTip's newTriangleTipLocationenumeration andpublic void setTriangleTipLocation(TriangleTipLocation triangleTipLocation)method. - The width of the empty border surrounding the balloon tip's close button can be specified by using
BalloonTip's newpublic void setCloseButtonBorderWidth(int top, int left, int bottom, int right)method. By default, an edged balloon tip leaves a one-pixel border around its close button. - The balloontip API's class files are bundled in a
balloontip_2007-12-30.jarfile. Furthermore, the updatedBalloonTipTestDriveexample application's class files are also bundled in this JAR file. You can easily run the example viajava -jar balloontip_2007-12-30.jar.
Because BalloonTip uses the enumeration language feature
introduced by J2SE 5.0, it can no longer be used with earlier versions
of Java. If this is a problem, you need to change the source code.
Jeff Friesen is a freelance software developer and educator who specializes in Java technology. Check out his javajeff.mb.ca website to discover all of his published Java articles and more.
[### Learn more about this topic
]()* Visit theBalloon Tips for Java project homepage, hosted on Java.net, to download the latest version of the balloontip API.
- This article's code archive includes the VerifyAge1 and VerifyAge2 applications, used to demonstrate the use of balloon tips in a Swing-based application.
- Familiarize yourself with the GNU Lesser General Public License (LGPL).
- See "Swing-based tree layouts with CheckboxTree"
(Lorenzo Bigagli and Enrico Boldrini, JavaWorld, September 2007) for
another open source GUI component that you can use in your Swing-based
applications. - Visit JavaWorld's Swing research center for many more articles about Swing-based application development.
- Also see Network World's IT Buyer's Guides: Side-by-side comparison of hundreds of products in over 70 categories.
https://www.javaworld.com/article/2077811/open-source-java-projects-balloontip-for-java.html
https://github.com/acuilab/balloontip
https://github.com/timmolderez/balloontip