Tip of the Day
Applications often present Tip of the Day dialog boxes that help users to learn the application’s features. It’s typical for the dialog box to appear automatically the first time the application starts running, to appear automatically on subsequent application startups (optional, based on user preference), and to appear manually when chosen by the user.
SwingX supports Tip of the Day dialog boxes through its org.jdesktop.swingx.JXTipOfTheDay class. Because this class subclasses org.jdesktop.swingx.JXPanel (which enhances javax.swing.JPanel with support for translucency), JXTipOfTheDay can be used as a pane in some other dialog box. For this article, however, I’ll focus only on the dialog box usage.
The JXTipOfTheDay class describes a component with tip data specified by a model. This model is an instance of classes that implement the org.jdesktop.swingx.tips.TipOfTheDayModel interface, which specifies a collection of tips, and the nested TipOfTheDayModel$Tip interface, which describes one tip.
TipOfTheDayModel’s public int getTipCount() method returns the number of tips managed by the model; its TipOfTheDayModel.Tip getTipAt(int index) method returns the Tip at index. Tip’s public String getTipName() method returns the tip’s name; its public Object getTip() method returns the tip as a string, a component, or an icon.
Although you could implement the TipOfTheDayModel and TipOfTheDayModel$Tip interfaces in your own model class(es), SwingX provides the org.jdesktop.swingx.tips.DefaultTipOfTheDayModel and org.jdesktop.swingx.tips.DefaultTip classes that implement these interfaces for you, making it possible to construct a model conveniently:
TipOfTheDayModel.Tip tip1; tip1 = new DefaultTip ("Tip 1", "<html>You can <strong>embed</strong> various " + "<em>HTML</em> tags in tips."); TipOfTheDayModel.Tip tip2; tip2 = new DefaultTip ("Tip 2", "JXTipOfTheDay provides methods that let you " + "programmatically navigate tips when you use " + "JXTipOfTheDay as a pane."); java.util.List<TipOfTheDayModel.Tip> tips = java.util.Arrays.asList (tip1, tip2); DefaultTipOfTheDayModel model = new DefaultTipOfTheDayModel (tips);
After creating the model, you’re ready to create a JXTipOfTheDay component. Accomplish this task by passing the model to the public JXTipOfTheDay(TipOfTheDayModel model) constructor. For those situations in which you first create the component and then the model, call public JXTipOfTheDay() followed by JXTipOfTheDay’s public void setModel(TipOfTheDayModel model) method:
// Create JXTipOfTheDay component based on previous model. // Option 1. JXTipOfTheDay totd = new JXTipOfTheDay (model); // Option 2. JXTipOfTheDay totd = new JXTipOfTheDay (); totd.setModel (model);
JXTipOfTheDay includes several showDialog() methods that present this component as a dialog box. The simplest of these methods is public void showDialog(Component parent). Call this method with a parent window reference, as in totd.showDialog (frame);. The resulting modal dialog box is shown in Figure 4.
Figure 4 The Tip of the Day dialog box presents buttons for selecting previous/next tips.
Instead of using DefaultTipOfTheDayModel and DefaultTip to programmatically build a model, you can let SwingX create the model for you. Begin by storing your tips in persistent storage, with this format:
tip.x.description=y
where x is a 1-based integer and y is tip text. You can optionally precede this line with tip.x.name=z, where z names the tip:
tip.1.description=Buy low, sell high tip.2.name=Java tip tip.2.description=Java’s original name was Oak.
Continue by streaming this formatted storage into a java.util.Properties object. If streaming is successful, call org.jdesktop.swingx.tips.TipLoader’s public static TipOfTheDayModel load(Properties props) method to load props’ contents into a new model:
Properties tips = new Properties (); FileInputStream fis = null; try { fis = new FileInputStream ("tips.properties"); tips.load (fis); TipOfTheDayModel model = TipLoader.load (tips); JXTipOfTheDay totd = new JXTipOfTheDay (model); totd.showDialog (frame); } catch (IOException e) { JOptionPane.showMessageDialog (frame, e.getMessage ()); }
Users often want to prevent the Tip of the Day dialog box from appearing at application startup. Accomplish this task by having the application check its persistent storage to determine whether the dialog box should appear. If shown, have the dialog box present a check box (see Figure 5) that the user toggles to prevent future dialog box activation. Save this choice to persistent storage when the dialog box closes.
Figure 5 Clear the check box to prevent future dialog box activation.
JXTipOfTheDay provides two ways to prevent the Tip of the Day dialog box from appearing. The first way involves its public boolean showDialog(Component parent, Preferences showOnStartupPref) method and Java’s Preferences API. The second way involves its public boolean showDialog(Component parent, JXTipOfTheDay.ShowOnStartupChoice choice) method.
For brevity, I ignore the former showDialog() method. The latter showDialog() method calls ShowOnStartupChoice’s public boolean isShowingOnStartup() method before showing the dialog box; the dialog box is not shown if false returns. When the dialog box closes, it calls void setShowingOnStartup(boolean showOnStartup) to save the check box value to persistent storage:
totd.showDialog (frame, new JXTipOfTheDay.ShowOnStartupChoice () { public boolean isShowingOnStartup () { // Obtain value from persistent storage. // Here we assume that we always want to // show tips at startup. return true; } public void setShowingOnStartup (boolean x) { // Save user choice value to persistent // storage. Here we just output the value // for simplicity. if (x) System.out.println ("User wants tips"); else System.out.println ("No tips"); } });
The user will probably grow frustrated with an application that always presents the same tip at startup. This problem can be solved by retrieving (from persistent storage) the index of the next tip to present at startup, by calculating the next tip index in the setShowingOnStartup() method, and (in the same method) by saving this index to persistent storage.
JXTipOfTheDay provides a public void setCurrentTip(int index) method that causes the tip at the 0-based index to be displayed, and a public int getCurrentTip() method that returns the 0-based index of the displayed tip. To advance to the next tip, you need to know the total number of tips, which TipOfTheDayModel’s getTipCount() method provides:
final JXTipOfTheDay totd = new JXTipOfTheDay (model); totd.setCurrentTip (0); // Assume value read from persistent storage. totd.showDialog (frame, new JXTipOfTheDay.ShowOnStartupChoice () { public boolean isShowingOnStartup () { // Obtain value from persistent storage. // Here we assume that we always want to // show tips at startup. return true; } public void setShowingOnStartup (boolean x) { // Save user choice value to persistent // storage. Here we just output the value // for simplicity. if (x) System.out.println ("User wants tips"); else System.out.println ("No tips"); // Presumably, we also save the index of // the next tip to display at startup. // Here we just output this index. int ct = totd.getCurrentTip (); int nt = totd.getModel ().getTipCount (); if (++ct == nt) ct = 0; System.out.println ("Future tip is " + ct); } });
Earlier, I mentioned that a tip could be implemented as a string, a component, or an icon. Although I used DefaultTip’s public DefaultTip(String name, Object tip) constructor to store string-based tips, I could just as easily have stored icons or components as tips. For additional homework, modify the TOTDDemo application to store an icon-based tip.