Adding a listener to a preferences tree

You can implement the NodeChangeListener to listen for events when nodes are added or removed and implement the PreferenceChangeListener to listen for events that are emitted when the value of the preference changes. You need to add a listener to that particular node in which you are interested.

The first example I wrote involved 2 VMs. The first app showed a Swing gui and listened to preference change events, while the other app changed the preferences. Events were not received by the first app. It turns out that event notification is only guaranteed for changes made in the same VM.

This example starts up, writes a couple preferences, fires up a thread that changes these preferences after 5 seconds. You should see the changes in the GUI.

com/esus/examples/preferences/Main.java:

package com.esus.examples.preferences;

import javax.swing.border.*;
import java.util.prefs.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame 
{
   Preferences preferences;
 
   public static final String APPLICATIONPATH = "applicationpath";
   public static final String USERFONT        = "userfont";
   public static final String LASTUSERLOGIN   = "lastuserlogin";
   public static final String LDAPURL         = "ldapurl";
 
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
 
      loadPreferences();
 
      JLabel appPathLabel       = new JLabel("Application Path:");
      JLabel userFontLabel      = new JLabel("User font:");
      JLabel lastUserLoginLabel = new JLabel("Last user logged in: ");
      JLabel ldapURLLabel       = new JLabel("LDAP url:");
      final JTextField appPathTf       = new JTextField(
                                  preferences.get(APPLICATIONPATH, ""));
      final JTextField userFontTf      = new JTextField(
                                  preferences.get(USERFONT, ""));
      final JTextField lastUserLoginTf = new JTextField(
                                  preferences.get(LASTUSERLOGIN, ""));
      final JTextField ldapURLTf       = new JTextField(
                                  preferences.get(LDAPURL, ""));
 
      JPanel prefsPanel = new JPanel();
      prefsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
      prefsPanel.setLayout(new GridLayout(4, 4, 10, 10));
      prefsPanel.add(appPathLabel);
      prefsPanel.add(appPathTf);
      prefsPanel.add(userFontLabel);
      prefsPanel.add(userFontTf);
      prefsPanel.add(lastUserLoginLabel);
      prefsPanel.add(lastUserLoginTf);
      prefsPanel.add(ldapURLLabel);
      prefsPanel.add(ldapURLTf);
 
      JPanel buttonPanel = new JPanel();
      buttonPanel.setLayout(new GridLayout(1, 2, 70, 70));
 
      JButton cancelButton = new JButton("Cancel");
      cancelButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            System.exit(0);
         }
      });
 
      JButton saveButton = new JButton("Save");
      saveButton.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) {
            preferences.put(APPLICATIONPATH, appPathTf.getText());
            preferences.put(USERFONT,        userFontTf.getText());
            preferences.put(LASTUSERLOGIN,   lastUserLoginTf.getText());
            preferences.put(LDAPURL,         ldapURLTf.getText());
            System.out.println("Preferences saved!");
         }
      });

      // listen for changes to my preferences
      preferences.addPreferenceChangeListener(new PreferenceChangeListener() {
         public void preferenceChange(PreferenceChangeEvent pce) {
            String key = pce.getKey();
            String value = pce.getNewValue();
            if (key.equals(APPLICATIONPATH))    appPathTf.setText(value);
            else if (key.equals(USERFONT))      userFontTf.setText(value);
            else if (key.equals(LASTUSERLOGIN)) lastUserLoginTf.setText(value);
            else if (key.equals(LDAPURL))       ldapURLTf.setText(value);
         }
      });
  
      buttonPanel.add(cancelButton);
      buttonPanel.add(saveButton);
 
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(BorderLayout.CENTER, prefsPanel);
      getContentPane().add(BorderLayout.SOUTH, buttonPanel);
   }
 
   public void loadPreferences() {
      preferences = Preferences.userNodeForPackage(this.getClass());
 
      // put in some default values
      preferences.put(APPLICATIONPATH, "c:\application");
      preferences.put(USERFONT,        "Courier New");
      preferences.put(LASTUSERLOGIN,   "Joris Van den Bogaert");
      preferences.put(LDAPURL,         "ldap://localhost:389");
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(350, 190);
      main.setVisible(true);
 
      main.new ChangePreference().start();
   }
 
   class ChangePreference extends Thread {
      public void run() {
         try {
            Thread.sleep(5000);
         }
         catch(InterruptedException e) { }
 
         preferences.put(APPLICATIONPATH, "/home/me/test");
         preferences.put(USERFONT,        "Arial");
         preferences.put(LASTUSERLOGIN,   "Alicia Kolesnikova");
         preferences.put(LDAPURL,         "ldap://111.222.333.444:389");
      }
   }
}

