FileDocCategorySizeDatePackage
MenuSelectionManager.javaAPI DocJava SE 5 API14944Fri Aug 26 14:57:58 BST 2005javax.swing

MenuSelectionManager

public class MenuSelectionManager extends Object
A MenuSelectionManager owns the selection in menu hierarchy.
version
1.38 12/19/03
author
Arnaud Weber

Fields Summary
private static final MenuSelectionManager
instance
private Vector
selection
private static final boolean
TRACE
private static final boolean
VERBOSE
private static final boolean
DEBUG
protected transient ChangeEvent
changeEvent
Only one ChangeEvent is needed per button model instance since the event's only state is the source property. The source of events generated is always "this".
protected EventListenerList
listenerList
Constructors Summary
Methods Summary
public voidaddChangeListener(javax.swing.event.ChangeListener l)
Adds a ChangeListener to the button.

param
l the listener to add

        listenerList.add(ChangeListener.class, l);
    
public voidclearSelectedPath()
Tell the menu selection to close and unselect all the menu components. Call this method when a choice has been made

        if (selection.size() > 0) {
            setSelectedPath(null);
        }
    
public java.awt.ComponentcomponentForPoint(java.awt.Component source, java.awt.Point sourcePoint)
Returns the component in the currently selected path which contains sourcePoint.

param
source The component in whose coordinate space sourcePoint is given
param
sourcePoint The point which is being tested
return
The component in the currently selected path which contains sourcePoint (relative to the source component's coordinate space. If sourcePoint is not inside a component on the currently selected path, null is returned.

        int screenX,screenY;
        Point p = sourcePoint;
        int i,c,j,d;
        Component mc;
        Rectangle r2;
        int cWidth,cHeight;
        MenuElement menuElement;
        MenuElement subElements[];
        Vector tmp;
        int selectionSize;

        SwingUtilities.convertPointToScreen(p,source);

        screenX = p.x;
        screenY = p.y;

        tmp = (Vector)selection.clone();
        selectionSize = tmp.size();
        for(i=selectionSize - 1 ; i >= 0 ; i--) {
            menuElement = (MenuElement) tmp.elementAt(i);
            subElements = menuElement.getSubElements();
            
            for(j = 0, d = subElements.length ; j < d ; j++) {
		if (subElements[j] == null)
		    continue;
                mc = subElements[j].getComponent();
                if(!mc.isShowing())
                    continue;
                if(mc instanceof JComponent) {
                    cWidth  = ((JComponent)mc).getWidth();
                    cHeight = ((JComponent)mc).getHeight();
                } else {
                    r2 = mc.getBounds();
                    cWidth  = r2.width;
                    cHeight = r2.height;
                }
                p.x = screenX;
                p.y = screenY;
                SwingUtilities.convertPointFromScreen(p,mc);
		
                /** Return the deepest component on the selection
		 *  path in whose bounds the event's point occurs
                 */
                if (p.x >= 0 && p.x < cWidth && p.y >= 0 && p.y < cHeight) {
                    return mc;
                }
            }
        }
	return null;
    
public static javax.swing.MenuSelectionManagerdefaultManager()
Returns the default menu selection manager.

return
a MenuSelectionManager object

  // show bad params, misc.

                   
        	
        return instance;
    
protected voidfireStateChanged()
Notifies all listeners that have registered interest for notification on this event type. The event instance is created lazily.

see
EventListenerList

        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ChangeListener.class) {
                // Lazily create the event:
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
            }          
        }
    
public javax.swing.event.ChangeListener[]getChangeListeners()
Returns an array of all the ChangeListeners added to this MenuSelectionManager with addChangeListener().

return
all of the ChangeListeners added or an empty array if no listeners have been added
since
1.4

        return (ChangeListener[])listenerList.getListeners(
                ChangeListener.class);
    
public javax.swing.MenuElement[]getSelectedPath()
Returns the path to the currently selected menu item

return
an array of MenuElement objects representing the selected path

        MenuElement res[] = new MenuElement[selection.size()];
        int i,c;
        for(i=0,c=selection.size();i<c;i++) 
            res[i] = (MenuElement) selection.elementAt(i);
        return res;
    
