/** * */ package javax.swing.origamist; import java.awt.Dimension; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.SwingConstants; import cz.cuni.mff.peckam.java.origamist.services.ServiceLocator; import cz.cuni.mff.peckam.java.origamist.services.TooltipFactory; import cz.cuni.mff.peckam.java.origamist.services.interfaces.ConfigurationManager; /** * A toolbar that has convenience methods for adding buttons with locale-dependent values. * * @author Martin Pecka */ public class OrigamistToolBar extends JToolBar { /** */ private static final long serialVersionUID = 7248513028554332970L; /** The application localization texts. */ protected ResourceBundle messages; /** The name of the resource bundle this toolbar will fetch the localizations from. */ protected String bundleName; /** * @param bundleName The name of the resource bundle this toolbar will fetch the localizations from. */ public OrigamistToolBar(String bundleName) { super(); this.bundleName = bundleName; PropertyChangeListener l = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { ResourceBundle oldAppMessages = messages; messages = ResourceBundle.getBundle(OrigamistToolBar.this.bundleName, (Locale) evt.getNewValue()); if (oldAppMessages == null || !oldAppMessages.equals(messages)) firePropertyChange("messages", oldAppMessages, messages); } }; ServiceLocator.get(ConfigurationManager.class).get().addPropertyChangeListener("locale", l); l.propertyChange(new PropertyChangeEvent(this, "locale", null, ServiceLocator.get(ConfigurationManager.class) .get().getLocale())); } /** * Create a normal toolbar button. * * @param action The action the button should invoke. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return A normal toolbar button. */ public JButton createToolbarButton(Action action, final String bundleName, final String iconName) { return createToolbarItem(new JButton(), action, bundleName, iconName); } /** * Create a toolbar dropdown button. * * @param action The action the button should invoke when clicked on the main area. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return A toolbar dropdown button. */ public JDropDownButton createToolbarDropdownButton(Action action, final String bundleName, final String iconName) { return createToolbarItem(new JDropDownButton(new JButton()), action, bundleName, iconName); } /** * Create a toolbar dropdown button. * * @param button The button to use for the item. * @param action The action the button should invoke when clicked on the main area. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return A toolbar dropdown button. */ public <T extends JDropDownButton> T createToolbarDropdownButton(T button, Action action, final String bundleName, final String iconName) { return createToolbarItem(button, action, bundleName, iconName); } /** * Create a toolbar dropdown button's item. * * @param action The action the item should invoke. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return A toolbar dropdown button's item. */ public JMenuItem createToolbarDropdownItem(Action action, final String bundleName, final String iconName) { return createToolbarDropdownItem(new JMenuItem(), action, bundleName, iconName); } /** * Create a toolbar dropdown button's item. * * @param item The menu item to setup. * @param action The action the item should invoke. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return A toolbar dropdown button's item. */ public <T extends AbstractButton> T createToolbarDropdownItem(T item, Action action, final String bundleName, final String iconName) { item.setBorder(BorderFactory.createCompoundBorder(item.getBorder(), BorderFactory.createEmptyBorder(3, 0, 3, 0))); return createToolbarItem(item, action, bundleName, iconName); } /** * Configure the given AbstractButton to fire the given action, load its data from the resources beginning with * <code>bundleName</code> and load the icon from <code>/resources/images/</code>, if <code>iconName</code> is not * <code>null</code>. * * These bundle name suffixes are recognized: * <dl> * <dt>"" (empty suffix):</dt> * <dd>the text of the button</dd> * <dt>.hideText</dt> * <dd>if set (to any other value than "false"), don't display the text in the toolbar</dd> * <dt>.tooltip:</dt> * <dd>the tooltip for the button</dd> * <dt>.mnemonic:</dt> * <dd>the mnemonic for the button (the letter that will appear underlined in the button's text)</dd> * <dt>.accelerator:</dt> * <dd>the accelerator to be used with this button; if mnemonic is not set, use the keyCode of this accelerator as * mnemonic</dd> * </dl> * * @param <T> The type of the button. * @param button The button to configure. * @param action The action the button should invoke. * @param bundleName The base of the resource bundle strings used to configure this button. * @param iconName The name that will be appended to the path <code>/resources/images/</code> to find the icon. * @return The button given in <code>button</code> param, configured. */ public <T extends AbstractButton> T createToolbarItem(final T button, final Action action, final String bundleName, final String iconName) { if (bundleName == null) throw new NullPointerException("Tried to create toolbar item without giving the corresponding bundle name."); button.setOpaque(false); button.setAction(action); if (iconName != null) { URL url = getClass().getResource("/resources/images/" + iconName); if (url != null) button.setIcon(new ImageIcon(url)); } final AbstractButton btn; if (button instanceof JDropDownButton) { btn = ((JDropDownButton) button).getMainButton(); } else if (button instanceof JMenuItem) { btn = null; } else { btn = button; } final PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { boolean hideText = false; try { String hide = messages.getString(bundleName + ".hideText"); if (!"false".equals(hide)) hideText = true; } catch (MissingResourceException e) {} try { if (!hideText) button.setText(messages.getString(bundleName)); else button.setText(""); } catch (MissingResourceException e) { button.setText(""); } if (btn != null) { if (btn.getText() == null || btn.getText().length() == 0) { btn.setHorizontalAlignment(SwingConstants.CENTER); } else { btn.setHorizontalAlignment(SwingConstants.LEFT); } } String mnemonic = null; try { mnemonic = messages.getString(bundleName + ".mnemonic"); } catch (MissingResourceException e) {} String accelerator = null; try { accelerator = messages.getString(bundleName + ".accelerator"); } catch (MissingResourceException e) {} KeyStroke accStroke = KeyStroke.getKeyStroke(accelerator); KeyStroke mnemStroke = KeyStroke.getKeyStroke(mnemonic); if (mnemStroke == null) mnemStroke = accStroke; if (evt.getOldValue() != null && (evt.getOldValue() instanceof ResourceBundle)) { try { String oldAcc = ((ResourceBundle) evt.getOldValue()).getString(bundleName + ".accelerator"); KeyStroke oldStroke = KeyStroke.getKeyStroke(oldAcc); if (oldStroke != null) { getInputMap(WHEN_IN_FOCUSED_WINDOW).remove(oldStroke); } } catch (MissingResourceException e) {} } if (accStroke != null) { if ((button instanceof JMenuItem) && !(button instanceof JMenu)) ((JMenuItem) button).setAccelerator(accStroke); // TODO although this should enable the key shortcuts in the application-wide manner, it doesn't do // that... instead, after a key shortcut is used, the user has to click in the application in order // to be able to use another key shortcut getInputMap(WHEN_IN_FOCUSED_WINDOW).put(accStroke, action); getActionMap().put(action, action); } if (mnemStroke != null) { button.setMnemonic(mnemStroke.getKeyCode()); } try { String title = null; try { title = messages.getString(bundleName); } catch (MissingResourceException e) {} String tooltip = ServiceLocator.get(TooltipFactory.class).getDecorated( messages.getString(bundleName + ".tooltip"), title, iconName, accStroke); button.setToolTipText(tooltip); button.getAccessibleContext().setAccessibleDescription(tooltip); } catch (MissingResourceException e) {} } }; listener.propertyChange(new PropertyChangeEvent(this, "messages", null, messages)); addPropertyChangeListener("messages", listener); if (btn != null && btn.getIcon() != null) { if (btn != button) { btn.setMinimumSize(new Dimension(btn.getIcon().getIconWidth() + 3, btn.getIcon().getIconHeight() + 3)); } else { button.setMinimumSize(new Dimension(btn.getIcon().getIconWidth() + 2, btn.getIcon().getIconHeight() + 2)); } } return button; } /** * Create a horizontal "separator" that can have a title and can be used in a {@link JPopupMenu}. * * @param bundleName The resource bundle string used to get this separator's text. * @return A horizontal "separator" that can have a title and can be used in a {@link JPopupMenu}. */ public JTitledSeparator createToolbarDropdownSeparator(final String bundleName) { final JTitledSeparator separator = new JTitledSeparator(""); PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { try { separator.setTitle(messages.getString(bundleName)); } catch (MissingResourceException e) { separator.setTitle(""); } } }; listener.propertyChange(new PropertyChangeEvent(this, "messages", null, messages)); addPropertyChangeListener("messages", listener); return separator; } }