Using a ListResourceBundle

A ListResourceBundle is one where you centralize all your locale-specific strings and objects in a class. Provide your own versions by extending the abstract class ListResourceBundle and implementing the method getContents that returns an 2d array containing key/values pairs.

Keep in mind that:

  • keys are case-sensitive

  • a value doesn’t need to be a String, it may be an array or any type of object
  • you should put the resourcebundle in separate files and declare the class public

    To instantiate one of your resourcebundles, determine a locale (or use the default one) and call ResourceBundle.getResourceBundle(name of family class, locale). This method will look up the class using a certain lookup mechanism that goes from specific to general, in terms of locale information. For example, if the default locale is “en_US” and your specify “nl_BE”, the lookup will search for the following classes (assuming just MyResources.class exists):

       MyResources_nl_BE
       MyResources_nl
       MyResources_en_US
       MyResources_en
       MyResources
    

    If the lookup doesn’t find the class, it will throw a MissingResourceException.

    Once you have an instance of ResourceBundle, you can look up its contents using the three methods getString, getStringArray and getObject. If you want another type of array, for example an int[][], just use getObject and cast the result to int[][].

    Main.java:

    import java.util.*;
     
    public class Main {
       public static void main(String []args) {
          // use default locale 
          printLabelValues("LabelResourceBundle");
     
          // use locale en/../..
          Locale.setDefault(new Locale("en", "", ""));	
          printLabelValues("LabelResourceBundle");
     
          // use locale nl/BE/Unix
          Locale.setDefault(new Locale("nl", "BE", "Unix"));
          printLabelValues("LabelResourceBundle");
        
          // use locale nl					// MissingResourceException!
          Locale.setDefault(new Locale("nl", "", ""));	
          printLabelValues("LabelResourceBundle");
     
          // use locale ../BE/..				// MissingResourceException!
          Locale.setDefault(new Locale("", "BE", ""));
          printLabelValues("LabelResourceBundle");
     
          // use locale ../../.. 				// MissingResourceException!
          Locale.setDefault(new Locale("", "", ""));
          printLabelValues("LabelResourceBundle");
       }
     
       public static void printLabelValues(String resourceName) {
          ResourceBundle rb = ResourceBundle.getBundle(resourceName);  
     
          System.out.println("For locale: " + Locale.getDefault());
          System.out.println("tContents for LBL_NAME: " + rb.getString("LBL_NAME"));
          System.out.print("tContents for LBL_NUMBERS: ");
          String array[] = rb.getStringArray("LBL_NUMBERS");
          for (int i=0; i<array.length; i++) 
             System.out.print(array[i] + " ");
          System.out.println();
          System.out.println("tContents for LBL_OBJECT: of " + 
                                              rb.getObject("LBL_OBJECT").getClass());
       }
    }
    

    LabelResourceBundle_nl_BE.java:

    import java.util.*;
     
    public class LabelResourceBundle_nl_BE extends ListResourceBundle
    {
       private Object[][] contents = 
          { { "LBL_NAME", 		"Naam:" },
            { "LBL_NUMBERS",	new String[] { "een", "twee", "drie" }},
            { "LBL_OBJECT", 	new Integer(10) } };
      
       public Object[][] getContents() {
          return contents;
       }
    }
    

    LabelResourceBundle_fr_BE.java:

    import java.util.*;
     
    public class LabelResourceBundle_fr_BE extends ListResourceBundle
    {
       private Object[][] contents = 
          { { "LBL_NAME", 		"Nom:" },
            { "LBL_NUMBERS",	new String[] { "un", "deux", "troi" }},
            { "LBL_OBJECT", 	new Long(100) } };
     
       public Object[][] getContents() {
          return contents;
       }
    }
    

    LabelResourceBundle_en.java:

    import java.util.*;
     
    public class LabelResourceBundle_en extends ListResourceBundle
    {
       private Object[][] contents = 
          { { "LBL_NAME", 		"Name:" },
            { "LBL_NUMBERS",	new String[] { "one", "two", "three" }},
            { "LBL_OBJECT", 	new Double(1000.1) } };
     
       public Object[][] getContents() {
          return contents;
       }
    }
    

    Output after running Main:

    For locale: en_US
            Contents for LBL_NAME: Name:
            Contents for LBL_NUMBERS: one two three
            Contents for LBL_OBJECT: of class java.lang.Double
    For locale: en
            Contents for LBL_NAME: Name:
            Contents for LBL_NUMBERS: one two three
            Contents for LBL_OBJECT: of class java.lang.Double
    For locale: nl_BE_UNIX
            Contents for LBL_NAME: Naam:
            Contents for LBL_NUMBERS: een twee drie
            Contents for LBL_OBJECT: of class java.lang.Integer
    Exception in thread "main" java.util.MissingResourceException: Can't find resource
    for base name LabelResourceBundle, locale nl
            at java.util.ResourceBundle.getBundle(ResourceBundle.java:423)
            at java.util.ResourceBundle.getBundle(ResourceBundle.java:339)
            at Main.printLabelValues(Main.java, Compiled Code)
            at Main.main(Main.java:18)
    

  • Examples of Swing borders

    You can combine these borders with CompoundBorder.

    Main.java:

    import javax.swing.border.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
     
    public class Main extends JFrame
    {
       public Main() {
          getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
     
          JLabel labels[] = new JLabel[6];
          labels[0] = new JLabel("Label with EmptyBorder, margin 20");
          labels[0].setBorder(new EmptyBorder(20, 20, 20, 20));
          labels[1] = new JLabel("Label with LineBorder, red color, thickness 3");
          labels[1].setBorder(new LineBorder(Color.red, 3));
          labels[2] = new JLabel("Label with LOWERED BevelBorder");
          labels[2].setBorder(new BevelBorder(BevelBorder.LOWERED));
          labels[3] = new JLabel("Label with RAISED EtchedBorder");
          labels[3].setBorder(new EtchedBorder(EtchedBorder.RAISED));
          labels[4] = new JLabel("Label with MatteBorder and Icon");
          labels[4].setBorder(new MatteBorder(new ImageIcon("c:\female.gif")));
          labels[5] = new JLabel("Label with BevelBorder TitledBorder");
          labels[5].setBorder(new TitledBorder(new BevelBorder(BevelBorder.LOWERED), "Title"));
     
          for (int i=0; i<labels.length; i++) {
             labels[i].setOpaque(true);
             getContentPane().add(labels[i]);
          }
          
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
       }
     
       public static void main(String [] args) {
          Main main = new Main();
          main.setSize(300, 400);
          main.setVisible(true);
       } 
    } 
    

    Setting the top-left icon in a JFrame

    setIconImage (inherited method from java.awt.Frame) does the job!

     
    import javax.swing.*;
    import java.awt.*;
       
    public class IconFrame extends JFrame {
     
       public IconFrame() {
          setSize(100, 100);
    
          setIconImage(new ImageIcon("test.gif").getImage());
       }
     
       public static void main(String args[]) {
          new IconFrame().setVisible(true);     
       }
    }
    

    Making the scrolling in JScrollPane faster

    A new feature has been implemented in Swing 1.1.1 that makes scrolling faster in most cases. The idea is to only update the exposed viewport (the visible region) when scrolling. To enable this, set the property EnableWindowBlit to true.

       scrollpane.getViewport().putClientProperty("EnableWindowBlit", Boolean.TRUE);
    

    Preventing a JTabbedPane tab from being selected

    Override the setSelectedIndex in the JTabbedPane object, as this is called before the change takes place. Here’s an example that only allows you to select tabs with even numbers:

    import javax.swing.event.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
     
    public class Main extends JFrame implements ActionListener {
       JTabbedPane tabbedPane;
       int ntabs = 0;
     
       public Main() {
          getContentPane().setLayout(new BorderLayout());
          tabbedPane = new JTabbedPane() {
             public void setSelectedIndex(int index) {
                System.out.println("index selected: " + index);
                if (index % 2 == 0) {
                   System.out.println("allowed");
                   super.setSelectedIndex(index);
                }
                else {
                   System.out.println("not allowed");
                }
             }
          };
          createTab();
     
          getContentPane().add(BorderLayout.CENTER, tabbedPane);
          setJMenuBar(createMenuBar());
      
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
     
          setTitle("JTabbedPane Selection Demonstration");
          setSize(new Dimension(400, 400));
       }
     
       protected JMenuBar createMenuBar() {
          JMenuBar menuBar = new JMenuBar();
     
          JMenu menu = new JMenu("JTabbedPane");
          JMenuItem menuItem1 = new JMenuItem("Create new tab");
          menuItem1.addActionListener(this);
          menu.add(menuItem1);
          menuBar.add(menu);
     
          return menuBar;
       }
      
       public void actionPerformed(ActionEvent e) {
          if (e.getActionCommand().equals("Create new tab")) {
             createTab();
          }
       }
     
       protected void createTab() {
          tabbedPane.addTab("Tab #" + ntabs, new JLabel("Tab #" + ntabs));
          ntabs++;
       }
     
       public static void main(String []args) {
          Main main = new Main();
          main.show();
       }
    }
    

    More than one JToolBar

    Main.java:

    import javax.swing.border.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
     
    public class Main extends JFrame
    {
       public Main()
       {
          JToolBar toolBar1 = new JToolBar();
          JButton gifButton = new JButton("GIF");
          JButton jpgButton = new JButton("JPG");
          JButton tiffButton = new JButton("TIFF");
          toolBar1.add(gifButton);
          toolBar1.add(jpgButton);
          toolBar1.add(tiffButton);
     
          JToolBar toolBar2 = new JToolBar();
          JButton openButton = new JButton("Open");
          JButton saveButton = new JButton("Save");
          JButton exitButton = new JButton("Exit");
          toolBar2.add(openButton);
          toolBar2.add(saveButton);
          toolBar2.add(exitButton);
     
          getContentPane().setLayout(new BorderLayout());
          toolBar1.setBorder(new CompoundBorder(new LineBorder(Color.black),
                                                new EmptyBorder(10, 10, 10, 10)));
          toolBar2.setBorder(new CompoundBorder(new LineBorder(Color.black),
                                                new EmptyBorder(10, 10, 10, 10)));
     
          JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER));
          panel.add(toolBar1);
          panel.add(toolBar2);
     
          getContentPane().add(BorderLayout.NORTH, panel);
     
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
       }
          
       public static void main(String []args) {
          Main main = new Main();
          main.setSize(300, 300);
          main.setVisible(true);
       }
    }

    Programmatically maximizing a JInternalFrame

    Call setMaximum(true) on your JInternalFrame instance. Or call the method maximizeFrame of the DestopManager providing it with the instance of the JInternalFrame to be maximized. Here’s an example:

    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
    import java.net.*;
     
    public class Main extends JFrame implements ActionListener {
       JDesktopPane desktop;
       JInternalFrame frame;
     
       public Main() {
          setTitle("JInternalFrame Actions");
          desktop = new JDesktopPane(); 
          createInternalFrame(); 
          setContentPane(desktop);
          setJMenuBar(createMenuBar());
     
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(0);
             }
          });
     
          setSize(500, 400);
       }
     
       protected JMenuBar createMenuBar() {
          JMenuBar menuBar = new JMenuBar();
     
          JMenu menu = new JMenu("JInternalFrame Actions");
          JMenuItem menuItem = new JMenuItem("Maximize");
          menuItem.addActionListener(this);
          menu.add(menuItem);
          menuBar.add(menu);
     
          return menuBar;
       }
     
       public void actionPerformed(ActionEvent ae) {
          if (ae.getActionCommand().equals("Maximize")) {
     
             desktop.getDesktopManager().maximizeFrame(frame);
     
             /* OR:
     
             try {
                frame.setMaximum(true);
             } 
             catch(java.beans.PropertyVetoException e) {
             }
     
             */
             
          }
       }
     
       protected void createInternalFrame() {
          frame = new JInternalFrame("InternalFrame", 
             true,    // resizable
             true,    // closable
             true,    // maximizable
             true);   // iconifiable
          frame.setVisible(true); 
          desktop.add(frame);
          frame.setSize(200, 200);
          frame.setLocation(30, 30);
          try {
             frame.setSelected(true);
          } catch (java.beans.PropertyVetoException e) {}
       }
     
       public static void main(String []args) {
          Main main = new Main();
          main.setVisible(true);
       }
    }

    Changing the setMnemonic behavior of JButton so that it responds to CTRL instead of ALT

    Try this workaround!

    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.*;
       
    public class Main extends JFrame {
       JList list; 
     
       public Main() {
          getContentPane().setLayout(new FlowLayout());
     
          final JButton button = new JButton("Click-Me!");
     
          ActionListener al = new ActionListener() {
             public void actionPerformed(ActionEvent ae) {
                System.out.println("Button was clicked!");
             }
          };
          button.addActionListener(al);
      
          // don't use button.setMnemonic('C') if you want only the CTRL mask to work,
          // use button.getModel() instead. 
          button.getModel().setMnemonic('C');
          KeyStroke keyStroke = KeyStroke.getKeyStroke('C', Event.CTRL_MASK, false); 
          button.registerKeyboardAction(al, keyStroke, JComponent.WHEN_FOCUSED); 
          
          getContentPane().add(button);
     
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent event) {
                System.exit(0);   
             }      
          });
     
          setSize(400, 400);
       }
     
       public static void main(String[] args) {
          (new Main()).show();
       }
    }
    

    Defining the size of a JComboBox

    If you want to change the size of the JComboBox component itself, use:

       combobox.setPreferredSize(new Dimension(x, y));
    

    If you want to have control over the popupwidth and popupheight, extend JComboBox and add the necessary functionality to a custom ComboBoxUI:
    Main.java:

    import javax.swing.plaf.metal.*;
    import javax.swing.plaf.basic.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.util.*;
    import java.awt.*;
        
    public class Main extends JFrame
    { 
       public Main() {
          getContentPane().setLayout(new FlowLayout());
          
          Vector v = new Vector();
          for (int i=0; i<20; i++)
             v.addElement("ComboBox Item #" + i);
          SizedJComboBox combobox = new SizedJComboBox(v);
     
          combobox.setPopupWidth(190);
          combobox.setPopupHeight(30);
     
          getContentPane().add(combobox);
      
          addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent we) {
                System.exit(1);
             }
          });      
       
          setSize(new Dimension(200, 200));
       } 
     
       public static void main(String[] args) throws Exception {
          Main main = new Main();
          main.setVisible(true);
       }
    }
     
    class SizedJComboBox extends JComboBox {
       protected int popupWidth;
       protected int popupHeight;
     
       public SizedJComboBox(ComboBoxModel aModel) {
          super(aModel);
          setUI(new SizedComboBoxUI());
       }
     
       public SizedJComboBox(final Object[] items) { 
          super(items); 
          setUI(new SizedComboBoxUI());
       } 
     
       public SizedJComboBox(Vector items) { 
          super(items); 
          setUI(new SizedComboBoxUI()); 
       } 
     
       public void setPopupWidth(int width) { 
          popupWidth = width; 
       }
     
       public void setPopupHeight(int height) {
          popupHeight = height;
       } 
     
       public Dimension getPopupSize() { 
          Dimension size = getSize(); 
          if (popupWidth < 1) popupWidth = size.width; 
          return new Dimension(popupWidth, popupHeight); 
       } 
    }
     
    class SizedComboBoxUI extends MetalComboBoxUI {
       protected ComboPopup createPopup() { 
          BasicComboPopup popup = new BasicComboPopup(comboBox) { 
             public void show() { 
                Dimension popupSize = ((SizedJComboBox) comboBox).getPopupSize(); 
                popupSize.setSize(popupSize.width, popupSize.height < 1 ? getPopupHeightForRowCount(comboBox.getMaximumRowCount()) : popupSize.height); 
                Rectangle popupBounds = computePopupBounds( 0, 
                      comboBox.getBounds().height, popupSize.width, popupSize.height); 
                scroller.setMaximumSize(popupBounds.getSize()); 
                scroller.setPreferredSize(popupBounds.getSize()); 
                scroller.setMinimumSize(popupBounds.getSize()); 
                list.invalidate(); 
                int selectedIndex = comboBox.getSelectedIndex(); 
                if (selectedIndex == -1) { 
                   list.clearSelection(); 
                } else { 
                   list.setSelectedIndex( selectedIndex ); 
                } 
                list.ensureIndexIsVisible( list.getSelectedIndex() ); 
                setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled()); 
     
                show(comboBox, popupBounds.x, popupBounds.y); 
             } 
          }; 
     
          popup.getAccessibleContext().setAccessibleParent(comboBox); 
          return popup; 
       } 
    }