Solved

listener for all components inside a container

Posted on 2003-10-27
7
330 Views
Last Modified: 2013-11-23
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 ?
0
Comment
Question by:hapciu
  • 2
  • 2
7 Comments
 
LVL 7

Expert Comment

by:tomboshell
ID: 9632242
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
0
 
LVL 1

Expert Comment

by:amswain
ID: 9632382
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;        }
    }
0
 

Author Comment

by:hapciu
ID: 9637718
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...
0
 
LVL 7

Accepted Solution

by:
tomboshell earned 30 total points
ID: 9640031
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
0
 
LVL 1

Expert Comment

by:amswain
ID: 9640218
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.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Are you developing a Java application and want to create Excel Spreadsheets? You have come to the right place, this article will describe how you can create Excel Spreadsheets from a Java Application. For the purposes of this article, I will be u…
Jaspersoft Studio is a plugin for Eclipse that lets you create reports from a datasource.  In this article, we'll go over creating a report from a default template and setting up a datasource that connects to your database.
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
The viewer will learn how to implement Singleton Design Pattern in Java.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now