home *** CD-ROM | disk | FTP | other *** search
Wrap
Java Source | 1998-05-08 | 26.9 KB | 735 lines
/* * Copyright (c) 1997-1998 Borland International, Inc. All Rights Reserved. * * This SOURCE CODE FILE, which has been provided by Borland as part * of a Borland product for use ONLY by licensed users of the product, * includes CONFIDENTIAL and PROPRIETARY information of Borland. * * USE OF THIS SOFTWARE IS GOVERNED BY THE TERMS AND CONDITIONS * OF THE LICENSE STATEMENT AND LIMITED WARRANTY FURNISHED WITH * THE PRODUCT. * * IN PARTICULAR, YOU WILL INDEMNIFY AND HOLD BORLAND, ITS RELATED * COMPANIES AND ITS SUPPLIERS, HARMLESS FROM AND AGAINST ANY CLAIMS * OR LIABILITIES ARISING OUT OF THE USE, REPRODUCTION, OR DISTRIBUTION * OF YOUR PROGRAMS, INCLUDING ANY CLAIMS OR LIABILITIES ARISING OUT OF * OR RESULTING FROM THE USE, MODIFICATION, OR DISTRIBUTION OF PROGRAMS * OR FILES CREATED FROM, BASED ON, AND/OR DERIVED FROM THIS SOURCE * CODE FILE. */ package borland.samples.intl.beans; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.beans.*; import java.net.*; import java.text.*; import java.util.*; import borland.jbcl.model.*; import borland.jbcl.control.*; import borland.jbcl.view.*; import borland.jbcl.util.*; import com.objectspace.jgl.*; import borland.samples.intl.beans.event.*; import borland.samples.intl.beans.resources.LocaleChooserRes_en; /** * The LocaleChooser sample Java Bean is an example of an * internationalized Java Bean created using the JBCL TreeControl. * Its properties are customizable in the designer via custom property * editors, which are also internationalized. LocaleChooser's bean * info class extends java.beans.SimpleBeanInfo as an example of * how to create a bean info class from the JDK bean info helper class. * *<p> * LocaleChooser allows a user to select a locale from among a tree of * locales. When the LocaleChooser first appears, the current locale * (if present in the tree) is denoted by a blue ball adjacent to the * locale. Internal nodes of the locale tree are language names, the * leaves are country names. A user selects a locale by * double-clicking or pressing [Enter] on one of the leaves of the * locale tree. Note that the LocaleChooser does not automatically * change the current actual VM locale, but rather returns the * selected locale and updates its own internal state for the selected * locale. A localeChangeListener, registered with the LocaleChooser, * can set the current system default locale. *<p> * Language and country names can be displayed in one of three * different formats--ISO, locale's native language, and current * locale's language. *<p> * LocaleChooser takes advantage of the JBCL Model-View architecture * to perform animation within the tree. A separate thread is used * to send periodic timer events to the LocaleChooser, which updates * the currently displayed frame of the image. *<p> * LocaleChooser also uses its own specialized version of the * BasicTreeContainer, BasicOrderedTreeContainer, to sort node labels * as they are added to the locale tree. *<p> * LocaleChooserBeanInfo uses the java.beans.SimpleBeanInfo class to * provide bean info. The bean info itself is resourced, so the * fly-over brief hints providing a description of each property * appear in the language of the current locale (if available). *<p> * Customizable Properties *<ul> *<li>localeChoices * <p>array of locales to display in locale tree *<li>displayStyle * <p>determines whether locale names should be displayed as ISO * abbreviations, or localized names for the target locale itself, or * the user's current locale (if possible) *<li>selectedLocales * <p>array of locales to display as initially selected in locale * tree *<li>allowMultiSelect * <p>whether or not to allow multiple locales to be visually selected *<li>acceptLeafNodesOnly * <p>whether or not to allow only leaf nodes *(as opposed to internal * nodes) of the locale tree to be chosen *<li>collationLocale * <p>locale used for (vertical) sorting of locale names in the tree *<li>locale * <p>the current locale of the LocaleChooser *<li>title * <p>title to display at root of locale tree *<li>animationEnabled * <p>whether to enable or disable the globe animation (used when * LocaleChooser is stopped while running as part of an applet) *</ul> *<p> * Events * The LocaleChooser bean fires a LocaleChangeEvent when a user * selects a new locale. *<p> * <I>Special thanks</i> to Mr. Silviu Marghescu for graciously * allowing me to use his wonderful globe.gif in this sample Java * Bean. He wishes it be known that: *<p> * The globe.gif image was designed and is owned by Silviu Marghescu * (silviu@emaginet.com). Permission to use and redistribute is * granted to Borland International. Third parties should contact the * author before using the image in commercial products. *<p> * Silviu Marghescu *<p> * Thank you very much Mr. Marghescu! (dcy) */ public class LocaleChooser extends BevelPanel implements ActionListener, BinaryComparator, Runnable { protected TreeControl treeControl = new TreeControl(); protected GraphLocation rootNode = null; protected GraphLocation currentLocaleNode = null; protected WritableGraphSelection selection = new SingleGraphSelection(); protected Vector localeChangeListeners = new Vector(); protected Collator collator = null; protected Image currentSelectionImage = null; protected Image rootImage = null; // display styles, public constants public static final int ISO = 0; // ISO two-letter codes public static final int TARGET_LOCALE = 1; // according to the target locale (default) public static final int CURRENT_LOCALE = 2; // according to the current locale public static final Locale TITLE_LOCALE = new Locale("", "", "LocaleChooser.TITLE"); public static final Locale EVERY_LOCALE = new Locale("", "", "LocaleChooser.ALL"); public static final Locale [] ALL_LOCALES = { EVERY_LOCALE }; public static final Locale NULL_LOCALE = new Locale("", "", "LocaleChooser.NULL"); public static final Locale [] NO_LOCALES = { NULL_LOCALE }; public static final Locale DEFAULT_RUNTIME_LOCALE = new Locale("", "", "LocaleChooser.DEFAULT"); public static final String DEFAULT_TITLE = null; // user-definable properties protected Locale [] localeChoices = ALL_LOCALES; protected boolean allowMultiSelect = false; protected boolean acceptLeafNodesOnly = true; protected Locale [] selectedLocales = NO_LOCALES; protected int displayStyle = TARGET_LOCALE; protected Locale collationLocale = NULL_LOCALE; protected String title = DEFAULT_TITLE; protected Locale currentLocale; // resourceable strings ResourceBundle resourceBundle = java.util.ResourceBundle.getBundle("borland.samples.intl.beans.resources.LocaleChooserRes"); private Thread animationThread; private boolean animationEnabled = false; private int animationRate = 100; private int imageIndex = 0; private Image [] images; public LocaleChooser() { this(ALL_LOCALES); } public LocaleChooser(Locale [] localeChoices) { // set LocaleChooser's current locale (which can be // different from the current system locale) to // the default system locale currentLocale = Locale.getDefault(); try { // get the image used to signify the current locale currentSelectionImage = createImage((ImageProducer) this.getClass().getResource("resources/blueball.gif").getContent()); // get the spinning globe image for the root of the locale tree rootImage = createImage((ImageProducer) this.getClass().getResource("resources/globe.gif").getContent()); } catch (Exception ex) { ex.printStackTrace(); } // globe.gif contains 36 frames of a spinning globe, each 50x50 pixels in size // Use the MediaTracker to load all the frames into an image array MediaTracker mt = new MediaTracker(this); images = new Image[36]; int [] pixels; for (int i = 0; i < 36; i++) { pixels = new int[2500]; PixelGrabber pg = new PixelGrabber(rootImage, 0, i * 50, 50, 50, pixels, 0, 50); try { pg.grabPixels(); } catch (InterruptedException e) { e.printStackTrace(); } MemoryImageSource mis = new MemoryImageSource(50, 50, pixels, 0, 50); images[i] = createImage(mis); mt.addImage(images[i], i); } mt.checkAll(true); // turn the animation on by default setAnimationEnabled(true); // Call setLocaleChoices to build the locale tree setLocaleChoices(localeChoices); super.setLayout(new BorderLayout()); super.add(treeControl, BorderLayout.CENTER); treeControl.setExpandByDefault(true); treeControl.addActionListener(this); treeControl.setSelection(selection); treeControl.setViewManager(new BasicViewManager(new CompositeItemPainter(new SelectableTextItemPainter(), new ImageItemPainter(treeControl, Alignment.CENTER)))); } // By convention, a null argument is interpreted to mean all available locales at runtime public void setLocaleChoices(Locale [] localeChoices) { try { if ((localeChoices == null) || (localeChoices == NO_LOCALES)) { this.localeChoices = NO_LOCALES; } else if (localeChoices == ALL_LOCALES) { this.localeChoices = ALL_LOCALES; } else { this.localeChoices = (Locale []) localeChoices.clone(); } } catch (Exception e) { e.printStackTrace(); // should never occur! } buildTree(); } public Locale [] getLocaleChoices() { Locale [] localeListCopy = null; try { if ((localeChoices == ALL_LOCALES) || (localeChoices == NO_LOCALES)) { return localeChoices; } localeListCopy = (Locale []) localeChoices.clone(); } catch (Exception e) { e.printStackTrace(); // should never occur! } return localeListCopy; } // By convention, a null argument is interpreted to mean that no locales are selected public void setSelectedLocales(Locale [] selectedLocales) { LinkedTreeNode treeNode; Locale locale; if (allowMultiSelect) { selection = new BasicGraphSelection(); } else { selection = new SingleGraphSelection(); } if (selectedLocales == NO_LOCALES) { this.selectedLocales = NO_LOCALES; treeControl.setSelection(selection); return; } if (selectedLocales == ALL_LOCALES) { this.selectedLocales = ALL_LOCALES; } InputIterator iterator = ((LinkedTreeContainer) treeControl.getModel()).elements(); while (!iterator.atEnd()) { treeNode = (LinkedTreeNode) iterator.get(); locale = ((LocaleDataPair) ((Pair) treeControl.get(treeNode)).first).getLocale(); if (selectedLocales == ALL_LOCALES) { selection.add(treeNode); } else { for (int i = 0; i < selectedLocales.length; i++) { if (locale.equals(selectedLocales[i])) { selection.add(treeNode); break; } } } iterator.advance(); } treeControl.setSelection(selection); } public Locale [] getSelectedLocales() { GraphLocation [] selectedNodes = selection.getAll(); Locale [] selectedLocales = new Locale[selectedNodes.length]; for (int i = 0; i < selectedLocales.length; i++) { selectedLocales[i] = ((LocaleDataPair) ((Pair) treeControl.get(selectedNodes[i])).first).getLocale(); } if ((selectedLocales.length == 0) || ((selectedLocales.length == 1) && selectedLocales[0].equals(NO_LOCALES))) { return NO_LOCALES; } else if ((selectedLocales.length == 1) && selectedLocales[0].equals(ALL_LOCALES)) { return ALL_LOCALES; } return selectedLocales; } // ISO, TARGET_LOCALE, CURRENT_LOCALE public void setDisplayStyle(int displayStyle) { this.displayStyle = displayStyle; buildTree(); } public int getDisplayStyle() { return displayStyle; } public void setAcceptLeafNodesOnly(boolean acceptLeafNodesOnly) { this.acceptLeafNodesOnly = acceptLeafNodesOnly; } public boolean isAcceptLeafNodesOnly() { return acceptLeafNodesOnly; } public void setAllowMultiSelect(boolean allowMultiSelect) { if (this.allowMultiSelect == allowMultiSelect) { return; } if (allowMultiSelect) { selection = new BasicGraphSelection(); } else { selection = new SingleGraphSelection(); } treeControl.setSelection(selection); this.allowMultiSelect = allowMultiSelect; } public boolean isAllowMultiSelect() { return allowMultiSelect; } public void setLocale(Locale locale) { super.setLocale(locale); // update the resourced locale title if (title == DEFAULT_TITLE) { resourceBundle = java.util.ResourceBundle.getBundle("borland.samples.intl.beans.resources.LocaleChooserRes", locale); setTitle(DEFAULT_TITLE); } // update the current locale denoted in the tree InputIterator i = ((LinkedTreeContainer) treeControl.getModel()).elements(); LinkedTreeNode treeNode; while (!i.atEnd()) { treeNode = (LinkedTreeNode) i.get(); if (((LocaleDataPair) ((Pair) treeControl.get(treeNode)).first).getLocale().equals(locale)) { setCurrentLocaleNode(treeNode); break; } i.advance(); } notifyLocaleChangeListeners(locale); } public Locale getLocale() { try { return super.getLocale(); } catch (IllegalComponentStateException e) { return Locale.getDefault(); } } public void actionPerformed(ActionEvent e) { LinkedTreeNode target = (LinkedTreeNode) treeControl.getSubfocus(); if (acceptLeafNodesOnly && (target.hasChildren() == TriState.YES)) { return; } Locale newLocale = ((LocaleDataPair) ((Pair) treeControl.get(target)).first).getLocale(); setCurrentLocaleNode(target); setLocale(newLocale); } // fill the tree container (the model) with data protected void buildTree() { Locale [] selectedLocales = getSelectedLocales(); // Create the node used as the root of the tree. // Set its title and locale. The locale of the // root node is used as a semaphore in other // operations (e.g., to indicate all locales // or no locales in the custom property editors) LocaleDataPair localeDataPair = new LocaleDataPair(title == DEFAULT_TITLE ? resourceBundle.getString("Select_a_locale") : title, TITLE_LOCALE); // Create the model to contain the items of the // locale tree. We use a customized model, // BasicOrderedTreeContainer, which inserts // added nodes in sorted order according to // a collation locale (null, or Unicode order, // by default) BasicOrderedTreeContainer basicOrderedTreeContainer = new BasicOrderedTreeContainer(new Pair(localeDataPair, images[imageIndex])); imageIndex = (imageIndex + 1) % images.length; treeControl.setModel(basicOrderedTreeContainer); rootNode = treeControl.getRoot(); GraphLocation languageTreeNode = null; GraphLocation countryTreeNode = null; GraphLocation variantTreeNode = null; currentLocaleNode = null; Locale [] treeChoices; if (localeChoices == NO_LOCALES) { treeChoices = new Locale [] { }; } else if (localeChoices == ALL_LOCALES) { treeChoices = getAllRuntimeLocales(); } else { treeChoices = localeChoices; } // Iterate through the array of display locales, // adding them to the model. The model itself // uses a BinaryComparator to sort items added // to the tree. LocaleChooser implements the // BinaryComparator interface, and returns a // value based on the setting of its collationLocale // property. for (int i = 0; i < treeChoices.length; i++) { if (treeChoices[i].getLanguage().length() != 0) { // As a default, set the display language in the language of the locale itself localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayLanguage(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), "")); if (displayStyle == ISO) { localeDataPair.setText(treeChoices[i].getLanguage()); } else if (displayStyle == CURRENT_LOCALE) { localeDataPair.setText(treeChoices[i].getDisplayLanguage(getLocale())); } // In case a language string couldn't be found (the JDK LocaleElements classes // don't have complete data for every locale), try using the English name. if (localeDataPair.getText().length() == 0) { localeDataPair.setText(treeChoices[i].getDisplayLanguage(Locale.US)); } // If that can't be found, then use the ISO name. if (localeDataPair.getText().length() == 0) { localeDataPair.setText(treeChoices[i].getLanguage()); } languageTreeNode = basicOrderedTreeContainer.addOrderedChild(rootNode, new Pair(localeDataPair, null), this); if (getLocale().getLanguage().equals(treeChoices[i].getLanguage())) { setCurrentLocaleNode(languageTreeNode); } } else { continue; } if (treeChoices[i].getCountry().length() != 0) { // As a default, set the display language in the language of the locale itself localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayCountry(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), treeChoices[i].getCountry())); if (displayStyle == ISO) { localeDataPair.setText(treeChoices[i].getCountry()); } else if (displayStyle == CURRENT_LOCALE) { localeDataPair.setText(treeChoices[i].getDisplayCountry(getLocale())); } // In case a language string couldn't be found (the JDK LocaleElements classes // don't have complete data for every locale), try using the English name. if (localeDataPair.getText().length() == 0) { localeDataPair.setText(treeChoices[i].getDisplayCountry(Locale.US)); } // If that can't be found, then use the ISO name. if (localeDataPair.getText().length() == 0) { localeDataPair.setText(treeChoices[i].getCountry()); } countryTreeNode = basicOrderedTreeContainer.addOrderedChild(languageTreeNode, new Pair(localeDataPair, null), this); if (getLocale().getLanguage().equals(treeChoices[i].getLanguage()) && getLocale().getCountry().equals(treeChoices[i].getCountry())) { setCurrentLocaleNode(countryTreeNode); } } else { continue; } if (treeChoices[i].getVariant().length() != 0) { localeDataPair = new LocaleDataPair(treeChoices[i].getDisplayVariant(treeChoices[i]), new Locale(treeChoices[i].getLanguage(), treeChoices[i].getCountry(), treeChoices[i].getVariant())); if (displayStyle == ISO) { localeDataPair.setText(treeChoices[i].getVariant()); } else if (displayStyle == CURRENT_LOCALE) { localeDataPair.setText(treeChoices[i].getDisplayVariant(getLocale())); } variantTreeNode = basicOrderedTreeContainer.addOrderedChild(countryTreeNode, new Pair(localeDataPair, null), this); if (getLocale().equals(treeChoices[i])) { setCurrentLocaleNode(variantTreeNode); } } } setSelectedLocales(selectedLocales); } public void setTitle(String title) { this.title = title; LocaleDataPair oldLocaleDataPair = (LocaleDataPair) ((Pair) treeControl.get(rootNode)).first; LocaleDataPair newLocaleDataPair = new LocaleDataPair(title == DEFAULT_TITLE ? resourceBundle.getString("Select_a_locale") : title, oldLocaleDataPair.getLocale()); treeControl.set(rootNode, new Pair(newLocaleDataPair, images[imageIndex])); imageIndex = (imageIndex + 1) % images.length; } public String getTitle() { return title; } // a null value for the collationLocale means no specified Locale, i.e., Unicode collation order public void setCollationLocale(Locale collationLocale) { this.collationLocale = collationLocale; if (collationLocale.equals(NULL_LOCALE)) { collator = null; } else if (collationLocale.equals(DEFAULT_RUNTIME_LOCALE)) { collator = Collator.getInstance(); } else { collator = Collator.getInstance(collationLocale); } } public Locale getCollationLocale() { return collationLocale; } public Dimension getMinimumSize() { return treeControl.getMinimumSize(); } public Dimension getPreferredSize() { Dimension dimension = treeControl.getPreferredSize(); return new Dimension(dimension.width + 5, dimension.height + 5); } private void setCurrentLocaleNode(GraphLocation node) { // don't do anything if the same node is selected again if (currentLocaleNode == node) { return; } // only accept the new node if it is a more specific locale than the old node if ((currentLocaleNode != null) && (node != null)) { Locale currentLocale = ((LocaleDataPair) ((Pair) treeControl.get(currentLocaleNode)).first).getLocale(); Locale newLocale = ((LocaleDataPair) ((Pair) treeControl.get(node)).first).getLocale(); if (newLocale.getCountry().equals("") && !currentLocale.getCountry().equals("")) { return; } } // unmark the old node if (currentLocaleNode != null) { Pair oldPair = (Pair) treeControl.get(currentLocaleNode); treeControl.set(currentLocaleNode, new Pair(oldPair.first, null)); } // mark the new node if (node != null) { currentLocaleNode = node; Pair oldPair = (Pair) treeControl.get(currentLocaleNode); treeControl.set(currentLocaleNode, new Pair(oldPair.first, currentSelectionImage)); } } private Locale [] getAllRuntimeLocales() { return java.text.NumberFormat.getAvailableLocales(); } // event-handling public synchronized void addLocaleChangeListener(LocaleChangeListener listener) { localeChangeListeners.addElement(listener); } public synchronized void removeLocaleChangeListener(LocaleChangeListener listener) { localeChangeListeners.removeElement(listener); } protected void notifyLocaleChangeListeners(Locale locale) { LocaleChangeEvent localeChangeEvent = new LocaleChangeEvent(this, locale); Vector listeners; Component component = this; Frame frame = null; while ((component = component.getParent()) != null) { if (component instanceof Frame) { frame = (Frame) component; break; } } if (frame == null) { frame = new Frame(); } this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); synchronized (this) { listeners = (Vector) localeChangeListeners.clone(); } for (int i = 0; i < listeners.size(); i++) { ((LocaleChangeListener) listeners.elementAt(i)).localeChanged(localeChangeEvent); } frame.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } public void addSelectionListener(GraphSelectionListener listener) { treeControl.addSelectionListener(listener); } public void removeSelectionListener(GraphSelectionListener listener) { treeControl.removeSelectionListener(listener); } // implementation of BinaryComparator interface public int compare(Object item1, Object item2) { if (collator == null) { return ((Pair) item1).first.toString().compareTo(((Pair) item2).first.toString()); } else { return collator.compare(((Pair) item1).first.toString(), ((Pair) item2).first.toString()); } } public synchronized void setAnimationEnabled(boolean animationEnabled) { this.animationEnabled = animationEnabled; if (animationEnabled && animationThread == null) { animationThread = new Thread(this); animationThread.start(); } if (animationEnabled) { notify(); } else { imageIndex = 0; Pair oldPair = (Pair) treeControl.get(rootNode); oldPair.second = images[imageIndex]; treeControl.touched(rootNode); } } public boolean getAnimationEnabled() { return animationEnabled; } // implementation of Runnable interface public void run() { try { while (true) { synchronized (this) { while (!animationEnabled) { wait(); } } if (rootNode != null) { Pair oldPair = (Pair) treeControl.get(rootNode); oldPair.second = images[imageIndex]; treeControl.touched(rootNode); imageIndex = (imageIndex + 1) % images.length; } Thread.sleep(animationRate); } } catch (InterruptedException e) { } } public Component add(Component comp) { return null; } public Component add(String name, Component comp) { return null; } public Component add(Component comp, int index) { return null; } public void add(Component comp, Object constraints) {} public void add(Component comp, Object constraints, int index) {} public void remove(int index) {} public void remove(Component comp) {} public void removeAll() {} public void setLayout(LayoutManager mgr) {} public LayoutManager getLayout() { return super.getLayout(); } } // need to override the Pair.toString() method to return the correct string for TextItemPainter() class LocaleDataPair extends Pair { String textItem; Locale locale; public LocaleDataPair(String textItem, Locale locale) { this.textItem = textItem; this.locale = locale; } public String toString() { return textItem; } public Locale getLocale() { return locale; } public void setLocale(Locale locale) { this.locale = locale; } public void setText(String textItem) { this.textItem = textItem; } public String getText() { return textItem; } }