2

I want to set the most common JTabbedPane mouse event behavior, but I cannot find appropriate options to set:

  1. Left mouse button click - Select tab.

  2. Right mouse button click - Open current tab' dropdown menu.

  3. Wheel mouse button click - Close the tab.

Question: Is there any way to implement them?

PS: Any example from here could be an SSCCE.

SeniorJD
  • 6,946
  • 4
  • 36
  • 53

1 Answers1

7

Tab selection is performed with left mouse button by default, so you don't need to add that feature. Everything else you can find in this small example:

public static void main ( String[] args )
{
    final JFrame frame = new JFrame ();

    final JTabbedPane tabbedPane = new JTabbedPane ();
    tabbedPane.addTab ( "tab1", new JLabel ( "" ) );
    tabbedPane.addTab ( "tab2", new JLabel ( "" ) );
    tabbedPane.addTab ( "tab3", new JLabel ( "" ) );
    tabbedPane.addTab ( "tab4", new JLabel ( "" ) );
    frame.add ( tabbedPane );

    tabbedPane.setUI ( new MetalTabbedPaneUI ()
    {
        protected MouseListener createMouseListener ()
        {
            return new CustomAdapter ( tabbedPane );
        }
    } );

    frame.pack ();
    frame.setLocationRelativeTo ( null );
    frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
    frame.setVisible ( true );
}

private static class CustomAdapter extends MouseAdapter
{
    private JTabbedPane tabbedPane;

    public CustomAdapter ( JTabbedPane tabbedPane )
    {
        super ();
        this.tabbedPane = tabbedPane;
    }

    public void mousePressed ( MouseEvent e )
    {
        final int index = tabbedPane.getUI ().tabForCoordinate ( tabbedPane, e.getX (), e.getY () );
        if ( index != -1 )
        {
            if ( SwingUtilities.isLeftMouseButton ( e ) )
            {
                if ( tabbedPane.getSelectedIndex () != index )
                {
                    tabbedPane.setSelectedIndex ( index );
                }
                else if ( tabbedPane.isRequestFocusEnabled () )
                {
                    tabbedPane.requestFocusInWindow ();
                }
            }
            else if ( SwingUtilities.isMiddleMouseButton ( e ) )
            {
                tabbedPane.removeTabAt ( index );
            }
            else if ( SwingUtilities.isRightMouseButton ( e ) )
            {
                final JPopupMenu popupMenu = new JPopupMenu ();

                final JMenuItem addNew = new JMenuItem ( "Add new" );
                addNew.addActionListener ( new ActionListener ()
                {
                    public void actionPerformed ( ActionEvent e )
                    {
                        tabbedPane.addTab ( "tab", new JLabel ( "" ) );
                    }
                } );
                popupMenu.add ( addNew );

                final JMenuItem close = new JMenuItem ( "Close" );
                close.addActionListener ( new ActionListener ()
                {
                    public void actionPerformed ( ActionEvent e )
                    {
                        tabbedPane.removeTabAt ( index );
                    }
                } );
                popupMenu.add ( close );

                final JMenuItem closeAll = new JMenuItem ( "Close all" );
                closeAll.addActionListener ( new ActionListener ()
                {
                    public void actionPerformed ( ActionEvent e )
                    {
                        tabbedPane.removeAll ();
                    }
                } );
                popupMenu.add ( closeAll );

                final Rectangle tabBounds = tabbedPane.getBoundsAt ( index );
                popupMenu.show ( tabbedPane, tabBounds.x, tabBounds.y + tabBounds.height );
            }
        }
    }
}

Ofcourse you'd better save the displayed menu somewhere so it won't be recreated every time user opens it. You can also move the mouse listener to a separate class to use it every time you need menu and other features.

But my goal was to show you how those things can be done and not making a perfect example, so i guess it is more than enough to start working with tabbed pane :)

Mikle Garin
  • 10,083
  • 37
  • 59
  • 2
    nitpick: tabbedPane's cover method of `ui.tabForCoordinate` is `indexAtLocation` :-) – kleopatra Aug 07 '13 at 15:54
  • 1
    @kleopatra yeh well, i just don't remember all the names and sometimes use the UI methods instead - there is no difference anyway since that method is in TabbedPaneUI interface and available in any L&F. The thing i don't like about those covering methods - seems that their names are taken randomly, seriously - why don't just take the same method name as in the UI?.. – Mikle Garin Aug 07 '13 at 19:28
  • actually there _is_ a difference: the cover method guards against a null ui ;) Not probable, but can happen ... – kleopatra Aug 07 '13 at 19:32
  • 1
    that is almost an impossible case unless you try to do some really dumb things and mess around with Swing UI where it should not be done ;) – Mikle Garin Aug 07 '13 at 20:16
  • @MikleGarin Thanks, but there is one BUT: default `TabbedPane` mouseListener still working&existing, so my solution is to extend `DefaultTabbedPaneUI` and override `createMouseListener` method with the logic, close to yours. – SeniorJD Aug 08 '13 at 07:17
  • Basically, I don't disagree with that assessment :-) – kleopatra Aug 08 '13 at 07:19
  • @kleopatra "don't disagree".equalsByMeaning("agree")? – SeniorJD Aug 08 '13 at 08:10
  • @SeniorJD more or less, yeah (but note that we are nitpicking - the last item being a null ui :-) – kleopatra Aug 08 '13 at 08:19
  • @SeniorJD i have edited the example - it now has selection included and overrides Metal UI. And i found a funny Swing bug while looking at the MetalTabbedPaneUI - its Handler implements MouseMotionListener, but only mouse listener is added into tabbed pane, so mouseMoved/mouseDragged events are simply ignored. – Mikle Garin Aug 08 '13 at 09:15
  • setting a custom ui per-instance is ... a no-go: you don't want to remember doing it for each tabbedPane, and it will not survive the slightest sneeze of toggling the LAF. – kleopatra Aug 08 '13 at 09:52
  • @kleopatra That is only an example where adapter can be used. I don't really want to include tons of code which displays how and where UI class should be *properly* used - that will be an offtopic already. I bet SeniorJD already have his own TabbedPaneUI implementation where he will use the CustomAdapter code given in my example. – Mikle Garin Aug 08 '13 at 13:09
  • examples are just that .. examples. Nevertheless, they should contain disclaimers at the example-ness shortcuts, IMO :) Pretty sure that @SeniorJD know his/her way around, but answers here are read by newbies. Just saying .. – kleopatra Aug 08 '13 at 21:08