Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4133141
Votes 30
Synopsis Extend Action interface to handle toggles
Category java:classes_swing
Reported Against 1.3 , 1.4 , 1.2.2 , 1.3.1 , 1.4.1 , 1.4.2 , 1.2beta3 , 1.3.1_06 , 1.4.1_01 , 1.4.1_03 , swing1.0.2 , merlin-beta , kestrel-beta
Release Fixed mustang(b53)
State 10-Fix Delivered, request for enhancement
Priority: 4-Low
Related Bugs 6350748 , 4156235 , 4269932 , 4629461 , 6397556 , 4491747 , 4751776 , 4885827
Submit Date 28-APR-1998
Description


I really like the Action interface (in fact, I'd
implemented it myself in a previous version of my 
system) - but what about toggles? I'd like to be
able to define a ToggleAction that has an on/off 
state. When I add it to a JToolbar, I'd get a
JToggleButton. When I add it to a JMenu, I'd get
a JCheckboxMenuItem. Setting its state to be on via
the menuitem would cause the appearance of the
JToggleButton to change automatically, etc.
The same idea could probably be extended to encompass
JRadioButton/JRadioButtonMenuItem pairs...
(Review ID: 29188)
======================================================================




java version "1.3beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3beta-O)
Java(TM) HotSpot Client VM (build 1.3beta-O, mixed mode)

Why does the new method "JToolBar.CreateActionComponent" return a JButton?
Because of this, it's not possible to create a JToggleButton for the action.

Suggestion: CreateActionComponent should return a JAbstractButton or, even
better, a Component (since buttons aren't the only thing usually visible in a
toolbar, and for example ComboBoxes could be associated with an action as well).
(Review ID: 100282)
======================================================================




java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

Right now Action and AbstractAction provides support for creating several
controls with a common behaviour and with their enabled state synchronized, but
there is no way to have several toggle buttons synchronized in such a way. I
think it could be interesting to add ToggleAction and AbstractToggleAction, so
we could have several toggle buttons with their selected state synchronized.
The additional classes could be something like this:

package javax.swing;

public interface ToggleAction extends Action
  {
  boolean isSelected( );
  void setSelected( boolean selected );
  }
  


package javax.swing;

import java.io.Serializable;
import java.awt.event.*;

public abstract class AbstractToggleAction extends AbstractAction
implements ToggleAction, Cloneable, Serializable
  {
  protected boolean selected = false;

  public AbstractToggleAction( )
    { super( ); }

  public AbstractToggleAction( String name )
    { super( name ); }

  public AbstractToggleAction( String name, Icon icon )
    { super( name, icon ); }

  public Object clone( )
    {
    AbstractToggleAction copy = null;
    try
      {
      copy = (AbstractToggleAction) super.clone( );
      copy.selected = this.selected;
      }
    catch( CloneNotSupportedException exc )
      {}
    return copy;
    }

  public boolean isSelected( )
    { return selected; }

  public void setSelected( boolean selected )
    {
    if( this.selected != selected )
      {
      this.selected = selected;
      firePropertyChange( "selected", new Boolean( !this.selected ), new Boolean
( this.selected ) );
      }
    }
    
  public void actionPerformed( ActionEvent e )
    {
    Object source = e.getSource( );
    if( source instanceof AbstractButton )
      {
      AbstractButton button = (AbstractButton) source;
      setSelected( button.isSelected( ) );
      }
    }
  }

This classes could be used adding a setToggleAction method to JToggleButton and
JCheckboxMenuItem.
(Review ID: 111728)
======================================================================
Posted Date : 2005-08-26 23:30:48.0
Work Around




The only possible workaround is manual synchronization, as should be done with buttons if Action didn't exist
(Review ID: 111728)
======================================================================
Evaluation
We should do this, but we need to figure out an flexible solution
for all actions and containers.

---

There are a lot of good ideas here. First I was using 4629461 to drive this work in tiger but I'll close that as a duplicate of this one.
  xxxxx@xxxxx   2002-05-30

--
This one may require API changes which we cannot do for tiger at this point. We agree that this is an important feature but there is still some discussion on the correct approach. One approach is that we add new classes which represent toggles. Another approach is that the toggles are stored as properties on the Action.
  xxxxx@xxxxx   2003-12-08
Because this bug was filed much earlier than 4491747, I'm going to close 4491747 as a duplicate of this but fix all the remaining issues from 4491747 in this bug.  Here's the list of things that are going to change:

Action will get the new keys:

    public static final String SELECTED_KEY;
    public static final String DISPLAYED_MNEMONIC_INDEX_KEY;
    public static final String LARGE_ICON_KEY;

SELECTED_KEY will be useful for togglebutton, radiobutton, checkbox, checkboxmenuitem and radiobuttonmenuitem.  If the selected state of the button changes the selected property of the Action will be updated.  Similarly if the SELECTED_KEY property of the Action changes the button will update it's state appropriately.

