listener for all components inside a container

i want to have a mouse listener for a JPanel, that will work even if the user clicks on any component contained in the JPanel. is there an easy solution to that or do i have to add mouse listeners to all the components inside the panel and to check for every new component added ?
hapciuAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

tomboshellCommented:
You only need to make one mouse listener, make it a separate class (either a separate file or as an inner class, but not the ananymous inner).  Then register that instance to each control.  Each control is designed to operate separately.  You can easily do this by creating a method that 'only' adds the mouse listener to the objects.  AFAIK
amswainCommented:
I have a dynamic GUI where components can advertise status messages.  As the mouse moves around the GUI, the status bar is updated accordingly.  Here's my (cut down) code, I'm sure you can adapt it accordingly.  Hope this is of some help.

MainFrame() {
    statusListener = new StatusListener();
    getContentPane().addContainerListener( statusListener );
}


    /**
     *  Listens for components being added/removed to the component hierarchy and adds mouse
     *  motion listener to them.  The mouse motion listener grabs the status message from the
     *  component (or from its parent) and updates the status bar accordingly.  If a component
     *  fires a property change event and it is the active component, the status bar will also
     *  be updated.
     */
    private class StatusListener implements ContainerListener, MouseMotionListener, PropertyChangeListener {

        /** The last component that the mouse moved over */
        private StatusMessage lastComponent;

        public void mouseMoved( MouseEvent e ) {
            Component c = (Component)e.getSource();
            StatusMessage sm = getStatusMessageComponent( c );
            if ( sm != null ) {
                String msg = sm.getStatusMessage();
                getStatusBar().setMessage( msg );
                lastComponent = sm;
            } else {
                getStatusBar().setMessage( "" );
                lastComponent = null;
            }
        }
        public void mouseDragged( MouseEvent e ) {
        }
        public void componentAdded( ContainerEvent e ) {
            addContainerListeners( (Component)e.getSource() );
        }
        public void componentRemoved( ContainerEvent e ) {
            // we don't need to bother with this
            // since the containers won't get removed
        }
        public void propertyChange( PropertyChangeEvent e ) {
            Component c = (Component)e.getSource();
            boolean change = "statusMessage".equals( e.getPropertyName() );
            if ( change ) {
                StatusMessage sm = getStatusMessageComponent( c );
                if ( lastComponent==null || lastComponent==sm ) {
                    String msg = sm.getStatusMessage();
                    getStatusBar().setMessage( msg );
                }
            }
            // any change to the status message or busy flag might indicate that
            // new components have been added to the plugin (lazy creation)
            if ( change || "busy".equals( e.getPropertyName() ) ) {
                if ( log.isDebugEnabled() ) {
                    log.debug( "Status property changed for "+c.getName() );
                }
                addNestedListeners( c, true );
            }
        }

        /**
         *  Get the nearest status message component.
         */
        private StatusMessage getStatusMessageComponent( Component c ) {
            while( c != null ) {
                if ( c instanceof StatusMessage ) {
                    return (StatusMessage)c;
                }
                c = c.getParent();
            }
            return null;
        }

        // sensible container classes to listen to
        private Class[] containerClasses = new Class[] {
            JPanel.class,
            JScrollPane.class,
            JSplitPane.class,
            JTabbedPane.class,
            JViewport.class
        };

        // sensible component classes
        // to install mouse listeners on
        private Class[] componentClasses = new Class[] {
            AbstractButton.class,
            JComboBox.class,
            JLabel.class,
            JList.class,
            JScrollBar.class,
            JScrollPane.class,
            JSplitPane.class,
            JTabbedPane.class,
            JTable.class,
            JTableHeader.class,
            JTextComponent.class,
            JTree.class,
            JViewport.class
        };

        /**
         *  Add mouse motion and property change listeners to all nested component.
         *  This method will be called whenever the status message or busy flag changes
         *  on any plugin.
         */
        private void addNestedListeners( Component c, boolean remove ) {
            if ( isRelevant( componentClasses, c ) ) {
                if ( c instanceof StatusMessage ) {
                    if ( remove ) {
                        c.removePropertyChangeListener( this );
                    }
                    c.addPropertyChangeListener( this );
                }
                if ( remove ) {
                    c.removeMouseMotionListener( this );
                }
                c.addMouseMotionListener( this );

                if ( isRelevant( containerClasses, c ) ) {
                    Component[] cs = ((Container)c).getComponents();
                    for (int i=0; i<cs.length; i++) {
                        addNestedListeners( cs[i], remove );
                    }
                }
            }
        }

        /**
         *  Add listeners to all relevant components.
         */
        private void addContainerListeners( Component c ) {
            if ( c instanceof StatusMessage ) {
                if ( log.isDebugEnabled() ) {
                    log.debug( "Status component added "+(c.getName()!=null?c.getName():c.getClass().getName()) );
                }
                addNestedListeners( c, false );
            } else if ( isRelevant( containerClasses, c ) ) {
                Container ct = (Container)c;
                ct.removeContainerListener( this );
                ct.addContainerListener( this );
                Component[] cs = ct.getComponents();
                if ( log.isDebugEnabled() ) {
                    log.debug( "Container added "+cs.length+" "+(c.getName()!=null?c.getName():c.getClass().getName()) );
                }
                for (int i=0; i<cs.length; i++) {
                    addContainerListeners( cs[i] );
                }
            } else if ( c.isShowing() ) {
                if ( log.isDebugEnabled() ) {
                    log.debug( "Adding mouse listener to "+(c.getName()!=null?c.getName():c.getClass().getName()) );
                }
                c.removeMouseMotionListener( this );
                c.addMouseMotionListener( this );
            }
        }

        /**
         *  Test whether the component is a relevant type or not.
         */
        private boolean isRelevant( Class[] classes, Component c ) {
            for (int i=0; i<classes.length; i++) {
                if ( classes[i].isInstance( c ) ) {
                    return true;
                }
            }
            return false;        }
    }