public booleanisComponentPartOfCurrentMenu(java.awt.Component c)
Return true if c is part of the currently used menu

        if(selection.size() > 0) {
            MenuElement me = (MenuElement)selection.elementAt(0);
            return isComponentPartOfCurrentMenu(me,c);
        } else
            return false;
    
private booleanisComponentPartOfCurrentMenu(javax.swing.MenuElement root, java.awt.Component c)

        MenuElement children[];
        int i,d;
	
	if (root == null)
	    return false;

        if(root.getComponent() == c)
            return true;
        else {
            children = root.getSubElements();
            for(i=0,d=children.length;i<d;i++) {
                if(isComponentPartOfCurrentMenu(children[i],c))
                    return true;
            }
        }
        return false;
    
private voidprintMenuElementArray(javax.swing.MenuElement[] path)

	printMenuElementArray(path, false);
    
private voidprintMenuElementArray(javax.swing.MenuElement[] path, boolean dumpStack)

	System.out.println("Path is(");
	int i, j;
	for(i=0,j=path.length; i<j ;i++){
	    for (int k=0; k<=i; k++)
		System.out.print("  ");
	    MenuElement me = (MenuElement) path[i];
	    if(me instanceof JMenuItem) {
		System.out.println(((JMenuItem)me).getText() + ", ");
	    } else if (me instanceof JMenuBar) {
		System.out.println("JMenuBar, ");
	    } else if(me instanceof JPopupMenu) {
		System.out.println("JPopupMenu, ");
	    } else if (me == null) {
		System.out.println("NULL , ");
	    } else {
		System.out.println("" + me + ", ");
	    }
	}
	System.out.println(")");

	if (dumpStack == true)
	    Thread.dumpStack();
    
public voidprocessKeyEvent(java.awt.event.KeyEvent e)
When a MenuElement receives an event from a KeyListener, it should never process the event directly. Instead all MenuElements should call this method with the event.

param
e a KeyEvent object

        MenuElement[] sel2 = new MenuElement[0];
        sel2 = (MenuElement[])selection.toArray(sel2);
        int selSize = sel2.length;
        MenuElement[] path;

        if (selSize < 1) {
            return;
	}

        for (int i=selSize-1; i>=0; i--) {
            MenuElement elem = sel2[i];
            MenuElement[] subs = elem.getSubElements();
            path = null;

            for (int j=0; j<subs.length; j++) {
		if (subs[j] == null || !subs[j].getComponent().isShowing()
                    || !subs[j].getComponent().isEnabled()) {
		    continue;
                }

                if(path == null) {
                    path = new MenuElement[i+2];
                    System.arraycopy(sel2, 0, path, 0, i+1);
                    }
                path[i+1] = subs[j];
                subs[j].processKeyEvent(e, path, this);
                if (e.isConsumed()) {
                    return;
            }
        }
    }

        // finally dispatch event to the first component in path
        path = new MenuElement[1];
        path[0] = sel2[0];
        path[0].processKeyEvent(e, path, this);
        if (e.isConsumed()) {
            return;
        }
    
public voidprocessMouseEvent(java.awt.event.MouseEvent event)
When a MenuElement receives an event from a MouseListener, it should never process the event directly. Instead all MenuElements should call this method with the event.