DISPLAYED_MNEMONIC_INDEX_KEY name describes what it does;)

LARGE_ICON_KEY will be used by all non-menuitem buttons.  If LARGE_ICON_KEY is non-null it's value will be used as the icon, if it's null the icon from SMALL_ICON_KEY will be used.

The previously undocumented client property 'hideActionText' will now be a public property (AbstractButton will have a set/getter for it).  And changes to the property (or client property) will be immediate.

The system property swing.actions.reconfigureOnNull will be provided so that if true a 'null' property name will result in the component resetting all interested properties.

Finally, all of this will be documented in Action:)
Posted Date : 2005-08-26 23:30:48.0
Comments
  
  Include a link with my name & email   

Submitted On 18-NOV-1998
rmaddy
We are currently working on a solution to this issue since we need Action based
Toggle buttons, radio button menus, and checkbox menus too.  Our solution is in
design but includes a new interface deriving Action.  This adds some new
constants like SELECTED and GROUP.  We dervived a new class from AbstractAction
that implements our new interface.  All our actions derive from our new
abstract action class.  We are extending JButton, JToggleButton, JMenu,
JToolBar, JCheckBoxMenuItem, and JRadioButtonMenuItem to take our action
interface.  our JMenu, JToolBar and JRadioButtonMenuItem will check for the
GROUP value.  If found it will create a JRadioButtonMenuItem or JToggleButton
as appropriate and associate it with the ButtonGroup.  If no GROUP it will look
for SELECT.  If found it will create a JCheckBoxMenuItem or JToggleButton as
appropriate.  All these will have proper property change listeners and state
change listeners to react to Action changes and state changes.


Submitted On 16-FEB-2000
bodnej
I just tried to make the changes suggested by Raviola in the duplicate of this
bug and they are impossible, due to Sun declaring the method
registerMenuItemForAction in JMenu private.  If it was protected, there would
be no problem, but the private method isn't visible to any subclass, and it's
that private method which connects the internally created JMenuCheckboxItem
with the Action.  The field which registerMenuItemForAction modifies
(listenerRegistry) is also declared private, so you can't modify it from the
subclass.

It's a shame, because the solution would have been so easy, but Sun made a
mistake in the OO design for the JMenu.


Submitted On 22-FEB-2000
dougbell
It would be fairly easy to implement this myself and add 
support to JToolbar EXCEPT that the 
JToolbar.ActionChangedListener class is private and can't be 
extended to handle other properties, such as a toggle state.  
Further, JToolbar allows for overriding the 
createActionChangeListener() method to instantiate a 
different PropertyChangeListener, but the JToolbar.remove() 
method is hard-coded to expect a 
JToolbar.ActionChangedListener.  Since you can't extend the 
private JToolbar.ActionChangedListener, you can only extend 
the property handling if you also override remove() to 
remove the dependence on JToolbar.ActionChangedListener.  
But you can't override remove() to remove the dependency, 
because the static listenerRegistry Hashtable is also 
private.

Really, JToolbar needs to be fixed.  The minimum change 
would be to make JToolbar.ActionChangedListener a protected 
class.


Submitted On 19-MAY-2000
ralkire
protected JMenu.createActionComponent assumes that the component must be a JMenuItem, which precludes 
the use of all other components.  Also the Action class has no built in support for providing information to the 
method which type of component is desired.  Please fix this problem by creating a new pair of fields for the Action 
class that specify the class to instanciate for toolbar buttons, and menu items.  In this way, classes created 
outside the JDK can also be created.  This enables XML specified menu and toolbar items to be created supporting 
generic actions that are not possible otherwise.


Submitted On 11-APR-2001
dstuart
When I first saw Actions, I was pleased and thought "oh 
cool, now I don't have to do manual synchronization".. but 
unfortunately this is useless to me without the 
togglebutton and radio button behaviors as described. We 
have lots of things that could benefit from it, but we 
won't move until this is addressed..


Submitted On 11-APR-2001
dstuart
I've actually patched swing in our development environment 
to achieve this for (at least) JToggleButtons and 
JCheckboxMenuItems.

Feel free to contact me via e-mail if you want the patch..
(limit 1 per customer!)


Submitted On 02-NOV-2001
gfaron
  It seems to me that the private inner-class 
AbstractButton.ButtonActionPropertyChangeListener could 
have the following "else if" clause inserted into its 
propertyChange(PropertyChangeEvent) method and the issue 
would be fixed (together with the code that the orignal 
poster provided above).


  // This clause is already present in the method.
  else if (propertyName.equals("enabled"))
    {
    Boolean enabledState = (Boolean) e.getNewValue();
    button.setEnabled(enabledState.booleanValue());
    button.repaint();
    } // ends else if

  // Add this clause here.
  else if (propertyName.equals("selected"))
    {
    Boolean selectedState = (Boolean) e.getNewValue();
    button.setSelected(selectedState.booleanValue());
    button.repaint();
    } // ends else if


