Solved

listener for all components inside a container

Posted on 2003-10-27
7
344 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

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
What's wrong with this code? 4 26
ejb entity bean example issue 2 17
Is there a simpler dropbox system? 10 34
spring maven example issues 3 11
Programmer's Notepad is, one of the best free text editing tools available, simply because the developers appear to have second-guessed every weird problem or issue a programmer is likely to run into. One of these problems is selecting and deleti…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
Viewers learn how to read error messages and identify possible mistakes that could cause hours of frustration. Coding is as much about debugging your code as it is about writing it. Define Error Message: Line Numbers: Type of Error: Break Down…
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.

828 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