param
event a MouseEvent object

        int screenX,screenY;
        Point p;
        int i,c,j,d;
        Component mc;
        Rectangle r2;
        int cWidth,cHeight;
        MenuElement menuElement;
        MenuElement subElements[];
        MenuElement path[];
        Vector tmp;
        int selectionSize;
        p = event.getPoint();
	
	Component source = (Component)event.getSource();

	if (!source.isShowing()) {
	    // This can happen if a mouseReleased removes the
	    // containing component -- bug 4146684
	    return;
	}

	int type = event.getID();
	int modifiers = event.getModifiers();
	// 4188027: drag enter/exit added in JDK 1.1.7A, JDK1.2
	if ((type==MouseEvent.MOUSE_ENTERED||
	     type==MouseEvent.MOUSE_EXITED)
	    && ((modifiers & (InputEvent.BUTTON1_MASK |
			      InputEvent.BUTTON2_MASK | InputEvent.BUTTON3_MASK)) !=0 )) {
	    return;
	}

        SwingUtilities.convertPointToScreen(p,source);

        screenX = p.x;
        screenY = p.y;

        tmp = (Vector)selection.clone();
        selectionSize = tmp.size();
	boolean success = false;
	for (i=selectionSize - 1;i >= 0 && success == false; i--) {
            menuElement = (MenuElement) tmp.elementAt(i);
            subElements = menuElement.getSubElements();
            
            path = null;
	    for (j = 0, d = subElements.length;j < d && success == false; j++) {
		if (subElements[j] == null)
		    continue;
                mc = subElements[j].getComponent();
                if(!mc.isShowing())
                    continue;
                if(mc instanceof JComponent) {
                    cWidth  = ((JComponent)mc).getWidth();
                    cHeight = ((JComponent)mc).getHeight();
                } else {
                    r2 = mc.getBounds();
                    cWidth  = r2.width;
                    cHeight = r2.height;
                }
                p.x = screenX;
                p.y = screenY;
                SwingUtilities.convertPointFromScreen(p,mc);

                /** Send the event to visible menu element if menu element currently in
                 *  the selected path or contains the event location
                 */
                if(
                   (p.x >= 0 && p.x < cWidth && p.y >= 0 && p.y < cHeight)) {
                    int k;
                    if(path == null) {
                        path = new MenuElement[i+2];
                        for(k=0;k<=i;k++)
                            path[k] = (MenuElement)tmp.elementAt(k);
                    }
                    path[i+1] = subElements[j];
		    MenuElement currentSelection[] = getSelectedPath();

		    // Enter/exit detection -- needs tuning...
		    if (currentSelection[currentSelection.length-1] !=
			path[i+1] &&
			(currentSelection.length < 2 || 
			 currentSelection[currentSelection.length-2] !=
			 path[i+1])) {
			Component oldMC = currentSelection[currentSelection.length-1].getComponent();

			MouseEvent exitEvent = new MouseEvent(oldMC, MouseEvent.MOUSE_EXITED,
							      event.getWhen(),
							      event.getModifiers(), p.x, p.y,
							      event.getClickCount(),
							      event.isPopupTrigger());
			currentSelection[currentSelection.length-1].
			    processMouseEvent(exitEvent, path, this);

			MouseEvent enterEvent = new MouseEvent(mc, 
							       MouseEvent.MOUSE_ENTERED,
							       event.getWhen(),
							       event.getModifiers(), p.x, p.y,
							       event.getClickCount(),
							       event.isPopupTrigger());
			subElements[j].processMouseEvent(enterEvent, path, this);
		    } 
		    MouseEvent mouseEvent = new MouseEvent(mc, event.getID(),event. getWhen(),
							   event.getModifiers(), p.x, p.y,
							   event.getClickCount(),
							   event.isPopupTrigger());
		    subElements[j].processMouseEvent(mouseEvent, path, this);
		    success = true;
		    event.consume();
		}
            }
        }
    
public voidremoveChangeListener(javax.swing.event.ChangeListener l)
Removes a ChangeListener from the button.

param
l the listener to remove

        listenerList.remove(ChangeListener.class, l);
    
public voidsetSelectedPath(javax.swing.MenuElement[] path)
Changes the selection in the menu hierarchy. The elements in the array are sorted in order from the root menu element to the currently selected menu element.

Note that this method is public but is used by the look and feel engine and should not be called by client applications.

param
path an array of MenuElement objects specifying the selected path


                                                                                
        
        int i,c;
        int currentSelectionCount = selection.size();
        int firstDifference = 0;

        if(path == null) {
            path = new MenuElement[0];
        }

	if (DEBUG) {
	    System.out.print("Previous:  "); printMenuElementArray(getSelectedPath());
	    System.out.print("New:  "); printMenuElementArray(path);
	}

        for(i=0,c=path.length;i<c;i++) {
            if(i < currentSelectionCount && (MenuElement)selection.elementAt(i) == path[i]) 
                firstDifference++;
            else
                break;
        }

        for(i=currentSelectionCount - 1 ; i >= firstDifference ; i--) {
            MenuElement me = (MenuElement)selection.elementAt(i);
            selection.removeElementAt(i);
            me.menuSelectionChanged(false);
        }

        for(i = firstDifference, c = path.length ; i < c ; i++) {
	    if (path[i] != null) {
		selection.addElement(path[i]);
		path[i].menuSelectionChanged(true);
	    }        
	}

	fireStateChanged();