alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 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.actions;

import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
import org.openide.actions.ActionManager;
import org.openide.awt.Actions;

import org.openide.awt.JInlineMenu;
import org.openide.util.ContextAwareAction;
import org.openide.util.NbBundle;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.actions.*;

/** A "meta-action" that displays (in a submenu) a list of enabled actions provided by modules.
* Such registered actions are called "service actions":
* they are provided externally but seem to provide additional services on existing components.
* Often they will be {@link NodeAction}s or {@link CookieAction}s so that they will
* be enabled based on the node selection, i.e. the node containing this popup.
* It is desirable for most nodes to include this action somewhere in their popup menu.
*
* 

Note: you do not need to touch this class to add a service action! * Just add the action to a module manifest in an Action section. * *

The list of registered service actions is provided to this action from the implementation * by means of {@link ActionManager}. * * @author Jaroslav Tulach */ public class ToolsAction extends SystemAction implements ContextAwareAction, Presenter.Menu, Presenter.Popup { static final long serialVersionUID =4906417339959070129L; // Global ActionManager listener monitoring all available actions // and their state private static G gl; /** Lazy initialization of global listener. */ private static synchronized G gl () { if (gl == null) { gl = new G (); } return gl; } /* @return name */ public String getName () { return getActionName (); } /* @return help for this action */ public HelpCtx getHelpCtx () { return new HelpCtx (ToolsAction.class); } /* @return menu presenter for the action */ public JMenuItem getMenuPresenter () { return new Inline(this); } /* @return menu presenter for the action */ public JMenuItem getPopupPresenter () { return new Popup(this); } /* Does nothing. */ public void actionPerformed (java.awt.event.ActionEvent ev) { assert false; } public Action createContextAwareInstance(Lookup actionContext) { return new DelegateAction(this, actionContext); } /* @return name */ private static String getActionName () { return NbBundle.getMessage(ToolsAction.class, "CTL_Tools"); } /** Implementation method that regenerates the items in the menu or * in the array. * * @param forMenu true if will be presented in menu or false if presented in popup * @param list (can be null) */ private static List generate (Action toolsAction, boolean forMenu) { ActionManager am = ActionManager.getDefault(); SystemAction[] actions = am.getContextActions (); List list = new ArrayList( actions.length ); boolean separator = false; boolean firstItemAdded = false; // flag to prevent adding separator before actual menu items // Get action context. Lookup lookup; if(toolsAction instanceof Lookup.Provider) { lookup = ((Lookup.Provider)toolsAction).getLookup(); } else { lookup = null; } for (int i = 0; i < actions.length; i++) { Action a; // Retrieve context sensitive action instance if possible. if(lookup != null && actions[i] instanceof ContextAwareAction) { a = ((ContextAwareAction)actions[i]).createContextAwareInstance(lookup); } else { a = actions[i]; } if (a == null) { if (firstItemAdded) separator = true; } else { boolean isPopup = (a instanceof Presenter.Popup); boolean isMenu = (a instanceof Presenter.Menu); if (!((forMenu && isMenu) || (!forMenu && isPopup)) && (isMenu || isPopup)) { continue; // do not call isEnabled on action that is only popup presenter when building menu (i18nPopupAction) } if (a.isEnabled()) { JMenuItem mi; if (forMenu && isMenu) { mi = ((Presenter.Menu)a).getMenuPresenter(); } else if (!forMenu && isPopup) { mi = ((Presenter.Popup)a).getPopupPresenter(); } else if (!isMenu && !isPopup) { // Generic Swing action. mi = new JMenuItem(); Actions.connect(mi, a, !forMenu); } else { // Should not be here. continue; } if (separator) { list.add(null); separator = false; } list.add(mi); firstItemAdded = true; } } } return list; } /** Inline menu that watches model changes only when really needed. */ private static final class Inline extends JInlineMenu implements PropertyChangeListener, Runnable { static final long serialVersionUID =2269006599727576059L; /** timestamp of the beginning of the last regeneration */ private int timestamp = 0; /** Associated tools action. */ private Action toolsAction; Inline(Action toolsAction) { this.toolsAction = toolsAction; putClientProperty("hack.preShowUpdater", this); } /** By calling this method, our parent notifies us we've to be keep * updated, so we start listening on SystemAction changes, and * schedule updating Runnable imediately. */ public void addNotify() { // We were not notified by our parent, too bad if(timestamp != gl().getTimestamp()) SwingUtilities.invokeLater(this); gl ().addPropertyChangeListener( this ); super.addNotify(); } /** By calling this method, our parent notifies us we don't have * to be up-to-date more, so we switch to lazy mode and discard any * pending updates. */ public void removeNotify() { gl ().removePropertyChangeListener( this ); super.removeNotify(); } /** Change of model. */ public void propertyChange (PropertyChangeEvent ev) { String prop = ev.getPropertyName (); if (prop == null || prop.equals (G.PROP_STATE)) { SwingUtilities.invokeLater (this); } } /** Runs the update */ public void run () { if( timestamp == gl ().getTimestamp() ) return; // generate directly list of menu items List l = generate (toolsAction, true); setMenuItems ((JMenuItem[])l.toArray(new JMenuItem[l.size ()])); timestamp = gl ().getTimestamp(); } } //-------------------------------------------------- /** Inline menu that is either empty or contains one submenu.*/ private static final class Popup extends JInlineMenu implements Runnable { /** A special menu that will properly update its submenu before posting */ private class MyMenu extends org.openide.awt.JMenuPlus implements PopupMenuListener { /* A popup menu we've attached our listener to. * If null, the content is not up-to-date */ private JPopupMenu lastPopup = null; MyMenu() { super(getActionName()); } public JPopupMenu getPopupMenu() { JPopupMenu popup = super.getPopupMenu(); fillSubmenu(popup); return popup; } private void fillSubmenu(JPopupMenu pop) { if (lastPopup == null) { pop.addPopupMenuListener(this); lastPopup = pop; removeAll (); Iterator it = generate(toolsAction, false).iterator(); while( it.hasNext() ) { java.awt.Component item = (java.awt.Component)it.next(); if( item == null ) { addSeparator (); } else { add ( item ); } } // also work with empty element if(getMenuComponentCount () == 0) { JMenuItem empty = new JMenuItem(NbBundle.getMessage( ToolsAction.class, "CTL_EmptySubMenu")); empty.setEnabled(false); add(empty); } } } public void popupMenuCanceled(PopupMenuEvent e) {} public void popupMenuWillBecomeVisible(PopupMenuEvent e) {} public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { lastPopup.removePopupMenuListener(this); lastPopup = null; // clear the status and stop listening } } static final long serialVersionUID =2269006599727576059L; /** sub menu */ private JMenu menu = new MyMenu(); private boolean inited = false; /** Associated tools action. */ private Action toolsAction; public Popup (Action toolsAction) { super(); this.toolsAction = toolsAction; HelpCtx.setHelpIDString (menu, ToolsAction.class.getName ()); // This can be probably swapped as the popup is short time entity and our hack // will be called just once and very early after the constructor. // run(); putClientProperty("hack.preShowUpdater", this); } public void addNotify() { if (!inited) { // should not happen SwingUtilities.invokeLater (this); // too late to do it here } super.addNotify(); } /** Runs the update */ public void run () { if(!inited) { setMenuItems ( gl().isPopupEnabled(toolsAction) ? new JMenuItem[] { menu } : new JMenuItem[0] // Tools action won't be seen. ); inited = true; } } } //------------------------------------------ /** @deprecated Useless, see {@link ActionManager}. */ public static void setModel (Model m) { throw new SecurityException (); } /** @deprecated Useless, see {@link ActionManager}. */ public static interface Model { public SystemAction[] getActions (); public void addChangeListener (javax.swing.event.ChangeListener l); public void removeChangeListener (javax.swing.event.ChangeListener l); } //------------------------------------------------ //---------------------------------------------------------- private static class G implements PropertyChangeListener { private int timestamp = 1; private SystemAction[] actions = null; private PropertyChangeSupport supp = new PropertyChangeSupport( this ); public static final String PROP_STATE = "actionsState"; // NOI18N public G() { ActionManager am = ActionManager.getDefault(); am.addPropertyChangeListener ( this ); actionsListChanged(); } public final void addPropertyChangeListener( PropertyChangeListener listener ) { supp.addPropertyChangeListener(listener); } public final void removePropertyChangeListener( PropertyChangeListener listener ) { supp.removePropertyChangeListener (listener); } protected final void firePropertyChange( String name , Object o, Object n ) { supp.firePropertyChange(name, o, n); } private void actionsListChanged() { timestamp++; // deregister all actions listeners SystemAction[] copy = actions; if( copy != null ) { for( int i = 0; i< copy.length; i++ ) { SystemAction act = copy[i]; if( act != null ) act.removePropertyChangeListener ( this ); } } ActionManager am = ActionManager.getDefault(); copy = am.getContextActions (); for (int i = 0; i < copy.length; i++) { SystemAction act = copy[i]; if( act != null ) act.addPropertyChangeListener ( this ); } actions = copy; firePropertyChange( PROP_STATE, null, null ); // tell the world } private void actionStateChanged() { timestamp++; firePropertyChange( PROP_STATE, null, null ); // tell the world } public void propertyChange (PropertyChangeEvent ev) { String prop = ev.getPropertyName (); if (prop == null || prop.equals (ActionManager.PROP_CONTEXT_ACTIONS)) { actionsListChanged(); } else if( prop.equals (SystemAction.PROP_ENABLED)) { actionStateChanged(); } } /** Tells if there is any action that is willing to provide * Presenter.Popup */ private boolean isPopupEnabled(Action toolsAction) { boolean en = false; SystemAction[] copy = actions; // Get action conext. Lookup lookup; if(toolsAction instanceof Lookup.Provider) { lookup = ((Lookup.Provider)toolsAction).getLookup(); } else { lookup = null; } for (int i=0; iLookup.Provider. */ public Lookup getLookup() { return lookup; } public void actionPerformed(java.awt.event.ActionEvent e) { } public void putValue(String key, Object o) {} public Object getValue(String key) { return delegate.getValue(key); } public boolean isEnabled() { // Irrelevant see G#isPopupEnabled(..). return delegate.isEnabled(); } public void setEnabled(boolean b) { // Irrelevant see G#isPopupEnabled(..). } public void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener (listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener (listener); } /** Implements Presenter.Menu. */ public javax.swing.JMenuItem getMenuPresenter() { return new Inline(this); } /** Implements Presenter.Popup. */ public javax.swing.JMenuItem getPopupPresenter() { return new ToolsAction.Popup(this); } } // End of DelegateAction. }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.