home *** CD-ROM | disk | FTP | other *** search
/ PC World 1998 October / PCWorld_1998-10_cd.bin / software / prehled / inprise / JSAMPLES.Z / LocaleChooser.java < prev    next >
Text File  |  1998-05-08  |  28KB  |  735 lines

  1. /*
  2.  * Copyright (c) 1997-1998 Borland International, Inc. All Rights Reserved.
  3.  * 
  4.  * This SOURCE CODE FILE, which has been provided by Borland as part
  5.  * of a Borland product for use ONLY by licensed users of the product,
  6.  * includes CONFIDENTIAL and PROPRIETARY information of Borland.  
  7.  *
  8.  * USE OF THIS SOFTWARE IS GOVERNED BY THE TERMS AND CONDITIONS 
  9.  * OF THE LICENSE STATEMENT AND LIMITED WARRANTY FURNISHED WITH
  10.  * THE PRODUCT.
  11.  *
  12.  * IN PARTICULAR, YOU WILL INDEMNIFY AND HOLD BORLAND, ITS RELATED
  13.  * COMPANIES AND ITS SUPPLIERS, HARMLESS FROM AND AGAINST ANY CLAIMS
  14.  * OR LIABILITIES ARISING OUT OF THE USE, REPRODUCTION, OR DISTRIBUTION
  15.  * OF YOUR PROGRAMS, INCLUDING ANY CLAIMS OR LIABILITIES ARISING OUT OF
  16.  * OR RESULTING FROM THE USE, MODIFICATION, OR DISTRIBUTION OF PROGRAMS
  17.  * OR FILES CREATED FROM, BASED ON, AND/OR DERIVED FROM THIS SOURCE
  18.  * CODE FILE.
  19.  */
  20. package borland.samples.intl.beans;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.awt.image.*;
  25. import java.beans.*;
  26. import java.net.*;
  27. import java.text.*;
  28. import java.util.*;
  29. import borland.jbcl.model.*;
  30. import borland.jbcl.control.*;
  31. import borland.jbcl.view.*;
  32. import borland.jbcl.util.*;
  33. import com.objectspace.jgl.*;
  34.  
  35. import borland.samples.intl.beans.event.*;
  36. import borland.samples.intl.beans.resources.LocaleChooserRes_en;
  37.  
  38. /**
  39.  * The LocaleChooser sample Java Bean is an example of an
  40.  * internationalized Java Bean created using the JBCL TreeControl.
  41.  * Its properties are customizable in the designer via custom property
  42.  * editors, which are also internationalized.  LocaleChooser's bean
  43.  * info class extends java.beans.SimpleBeanInfo as an example of
  44.  * how to create a bean info class from the JDK bean info helper class.
  45.  * 
  46.  *<p>
  47.  * LocaleChooser allows a user to select a locale from among a tree of
  48.  * locales.  When the LocaleChooser first appears, the current locale
  49.  * (if present in the tree) is denoted by a blue ball adjacent to the
  50.  * locale.  Internal nodes of the locale tree are language names, the
  51.  * leaves are country names.  A user selects a locale by
  52.  * double-clicking or pressing [Enter] on one of the leaves of the
  53.  * locale tree.  Note that the LocaleChooser does not automatically
  54.  * change the current actual VM locale, but rather returns the
  55.  * selected locale and updates its own internal state for the selected
  56.  * locale.  A localeChangeListener, registered with the LocaleChooser,
  57.  * can set the current system default locale.
  58.  *<p>
  59.  * Language and country names can be displayed in one of three
  60.  * different formats--ISO, locale's native language, and current
  61.  * locale's language.
  62.  *<p>
  63.  * LocaleChooser takes advantage of the JBCL Model-View architecture
  64.  * to perform animation within the tree.  A separate thread is used
  65.  * to send periodic timer events to the LocaleChooser, which updates
  66.  * the currently displayed frame of the image.
  67.  *<p>
  68.  * LocaleChooser also uses its own specialized version of the
  69.  * BasicTreeContainer, BasicOrderedTreeContainer, to sort node labels
  70.  * as they are added to the locale tree.
  71.  *<p>
  72.  * LocaleChooserBeanInfo uses the java.beans.SimpleBeanInfo class to
  73.  * provide bean info.  The bean info itself is resourced, so the
  74.  * fly-over brief hints providing a description of each property
  75.  * appear in the language of the current locale (if available).
  76.  *<p>
  77.  * Customizable Properties
  78.  *<ul>
  79.  *<li>localeChoices
  80.  * <p>array of locales to display in locale tree
  81.  *<li>displayStyle
  82.  * <p>determines whether locale names should be displayed as ISO
  83.  *    abbreviations, or localized names for the target locale itself, or
  84.  *    the user's current locale (if possible)
  85.  *<li>selectedLocales
  86.  * <p>array of locales to display as initially selected in locale
  87.  *    tree
  88.  *<li>allowMultiSelect
  89.  * <p>whether or not to allow multiple locales to be visually selected
  90.  *<li>acceptLeafNodesOnly
  91.  * <p>whether or not to allow only leaf nodes *(as opposed to internal
  92.  *    nodes) of the locale tree to be chosen
  93.  *<li>collationLocale
  94.  * <p>locale used for (vertical) sorting of locale names in the tree
  95.  *<li>locale
  96.  * <p>the current locale of the LocaleChooser
  97.  *<li>title
  98.  * <p>title to display at root of locale tree
  99.  *<li>animationEnabled
  100.  * <p>whether to enable or disable the globe animation (used when
  101.  *    LocaleChooser is stopped while running as part of an applet)
  102.  *</ul>
  103.  *<p>
  104.  * Events
  105.  * The LocaleChooser bean fires a LocaleChangeEvent when a user
  106.  * selects a new locale.
  107.  *<p>
  108.  * <I>Special thanks</i> to Mr. Silviu Marghescu for graciously
  109.  * allowing me to use his wonderful globe.gif in this sample Java
  110.  * Bean.  He wishes it be known that:
  111.  *<p>
  112.  * The globe.gif image was designed and is owned by Silviu Marghescu
  113.  * (silviu@emaginet.com).  Permission to use and redistribute is
  114.  * granted to Borland International.  Third parties should contact the
  115.  * author before using the image in commercial products.
  116.  *<p>
  117.  * Silviu Marghescu
  118.  *<p>
  119.  * Thank you very much Mr. Marghescu! (dcy)
  120.  */
  121. public class LocaleChooser extends BevelPanel implements ActionListener, BinaryComparator, Runnable {
  122.  
  123.   protected TreeControl treeControl = new TreeControl();
  124.   protected GraphLocation rootNode = null;
  125.   protected GraphLocation currentLocaleNode = null;
  126.   protected WritableGraphSelection selection = new SingleGraphSelection();
  127.   protected Vector localeChangeListeners = new Vector();
  128.   protected Collator collator = null;
  129.   protected Image currentSelectionImage = null;
  130.   protected Image rootImage = null;
  131.  
  132.   // display styles, public constants
  133.   public static final int ISO = 0;               // ISO two-letter codes
  134.   public static final int TARGET_LOCALE = 1;     // according to the target locale (default)
  135.   public static final int CURRENT_LOCALE = 2;    // according to the current locale
  136.  
  137.   public static final Locale TITLE_LOCALE = new Locale("", "", "LocaleChooser.TITLE");  
  138.   public static final Locale EVERY_LOCALE = new Locale("", "", "LocaleChooser.ALL");
  139.   public static final Locale [] ALL_LOCALES = { EVERY_LOCALE };
  140.   public static final Locale NULL_LOCALE = new Locale("", "", "LocaleChooser.NULL");
  141.   public static final Locale [] NO_LOCALES = { NULL_LOCALE };
  142.   public static final Locale DEFAULT_RUNTIME_LOCALE = new Locale("", "", "LocaleChooser.DEFAULT");
  143.   public static final String DEFAULT_TITLE = null;
  144.  
  145.   // user-definable properties
  146.   protected Locale [] localeChoices = ALL_LOCALES;
  147.   protected boolean allowMultiSelect = false;
  148.   protected boolean acceptLeafNodesOnly = true;
  149.   protected Locale [] selectedLocales = NO_LOCALES;
  150.   protected int displayStyle = TARGET_LOCALE;
  151.   protected Locale collationLocale = NULL_LOCALE;
  152.   protected String title = DEFAULT_TITLE;
  153.   protected Locale currentLocale;
  154.  
  155.   // resourceable strings
  156.   ResourceBundle resourceBundle = java.util.ResourceBundle.getBundle("borland.samples.intl.beans.resources.LocaleChooserRes");
  157.  
  158.   private Thread animationThread;
  159.   private boolean animationEnabled = false;
  160.   private int animationRate = 100;
  161.   private int imageIndex = 0;
  162.   private Image [] images;
  163.  
  164.   public LocaleChooser() {
  165.     this(ALL_LOCALES);
  166.   }
  167.  
  168.   public LocaleChooser(Locale [] localeChoices) {
  169.     // set LocaleChooser's current locale (which can be 
  170.     // different from the current system locale) to 
  171.     // the default system locale
  172.     currentLocale = Locale.getDefault();
  173.     try {
  174.     // get the image used to signify the current locale
  175.     currentSelectionImage = createImage((ImageProducer) this.getClass().getResource("resources/blueball.gif").getContent());
  176.     // get the spinning globe image for the root of the locale tree
  177.     rootImage = createImage((ImageProducer) this.getClass().getResource("resources/globe.gif").getContent());
  178.     } catch (Exception ex) {
  179.       ex.printStackTrace();
  180.     }
  181.  
  182.     // globe.gif contains 36 frames of a spinning globe, each 50x50 pixels in size
  183.     // Use the MediaTracker to load all the frames into an image array
  184.     MediaTracker mt = new MediaTracker(this);
  185.     images = new Image[36];
  186.     int [] pixels;
  187.     for (int i = 0; i < 36; i++) {
  188.       pixels = new int[2500];
  189.       PixelGrabber pg = new PixelGrabber(rootImage, 0, i * 50, 50, 50, pixels, 0, 50);
  190.       try {
  191.         pg.grabPixels();
  192.       } catch (InterruptedException e) {
  193.         e.printStackTrace();
  194.       }
  195.       MemoryImageSource mis = new MemoryImageSource(50, 50, pixels, 0, 50);
  196.       images[i] = createImage(mis);
  197.       mt.addImage(images[i], i);
  198.     }
  199.     mt.checkAll(true);
  200.  
  201.     // turn the animation on by default
  202.     setAnimationEnabled(true);
  203.  
  204.     //  Call setLocaleChoices to build the locale tree
  205.     setLocaleChoices(localeChoices);
  206.     super.setLayout(new BorderLayout());
  207.     super.add(treeControl, BorderLayout.CENTER);
  208.     treeControl.setExpandByDefault(true);
  209.     treeControl.addActionListener(this);
  210.     treeControl.setSelection(selection);
  211.     treeControl.setViewManager(new BasicViewManager(new CompositeItemPainter(new SelectableTextItemPainter(),
  212.                                                                              new ImageItemPainter(treeControl, Alignment.CENTER))));
  213.   }
  214.  
  215.   // By convention, a null argument is interpreted to mean all available locales at runtime
  216.   public void setLocaleChoices(Locale [] localeChoices) {
  217.     try {
  218.       if ((localeChoices == null) ||
  219.           (localeChoices == NO_LOCALES)) {
  220.         this.localeChoices = NO_LOCALES;
  221.       } else if (localeChoices == ALL_LOCALES) {
  222.         this.localeChoices = ALL_LOCALES;
  223.       } else {
  224.         this.localeChoices = (Locale []) localeChoices.clone();
  225.       }
  226.     } catch (Exception e) {
  227.       e.printStackTrace();      // should never occur!
  228.     }
  229.     buildTree();
  230.   }
  231.  
  232.   public Locale [] getLocaleChoices() {
  233.     Locale [] localeListCopy = null;
  234.     try {
  235.       if ((localeChoices == ALL_LOCALES) || (localeChoices == NO_LOCALES)) {
  236.         return localeChoices;
  237.       }
  238.       localeListCopy = (Locale []) localeChoices.clone();
  239.     } catch (Exception e) {
  240.       e.printStackTrace();      // should never occur!
  241.     }
  242.     return localeListCopy;
  243.   }
  244.  
  245.   // By convention, a null argument is interpreted to mean that no locales are selected
  246.   public void setSelectedLocales(Locale [] selectedLocales) {
  247.     LinkedTreeNode treeNode;
  248.     Locale locale;
  249.  
  250.     if (allowMultiSelect) {
  251.       selection = new BasicGraphSelection();
  252.     } else {
  253.       selection = new SingleGraphSelection();
  254.     }
  255.  
  256.     if (selectedLocales == NO_LOCALES) {
  257.       this.selectedLocales = NO_LOCALES;
  258.       treeControl.setSelection(selection);
  259.       return;
  260.     }
  261.  
  262.     if (selectedLocales == ALL_LOCALES) {
  263.       this.selectedLocales = ALL_LOCALES;
  264.     }
  265.  
  266.     InputIterator iterator = ((LinkedTreeContainer) treeControl.getModel()).elements();
  267.     while (!iterator.atEnd()) {
  268.       treeNode = (LinkedTreeNode) iterator.get();
  269.       locale = ((LocaleDataPair) ((Pair) treeControl.get(treeNode)).first).getLocale();
  270.       if (selectedLocales == ALL_LOCALES) {
  271.         selection.add(treeNode);
  272.       } else {
  273.         for (int i = 0; i < selectedLocales.length; i++) {
  274.           if (locale.equals(selectedLocales[i])) {
  275.             selection.add(treeNode);
  276.             break;
  277.           }
  278.         }
  279.       }
  280.       iterator.advance();
  281.     }
  282.     treeControl.setSelection(selection);
  283.   }
  284.  
  285.   public Locale [] getSelectedLocales() {
  286.     GraphLocation [] selectedNodes = selection.getAll();
  287.     Locale [] selectedLocales = new Locale[selectedNodes.length];
  288.  
  289.     for (int i = 0; i < selectedLocales.length; i++) {
  290.       selectedLocales[i] = ((LocaleDataPair) ((Pair) treeControl.get(selectedNodes[i])).first).getLocale();
  291.     }
  292.  
  293.     if ((selectedLocales.length == 0) || 
  294.         ((selectedLocales.length == 1) && selectedLocales[0].equals(NO_LOCALES))) {
  295.       return NO_LOCALES;
  296.     } else if ((selectedLocales.length == 1) && selectedLocales[0].equals(ALL_LOCALES)) {
  297.       return ALL_LOCALES;
  298.     }
  299.     return selectedLocales;
  300.   }
  301.  
  302.   // ISO, TARGET_LOCALE, CURRENT_LOCALE
  303.   public void setDisplayStyle(int displayStyle) {
  304.     this.displayStyle = displayStyle;
  305.     buildTree();
  306.   }
  307.  
  308.   public int getDisplayStyle() {
  309.     return displayStyle;
  310.   }
  311.  
  312.   public void setAcceptLeafNodesOnly(boolean acceptLeafNodesOnly) {
  313.     this.acceptLeafNodesOnly = acceptLeafNodesOnly;
  314.   }
  315.  
  316.   public boolean isAcceptLeafNodesOnly() {
  317.     return acceptLeafNodesOnly;
  318.   }
  319.  
  320.   public void setAllowMultiSelect(boolean allowMultiSelect) {
  321.  
  322.     if (this.allowMultiSelect == allowMultiSelect) {
  323.       return;
  324.     }
  325.  
  326.     if (allowMultiSelect) {
  327.       selection = new BasicGraphSelection();
  328.     } else {
  329.       selection = new SingleGraphSelection();
  330.     }
  331.  
  332.     treeControl.setSelection(selection);
  333.     this.allowMultiSelect = allowMultiSelect;
  334.   }
  335.  
  336.   public boolean isAllowMultiSelect() {
  337.     return allowMultiSelect;
  338.   }
  339.  
  340.   public void setLocale(Locale locale) {
  341.     super.setLocale(locale);
  342.  
  343.     // update the resourced locale title
  344.     if (title == DEFAULT_TITLE) {
  345.       resourceBundle = java.util.ResourceBundle.getBundle("borland.samples.intl.beans.resources.LocaleChooserRes", locale);
  346.       setTitle(DEFAULT_TITLE);
  347.     }
  348.  
  349.     // update the current locale denoted in the tree
  350.     InputIterator i = ((LinkedTreeContainer) treeControl.getModel()).elements();
  351.     LinkedTreeNode treeNode;
  352.  
  353.     while (!i.atEnd()) {
  354.       treeNode = (LinkedTreeNode) i.get();
  355.       if (((LocaleDataPair) ((Pair) treeControl.get(treeNode)).first).getLocale().equals(locale)) {
  356.         setCurrentLocaleNode(treeNode);
  357.         break;
  358.       }
  359.       i.advance();
  360.     }
  361.     notifyLocaleChangeListeners(locale);
  362.   }
  363.  
  364.   public Locale getLocale() {
  365.     try {
  366.       return super.getLocale();
  367.     } catch (IllegalComponentStateException e) {
  368.       return Locale.getDefault();
  369.     }
  370.   }
  371.  
  372.   public void actionPerformed(ActionEvent e) {
  373.  
  374.     LinkedTreeNode target = (LinkedTreeNode) treeControl.getSubfocus();
  375.  
  376.     if (acceptLeafNodesOnly && (target.hasChildren() == TriState.YES)) {
  377.       return;
  378.     }
  379.  
  380.     Locale newLocale = ((LocaleDataPair) ((Pair) treeControl.get(target)).first).getLocale();
  381.     setCurrentLocaleNode(target);
  382.     setLocale(newLocale);
  383.  
  384.   }
  385.  
  386.   // fill the tree container (the model) with data
  387.   protected void buildTree() {
  388.     Locale [] selectedLocales = getSelectedLocales();
  389.  
  390.     // Create the node used as the root of the tree.
  391.     // Set its title and locale.  The locale of the
  392.     // root node is used as a semaphore in other
  393.     // operations (e.g., to indicate all locales
  394.     // or no locales in the custom property editors)
  395.     LocaleDataPair localeDataPair = new LocaleDataPair(title == DEFAULT_TITLE ? resourceBundle.getString("Select_a_locale") :
  396.                                                                                 title,
  397.                                                        TITLE_LOCALE);
  398.  
  399.     // Create the model to contain the items of the
  400.     // locale tree.  We use a customized model,
  401.     // BasicOrderedTreeContainer, which inserts
  402.     // added nodes in sorted order according to
  403.     // a collation locale (null, or Unicode order,
  404.     // by default)
  405.     BasicOrderedTreeContainer basicOrderedTreeContainer = 
  406.       new BasicOrderedTreeContainer(new Pair(localeDataPair, images[imageIndex]));
  407.     imageIndex = (imageIndex + 1) % images.length;
  408.  
  409.     treeControl.setModel(basicOrderedTreeContainer);
  410.  
  411.     rootNode = treeControl.getRoot();
  412.  
  413.     GraphLocation languageTreeNode = null;
  414.     GraphLocation countryTreeNode = null;
  415.     GraphLocation variantTreeNode = null;
  416.  
  417.     currentLocaleNode = null;
  418.  
  419.     Locale [] treeChoices;
  420.  
  421.     if (localeChoices == NO_LOCALES) {
  422.       treeChoices = new Locale [] { };
  423.     } else if (localeChoices == ALL_LOCALES) {
  424.       treeChoices = getAllRuntimeLocales();
  425.     } else {
  426.       treeChoices = localeChoices;
  427.     }
  428.  
  429.     // Iterate through the array of display locales,
  430.     // adding them to the model.  The model itself
  431.     // uses a BinaryComparator to sort items added
  432.     // to the tree.  LocaleChooser implements the
  433.     // BinaryComparator interface, and returns a
  434.     // value based on the setting of its collationLocale
  435.     // property.
  436.     for (int i = 0; i < treeChoices.length; i++) {
  437.       if (treeChoices[i].getLanguage().length() != 0) {
  438.         // As a default, set the display language in the language of the locale itself
  439.         localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayLanguage(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), ""));
  440.         if (displayStyle == ISO) {
  441.           localeDataPair.setText(treeChoices[i].getLanguage());
  442.         } else if (displayStyle == CURRENT_LOCALE) {
  443.           localeDataPair.setText(treeChoices[i].getDisplayLanguage(getLocale()));
  444.         }
  445.         // In case a language string couldn't be found (the JDK LocaleElements classes
  446.         // don't have complete data for every locale), try using the English name.
  447.         if (localeDataPair.getText().length() == 0) {
  448.           localeDataPair.setText(treeChoices[i].getDisplayLanguage(Locale.US));
  449.         }
  450.         // If that can't be found, then use the ISO name.
  451.         if (localeDataPair.getText().length() == 0) {
  452.           localeDataPair.setText(treeChoices[i].getLanguage());
  453.         }
  454.         languageTreeNode =
  455.           basicOrderedTreeContainer.addOrderedChild(rootNode,
  456.                                                     new Pair(localeDataPair, null),
  457.                                                     this);
  458.         if (getLocale().getLanguage().equals(treeChoices[i].getLanguage())) {
  459.           setCurrentLocaleNode(languageTreeNode);
  460.         }
  461.       } else {
  462.         continue;
  463.       }
  464.       if (treeChoices[i].getCountry().length() != 0) {
  465.         // As a default, set the display language in the language of the locale itself
  466.         localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayCountry(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), treeChoices[i].getCountry()));
  467.         if (displayStyle == ISO) {
  468.           localeDataPair.setText(treeChoices[i].getCountry());
  469.         } else if (displayStyle == CURRENT_LOCALE) {
  470.           localeDataPair.setText(treeChoices[i].getDisplayCountry(getLocale()));
  471.         }
  472.         // In case a language string couldn't be found (the JDK LocaleElements classes
  473.         // don't have complete data for every locale), try using the English name.
  474.         if (localeDataPair.getText().length() == 0) {
  475.           localeDataPair.setText(treeChoices[i].getDisplayCountry(Locale.US));
  476.         }
  477.         // If that can't be found, then use the ISO name.
  478.         if (localeDataPair.getText().length() == 0) {
  479.           localeDataPair.setText(treeChoices[i].getCountry());
  480.         }
  481.         countryTreeNode =
  482.           basicOrderedTreeContainer.addOrderedChild(languageTreeNode,
  483.                                                     new Pair(localeDataPair, null),
  484.                                                     this);
  485.         if (getLocale().getLanguage().equals(treeChoices[i].getLanguage()) &&
  486.             getLocale().getCountry().equals(treeChoices[i].getCountry())) {
  487.           setCurrentLocaleNode(countryTreeNode);
  488.         }
  489.       } else {
  490.         continue;
  491.       }
  492.       if (treeChoices[i].getVariant().length() != 0) {
  493.         localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayVariant(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), treeChoices[i].getCountry(), treeChoices[i].getVariant()));
  494.         if (displayStyle == ISO) {
  495.           localeDataPair.setText(treeChoices[i].getVariant());
  496.         } else if (displayStyle == CURRENT_LOCALE) {
  497.           localeDataPair.setText(treeChoices[i].getDisplayVariant(getLocale()));
  498.         }
  499.         variantTreeNode =
  500.           basicOrderedTreeContainer.addOrderedChild(countryTreeNode,
  501.                                                     new Pair(localeDataPair, null),
  502.                                                     this);
  503.         if (getLocale().equals(treeChoices[i])) {
  504.           setCurrentLocaleNode(variantTreeNode);
  505.         }
  506.       }
  507.     }
  508.  
  509.     setSelectedLocales(selectedLocales);
  510.   }
  511.  
  512.   public void setTitle(String title) {
  513.     this.title = title;
  514.     LocaleDataPair oldLocaleDataPair = (LocaleDataPair) ((Pair) treeControl.get(rootNode)).first;
  515.     LocaleDataPair newLocaleDataPair = new LocaleDataPair(title == DEFAULT_TITLE ? resourceBundle.getString("Select_a_locale") :
  516.                                                                                    title,
  517.                                                           oldLocaleDataPair.getLocale());
  518.     treeControl.set(rootNode, new Pair(newLocaleDataPair, images[imageIndex]));
  519.     imageIndex = (imageIndex + 1) % images.length;
  520.   }
  521.  
  522.   public String getTitle() {
  523.     return title;
  524.   }
  525.  
  526.   // a null value for the collationLocale means no specified Locale, i.e., Unicode collation order
  527.   public void setCollationLocale(Locale collationLocale) {
  528.     this.collationLocale = collationLocale;
  529.     if (collationLocale.equals(NULL_LOCALE)) {
  530.       collator = null;
  531.     } else if (collationLocale.equals(DEFAULT_RUNTIME_LOCALE)) {
  532.       collator = Collator.getInstance();
  533.     } else {
  534.       collator = Collator.getInstance(collationLocale);
  535.     }
  536.   }
  537.  
  538.   public Locale getCollationLocale() {
  539.     return collationLocale;
  540.   }
  541.  
  542.   public Dimension getMinimumSize() {
  543.     return treeControl.getMinimumSize();
  544.   }
  545.  
  546.   public Dimension getPreferredSize() {
  547.     Dimension dimension = treeControl.getPreferredSize();
  548.     return new Dimension(dimension.width + 5, dimension.height + 5);
  549.   }
  550.  
  551.   private void setCurrentLocaleNode(GraphLocation node) {
  552.     // don't do anything if the same node is selected again
  553.     if (currentLocaleNode == node) {
  554.       return;
  555.     }
  556.  
  557.     // only accept the new node if it is a more specific locale than the old node
  558.     if ((currentLocaleNode != null) && (node != null)) {
  559.       Locale currentLocale = ((LocaleDataPair) ((Pair) treeControl.get(currentLocaleNode)).first).getLocale();
  560.       Locale newLocale = ((LocaleDataPair) ((Pair) treeControl.get(node)).first).getLocale();
  561.       if (newLocale.getCountry().equals("") && !currentLocale.getCountry().equals("")) {
  562.         return;
  563.       }
  564.     }
  565.  
  566.     // unmark the old node
  567.     if (currentLocaleNode != null) {
  568.       Pair oldPair = (Pair) treeControl.get(currentLocaleNode);
  569.       treeControl.set(currentLocaleNode, new Pair(oldPair.first, null));
  570.     }
  571.     // mark the new node
  572.     if (node != null) {
  573.       currentLocaleNode = node;
  574.       Pair oldPair = (Pair) treeControl.get(currentLocaleNode);
  575.       treeControl.set(currentLocaleNode, new Pair(oldPair.first, currentSelectionImage));
  576.     }    
  577.   }
  578.  
  579.   private Locale [] getAllRuntimeLocales() {
  580.     return java.text.NumberFormat.getAvailableLocales();
  581.   }
  582.  
  583.   // event-handling 
  584.   public synchronized void addLocaleChangeListener(LocaleChangeListener listener) {
  585.     localeChangeListeners.addElement(listener);
  586.   }
  587.  
  588.   public synchronized void removeLocaleChangeListener(LocaleChangeListener listener) {
  589.     localeChangeListeners.removeElement(listener);
  590.   }
  591.                                                         
  592.   protected void notifyLocaleChangeListeners(Locale locale) {
  593.     LocaleChangeEvent localeChangeEvent = new LocaleChangeEvent(this, locale);
  594.     Vector listeners;
  595.  
  596.     Component component = this;
  597.     Frame frame = null;
  598.  
  599.     while ((component = component.getParent()) != null) {
  600.       if (component instanceof Frame) {
  601.         frame = (Frame) component;
  602.         break;
  603.       }
  604.     }
  605.  
  606.     if (frame == null) {
  607.       frame = new Frame();
  608.     }
  609.     this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  610.     frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  611.  
  612.     synchronized (this) {
  613.       listeners = (Vector) localeChangeListeners.clone();
  614.     }
  615.     for (int i = 0; i < listeners.size(); i++) {
  616.       ((LocaleChangeListener) listeners.elementAt(i)).localeChanged(localeChangeEvent);
  617.     }
  618.     frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  619.     this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  620.   }
  621.  
  622.  
  623.   public void addSelectionListener(GraphSelectionListener listener) {
  624.     treeControl.addSelectionListener(listener);
  625.   }
  626.  
  627.   public void removeSelectionListener(GraphSelectionListener listener) {
  628.     treeControl.removeSelectionListener(listener);
  629.   }
  630.  
  631.   // implementation of BinaryComparator interface
  632.   public int compare(Object item1, Object item2) {
  633.     if (collator == null) {
  634.       return ((Pair) item1).first.toString().compareTo(((Pair) item2).first.toString());
  635.     } else {
  636.       return collator.compare(((Pair) item1).first.toString(),
  637.                               ((Pair) item2).first.toString());
  638.     }
  639.   }
  640.  
  641.   public synchronized void setAnimationEnabled(boolean animationEnabled) {
  642.     this.animationEnabled = animationEnabled;
  643.     if (animationEnabled && animationThread == null) {
  644.       animationThread = new Thread(this);
  645.       animationThread.start();
  646.     }
  647.     if (animationEnabled) {
  648.       notify();
  649.     } else {
  650.       imageIndex = 0;
  651.       Pair oldPair = (Pair) treeControl.get(rootNode);
  652.       oldPair.second = images[imageIndex];
  653.       treeControl.touched(rootNode);
  654.     }
  655.   }
  656.  
  657.   public boolean getAnimationEnabled() {
  658.     return animationEnabled;
  659.   }
  660.  
  661.   // implementation of Runnable interface
  662.   public void run() {
  663.     try {
  664.       while (true) {
  665.         synchronized (this) {
  666.           while (!animationEnabled) {
  667.             wait();
  668.           }
  669.         }
  670.         if (rootNode != null) {
  671.           Pair oldPair = (Pair) treeControl.get(rootNode);
  672.           oldPair.second = images[imageIndex];
  673.           treeControl.touched(rootNode);
  674.           imageIndex = (imageIndex + 1) % images.length;
  675.         }
  676.         Thread.sleep(animationRate);
  677.       }
  678.     } catch (InterruptedException e) {
  679.     }
  680.   }
  681.  
  682.   public Component add(Component comp) {
  683.     return null;
  684.   }
  685.   public Component add(String name, Component comp) {
  686.     return null;
  687.   }
  688.   public Component add(Component comp, int index) {
  689.     return null;
  690.   }
  691.   public void add(Component comp, Object constraints) {}
  692.   public void add(Component comp, Object constraints, int index) {}
  693.   public void remove(int index) {}
  694.   public void remove(Component comp) {}
  695.   public void removeAll() {}
  696.   public void setLayout(LayoutManager mgr) {}
  697.   public LayoutManager getLayout() {
  698.     return super.getLayout();
  699.   }
  700.  
  701. }
  702.  
  703. // need to override the Pair.toString() method to return the correct string for TextItemPainter()
  704. class LocaleDataPair extends Pair {
  705.   String textItem;
  706.   Locale locale;
  707.  
  708.   public LocaleDataPair(String textItem, Locale locale) {
  709.     this.textItem = textItem;
  710.     this.locale = locale;
  711.   }
  712.  
  713.   public String toString() {
  714.     return textItem;
  715.   }
  716.  
  717.   public Locale getLocale() {
  718.     return locale;
  719.   }
  720.  
  721.   public void setLocale(Locale locale) {
  722.     this.locale = locale;
  723.   }
  724.  
  725.   public void setText(String textItem) {
  726.     this.textItem = textItem;
  727.   }
  728.  
  729.   public String getText() {
  730.     return textItem;
  731.   }
  732.  
  733. }
  734.  
  735.