Submitted On 07-DEC-2001
gfaron
  I have a fix for this one, but the CGI page is not 
allowing me to post it.  Please e-mail for the fix (mention 
4133141 in the subject as well).


Submitted On 24-OCT-2002
joneshJ
gfaron seems to have the simplest fix for the problem (as 
described).
I worked around the problem by creating an Action subclass 
with isSelected/setSelected methods and firing a "selected" 
PropertyChangeEvent.

Then you have to override JCheckBoxMenuItem and 
JToggleButton to implement the method 
createActionPropertyChangeListener which handles 
the "selected" property properly.


Submitted On 20-MAR-2003
risadinha
My Workaround:

Use one ButtonModel for all Buttons with the same Action.
The reference of the ButtonModel can be stored in the action
such that when creating a button the button model can be set
in a second step:
ButtonModelAction action = new ButtonModelAction();
AbstractButton b = new WhateverAbstractButton(action);
b.setModel(action.getButtonModel());

The adventage is, that disabling, arming etc. can be done
through the model, as well. If the state of the buttons
depends on a table or tree selection, the model can change
state according to this selection and all buttons will adopt
apropriately.


Submitted On 11-DEC-2003
christopher_j_nielsen
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

///////////////////////////////////////////////////////////////////////////
// Simple extension to AbstractAction, to 
support "togglable" actions
// (JCheckBoxMenuItem, JRadioButtonMenuItem, etc.)
///////////////////////////////////////////////////////////////////////////

public abstract class MyAbstractAction
	extends AbstractAction
{
	// Called when it is time for us to update 
our enabled state
	// (we should also obtain any state 
necessary for toggle state,
	// obtained via 'isSelected()' method).
	//
	// NOTE: this code should run as quickly as 
possible, otherwise
	// user will notice a delay before menu is 
displayed!
	public void update()
	{
	}

	// For check box and radio button menu 
items -- return TRUE if
	// item should be toggled 'ON', FALSE 
otherwise.  For
	// non-togglable items, there is no need to 
override this method.
	public boolean isSelected()
	{
		return(false);
	}

	abstract public void actionPerformed
(ActionEvent evt);
}

///////////////////////////////////////////////////////////////////////////
// Class to dynamically update menu items at popup 
time (similar to MFC)
// via an extended AbstractAction.
///////////////////////////////////////////////////////////////////////////

public class MyAbstractActionUpdater
	implements PopupMenuListener
{
	// Recursively attach ourselves as a 
PopupMenuListener, to the passed-
	// in element and all popup submenus.
	public void attach( MenuElement 
menuElement )
	{
		if ( menuElement instanceof 
JMenu )				
		{
			JPopupMenu 
popupMenu = ((JMenu) menuElement).getPopupMenu
();
		
	popupMenu.addPopupMenuListener(this);
		}

		attach( 
menuElement.getSubElements() );
	}

	public void attach( MenuElement 
menuElementArray[] )
	{
		int nCount = 
menuElementArray.length;

		for ( int i = 0 ; i < nCount ; i++ )
			attach( 
menuElementArray[i] );
	}

	// Update the state of all items in a given 
popup menu, just before
	// the menu is displayed.
	//
	// Note that we are only concerned with top-
level items in this
	// popup menu -- submenu popups are 
handled only when they are
	// ready to become visible.
	public void popupMenuWillBecomeVisible
(PopupMenuEvent evt)
	{
		JPopupMenu popupMenu = 
(JPopupMenu) evt.getSource();

		MenuElement itemArray[] = 
popupMenu.getSubElements();
		int nCount = itemArray.length;
		MenuElement genericItem;
		JMenuItem item;	
			
		Action genericAction;
		MyAbstractAction action;

		for ( int i = 0 ; i < nCount ; i++ )
		{
			genericItem = 
itemArray[i];
			if ( genericItem 
instanceof JMenuItem )
			{
				item = 
(JMenuItem) genericItem;

			
	genericAction = item.getAction();
				if ( 
genericAction != null
				
	&& (genericAction instanceof 
MyAbstractAction) )
				{
				
	action = (MyAbstractAction) genericAction;

				
	// First update the action state, then set the
				
	// 'selected' status of menu item based on 
action.
				
	action.update();
				
	item.setSelected( action.isSelected() );
				}
			}
		}
	}

	public void popupMenuWillBecomeInvisible
(PopupMenuEvent evt)
	{
	}

	public void popupMenuCanceled
(PopupMenuEvent evt)
	{
	}
}


Submitted On 11-DEC-2003
christopher_j_nielsen
I agree with this request -- I too have had to come up 
with a workaround to ensure that all Action-related 
code remains clean, when dealing with 'togglable' 
actions.

My solution entails dynamically updating menu state 
on-the-fly, just before a menu popup is shown.  I will 
post the code in short order.



PLEASE NOTE: JDK6 is formerly known as Project Mustang