/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.awt; import java.awt.Component; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.event.*; import java.util.*; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.swing.*; import javax.swing.border.*; import org.openide.*; import org.openide.loaders.*; import org.openide.cookies.InstanceCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.Repository; import org.openide.util.Task; import org.openide.util.TaskListener; /** * This class keeps track of the current toolbars and their names. * @author David Peroutka, Libor Kramolis */ public final class ToolbarPool extends JComponent implements Accessible { /** Default ToolbarPool */ private static ToolbarPool defaultPool; /** objects responsible for creation of the window */ private Folder instance; /** DataFolder from which the pool was created */ private DataFolder folder; /** Maps name to <code>Toolbar</code>s */ private Map toolbars; /** Maps name to <code>ToolbarPool.Configuration</code>s */ private Map toolbarConfigs; /** Current name of selected configuration */ private String name = ""; // NOI18N /** Center component */ private Component center; /** Popup menu listener */ private PopupListener listener; /** Accessible context */ private AccessibleContext accessibleContext; /** Name of default toolbar configuration. */ public static final String DEFAULT_CONFIGURATION = "Standard"; // NOI18N private TPTaskListener taskListener; /** * Returns default toolbar pool. * @return default system pool */ public static synchronized ToolbarPool getDefault () { if (defaultPool == null) { FileObject fo = Repository.getDefault().getDefaultFileSystem().findResource("Toolbars"); // NOI18N if (fo == null) throw new IllegalStateException("No Toolbars/"); // NOI18N DataFolder folder = DataFolder.findFolder(fo); defaultPool = new ToolbarPool(folder); // we mustn't do this in constructor to prevent from // nevereding recursive calls to this method. defaultPool.instance.recreate(); } return defaultPool; } static final long serialVersionUID =3420915387298484008L; /** * Creates a new <code>ToolbarPool</code>. Useful for modules that need they * own toolbars. * * @param df the data folder to read toolbar definitions and configurations from * @since 1.5 */ public ToolbarPool (DataFolder df) { accessibleContext = null; folder = df; setLayout (new BorderLayout ()); listener = new PopupListener(); toolbars = new TreeMap(); toolbarConfigs = new TreeMap(); instance = new Folder (df); getAccessibleContext().setAccessibleName(instance.instanceName()); getAccessibleContext().setAccessibleDescription(instance.instanceName()); } /** Allows to wait till the content of the pool is initialized. */ public final void waitFinished () { instance.instanceFinished (); } /** Initialization of new values. * @param toolbars map (String, Toolbar) of toolbars * @param conf map (String, Configuration) of configs */ void update (Map toolbars, Map conf) { this.toolbars = toolbars; this.toolbarConfigs = conf; if (!"".equals(name)) { setConfiguration (name); } } /** Updates the default configuration. */ private synchronized void updateDefault () { JPanel tp = new JPanel (new FlowLayout (FlowLayout.LEFT)); Toolbar[] list = getToolbars (); for (int i = 0; i < list.length; i++) { tp.add (list[i]); } name = ""; // NOI18N revalidate (tp); } /** Activates a configuration. * @param c configuration */ private synchronized void activate (Configuration c) { Component comp = c.activate (); name = c.getName(); revalidate (comp); } /** Sets DnDListener to all Toolbars. */ public void setToolbarsListener (Toolbar.DnDListener l) { Iterator it = toolbars.values().iterator(); while (it.hasNext()) ((Toolbar)it.next()).setDnDListener (l); } /** Uses new component as a cental one. */ private void revalidate (Component c) { if (c != center) { // exchange if (center != null) { remove (center); center.removeMouseListener (listener); } add (center = c, BorderLayout.CENTER); center.addMouseListener (listener); java.awt.Window w = javax.swing.SwingUtilities.windowForComponent (this); if (w != null) { w.validate(); } } } /** * Returns a <code>Toolbar</code> to which this pool maps the given name. * @param name a <code>String</code> that is to be a toolbar's name * @return a <code>Toolbar</code> to which this pool maps the name */ public final Toolbar findToolbar (String name) { return (Toolbar)toolbars.get (name); } /** * Getter for the name of current configuration. * @return the name of current configuration */ public final String getConfiguration () { return name; } /** * Switch to toolbar configuration by specific config name * @param n toolbar configuration name */ public final void setConfiguration (String n) { String old = name; // should be 'instance.waitFinished();' but some bug in isFinished ... if (!instance.isFinished()) { if (taskListener == null) { taskListener = new TPTaskListener(); instance.addTaskListener(taskListener); } taskListener.setConfiguration(n); return; } if (taskListener != null) { instance.removeTaskListener(taskListener); taskListener = null; } Configuration config = null; if (n != null) { config = (Configuration)toolbarConfigs.get (n); } if (config != null) { // if configuration found activate (config); } else if (toolbarConfigs.isEmpty()) { // if no toolbar configuration updateDefault (); } else { // line below commented - bugfix, we need default configuration always when unknown config name is used: // if (center == null) { // bad config name (n) and no configuration activated yet config = (Configuration)toolbarConfigs.get (DEFAULT_CONFIGURATION); if (config == null) { config = (Configuration)toolbarConfigs.values().iterator().next(); } activate (config); } firePropertyChange("configuration", old, name); } /** * @return the <code>DataFolder</code> from which the pool was created. */ public final DataFolder getFolder() { return folder; } /** * Returns the toolbars contained in this pool. * @return the toolbars contained in this pool */ public final synchronized Toolbar[] getToolbars() { Toolbar[] arr = new Toolbar[toolbars.size ()]; return (Toolbar[])toolbars.values ().toArray (arr); } /** * @return the names of toolbar configurations contained in this pool */ public final synchronized String[] getConfigurations () { String[] arr = new String[toolbarConfigs.size ()]; return (String[])toolbarConfigs.keySet ().toArray (arr); } /** Read accessible context * @return - accessible context */ public AccessibleContext getAccessibleContext () { if(accessibleContext == null) { accessibleContext = new AccessibleJComponent() { public AccessibleRole getAccessibleRole() { return AccessibleRole.TOOL_BAR; } }; } return accessibleContext; } /** * This class is used for delayed setting of configuration after instance * creation is finished. It may happen during IDE start that * ToolbarPool.setConfiguration is called before instance is created. */ private class TPTaskListener implements TaskListener { private String conf; TPTaskListener() {} public void taskFinished(Task task) { ToolbarPool.this.setConfiguration(conf); conf = null; } void setConfiguration(String conf) { // #23619: Don't reset already pending configuration to be set. if(this.conf == null) { this.conf = conf; } } } /** * This class can be used to produce a <code>ToolbarPool</code> instance * from the given <code>DataFolder</code>. */ private class Folder extends FolderInstance { private WeakHashMap foldersCache = new WeakHashMap (15); public Folder (DataFolder f) { super (f); } /** * Full name of the data folder's primary file separated by dots. * @return the name */ public String instanceName () { return instanceClass().getName(); } /** * Returns the root class of all objects. * @return Object.class */ public Class instanceClass () { return ToolbarPool.class; } /** * Accepts only cookies that can provide <code>Configuration</code>. * @param cookie the instance cookie to test * @return true if the cookie can provide <code>Configuration</code> */ protected InstanceCookie acceptCookie (InstanceCookie cookie) throws java.io.IOException, ClassNotFoundException { Class cls = cookie.instanceClass(); if (ToolbarPool.Configuration.class.isAssignableFrom (cls)) { return cookie; } if (Component.class.isAssignableFrom (cls)) { return cookie; } return null; } /** * Returns a <code>Toolbar.Folder</code> cookie for the specified * <code>DataFolder</code>. * @param df a <code>DataFolder</code> to create the cookie for * @return a <code>Toolbar.Folder</code> for the specified folder */ protected InstanceCookie acceptFolder (DataFolder df) { InstanceCookie ic = (InstanceCookie)foldersCache.get (df); if (ic == null) { ic = new InstanceSupport.Instance(new Toolbar(df,true)); foldersCache.put (df, ic); } return ic; } /** * Updates the <code>ToolbarPool</code> represented by this folder. * * @param cookies array of instance cookies for the folder * @return the updated <code>ToolbarPool</code> representee */ protected Object createInstance (InstanceCookie[] cookies) throws java.io.IOException, ClassNotFoundException { final int length = cookies.length; final Map toolbars = new TreeMap (); final Map conf = new TreeMap (); for (int i = 0; i < length; i++) { try { Object obj = cookies[i].instanceCreate(); if (obj instanceof Toolbar) { Toolbar toolbar = (Toolbar)obj; // should be done by ToolbarPanel in add method toolbar.removeMouseListener (listener); toolbar.addMouseListener (listener); toolbars.put (toolbar.getName (), toolbar); continue; } if (obj instanceof Configuration) { Configuration config = (Configuration)obj; String name = config.getName (); if (name == null) { name = cookies[i].instanceName (); } conf.put (name, config); continue; } if (obj instanceof Component) { Component comp = (Component)obj; String name = comp.getName (); if (name == null) { name = cookies[i].instanceName (); } conf.put (name, new ComponentConfiguration (comp)); continue; } } catch (java.io.IOException ex) { ErrorManager.getDefault ().notify (ex); } catch (ClassNotFoundException ex) { ErrorManager.getDefault ().notify (ex); } } update (toolbars, conf); return ToolbarPool.this; } /** Recreate the instance in AWT thread. */ protected Task postCreationTask (Runnable run) { return new AWTTask (run); } } // end of Folder /** * Class to showing popup menu */ private class PopupListener extends MouseUtils.PopupMouseAdapter { PopupListener() {} /** * Called when the sequence of mouse events should lead to actual showing popup menu */ protected void showPopup (MouseEvent e) { Configuration conf = (Configuration)toolbarConfigs.get (name); if (conf != null) { JPopupMenu pop = conf.getContextMenu(); pop.show (e.getComponent (), e.getX (), e.getY ()); } } } // end of PopupListener /** * Abstract class for toolbar configuration */ public static interface Configuration { /** Activates the configuration and returns right * component that can display the configuration. * @return representation component */ public abstract Component activate (); /** Name of the configuration. * @return the name */ public abstract String getName (); /** Popup menu that should be displayed when the users presses * right mouse button on the panel. This menu can contain * contains list of possible configurations, additional actions, etc. * * @return popup menu to be displayed */ public abstract JPopupMenu getContextMenu (); } /** Implementation of configuration that reacts to one * component */ private static final class ComponentConfiguration extends JPopupMenu implements Configuration, ActionListener { private Component comp; ComponentConfiguration() {} static final long serialVersionUID =-409474484612485719L; /** @param comp component that represents this configuration */ public ComponentConfiguration (Component comp) { this.comp = comp; } /** Simply returns the representation component */ public Component activate () { return comp; } /** @return name of the component */ public String getName () { return comp.getName (); } /** Updates items in popup menu and returns itself. */ public JPopupMenu getContextMenu () { removeAll (); // generate list of available toolbar panels Iterator it = Arrays.asList (ToolbarPool.getDefault ().getConfigurations ()).iterator (); ButtonGroup bg = new ButtonGroup (); String current = ToolbarPool.getDefault ().getConfiguration (); while (it.hasNext()) { final String name = (String)it.next (); JRadioButtonMenuItem mi = new JRadioButtonMenuItem (name, (name.compareTo (current) == 0)); mi.addActionListener (this); bg.add (mi); this.add (mi); } return this; } /** Reacts to action in popup menu. Switches the configuration. */ public void actionPerformed (ActionEvent evt) { ToolbarPool.getDefault().setConfiguration (evt.getActionCommand ()); } } } // end of ToolbarPool