hapciuAuthor Commented:
tomboshell: that's what i thought of first. my problem is that i don't really want to add that listener to every component in the hierarchy. and besides, i need to add a component listener to every container inside my panel so i can add my mouseListener to every new component added later... too much trouble... i was wandering if there is such thing as a "global mouse listener" - i mean a mouse listener for all the children (and their children) of a container. basically i want any click inside my panel to be notified to a single listener.

amswain: you just exemplified what tomboshell described. didn't you look for an easier solution ? or then again, maybe there isn't one.

both thanks, but wasn't what i was lookin for...
tomboshellCommented:
Sorry.  The controls are not designed that way.  Of course, there are other options.  The worst option would be to write all your own controls, that then receive the same actionlistenter through inheritence (wayyyy to much work).  Another option would be to get all your controls through a 'Builder' pattern class.  I will just describe the idea and see if you can work with it.

Make a class with a static method (so that you can access it without having an instance), have the class accept the initalizing variables for the controls, also allow it to be empty, and have a parameter for what type of control.  I'll do some skeleton code....

public static JComponent buildControl(String text, String[] values, String objectType){
  JComponent j = null;
  if(objectType.equals("JPanel")){
    j = new JPanel();
  } else if(objectType.equals("JButton")){
     if(text.length() > -1) j = new JButton(text);
     else j = new JButton();
  } else if(objectType.equals("JComboBox")){
       if(values != null && values.length > 0) j = new JComboBox(values);
       else j = new JComboBox();
  }  // keep going with all the types you need
   
   // now add your mouselistener to this.
  j.addMouseListener(<your mouse lisener>);
  return j;
}



then somewhere you would call it and cast it as you need it
   JPanel myPanel = (JPanel) buildControl("", null, "JPanel");
  JButton myButton = (JButton)buildControl("Push Me", null, "JButton");
and so on

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
amswainCommented:
You can make the glass pane visible on the frame, add a single listener to the glass pane and get it to forward the mouse events to the underlying components.  This solves your one mouse listener problem but will introduce other problems.  For example, the cursor doesn't change when you move over a split pane divider etc.  I dont know how complicated your gui is.  The only solution I found was the one I mentioned earlier.

You could create your own subclasses of the standard swing components and override processEvent() but this isn't very attractive either.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Editors IDEs

From novice to tech pro — start learning today.