4

I have a Swing app with a main frame and some other forms loaded inside it. I Need to implement a general method to set the hand cursor for all buttons on any form.

This is similar of what we do with css on web pages (input[type=button] { cursor:pointer; })

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Juan Jimenez
  • 5,812
  • 6
  • 28
  • 37

2 Answers2

6

Walking the tree like @Madprogrammer suggested is the method if you want to change the cursor dynamically and/or on a particular form.

Just for fun (and to show-off SwingX again :) - if you want to install that cursor globally and then don't care about, install a ui-delegate which takes care of it. In SwingX, it's as simple as implementing and plugging a custom button addon. The side-effect is the same as in the other answer (though can't be solved as in that). The usual drawback (as always when installing custom ui delegates) is the need to subclass and plug-in delegates for all LAFs.

public class ButtonCursorAddon extends AbstractComponentAddon {

    /**
     * @param name
     */
    protected ButtonCursorAddon() {
        super("RolloverCursor");
    }

    @Override
    protected void addBasicDefaults(LookAndFeelAddons addon,
            DefaultsList defaults) {
        UIManager.getDefaults().remove("ButtonUI");
        defaults.add("ButtonUI", "org.jdesktop.swingx.plaf.ButtonCursorAddon$BasicButtonCursorUI");
    }

    @Override
    protected void addMetalDefaults(LookAndFeelAddons addon,
            DefaultsList defaults) {
        UIManager.getDefaults().remove("ButtonUI");
        defaults.add("ButtonUI", "org.jdesktop.swingx.plaf.ButtonCursorAddon$MetalButtonCursorUI");
    }

    @Override
    protected void addWindowsDefaults(LookAndFeelAddons addon,
            DefaultsList defaults) {
        UIManager.getDefaults().remove("ButtonUI");
        defaults.add("ButtonUI", "org.jdesktop.swingx.plaf.ButtonCursorAddon$WindowsButtonCursorUI");
    }


    @Override
    protected void addNimbusDefaults(LookAndFeelAddons addon,
            DefaultsList defaults) {
        UIManager.getDefaults().remove("ButtonUI");
        defaults.add("ButtonUI", "org.jdesktop.swingx.plaf.ButtonCursorAddon$SynthButtonCursorUI");
    }


    public static class BasicButtonCursorUI extends BasicButtonUI {

        public static ComponentUI createUI(JComponent c) {
            return new BasicButtonCursorUI();
        }

        @Override
        protected BasicButtonListener createButtonListener(AbstractButton b) {
            return new BasicHyperlinkListener(b);
        }

    }

    public static class SynthButtonCursorUI extends SynthButtonUI {

        public static ComponentUI createUI(JComponent c) {
            return new SynthButtonCursorUI();
        }

        @Override
        protected BasicButtonListener createButtonListener(AbstractButton b) {
            return new BasicHyperlinkListener(b);
        }

    }

    public static class MetalButtonCursorUI extends MetalButtonUI {

        public static ComponentUI createUI(JComponent c) {
            return new MetalButtonCursorUI();
        }

        @Override
        protected BasicButtonListener createButtonListener(AbstractButton b) {
            return new BasicHyperlinkListener(b);
        }

    }

    public static class WindowsButtonCursorUI extends WindowsButtonUI {

        public static ComponentUI createUI(JComponent c) {
            return new WindowsButtonCursorUI();
        }

        @Override
        protected BasicButtonListener createButtonListener(AbstractButton b) {
            return new BasicHyperlinkListener(b);
        }

    }

}

// usage: plug-in once in your application code (before creating any buttons)
static {
    LookAndFeelAddons.contribute(new ButtonCursorAddon());
}

I get this error: UIDefaults.getUI() failed: no ComponentUI class

Works for me - when registering the ui class with the UIManager, it needs the fully qualified class name to instantiate the delegate from:

// here the ButtonCursorUI is in package
// org.jdesktop.swingx.plaf
defaults.add("ButtonUI", "org.jdesktop.swingx.plaf.ButtonCursorAddon$WindowsButtonCursorUI");
// in another package that would be
defaults.add("ButtonUI", myPackageName + ".ButtonCursorAddon$WindowsButtonCursorUI");

Typically, you would have the different delegates in LAF specific subpackages of something.plaf (instead of in the addon itself) But then, it's an example :-)

kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • seems like a good approach and I'm actually using swingx on my project (sdk 1.6) but when I put it on my man class I get this error: UIDefaults.getUI() failed: no ComponentUI class for: javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=0,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=null,paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=,defaultCapable=true] – Juan Jimenez Aug 19 '12 at 23:42
5

Basically, you'll have to walk the container and sub containers...

Be careful though, you'll be surprised by what is a button

public static void setButtonCursor(JComponent component, Cursor cursor) {

    for (Component comp : component.getComponents()) {

        if (comp instanceof JButton) {

            comp.setCursor(cursor);

        } else if (comp instanceof JComponent) {

            setButtonCursor((JComponent)comp, cursor);

        }

    }

}

This has the nice side effect of walking into JComboBoxs (among other components) and changing the cursor for their drop buttons, so be careful ;)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • nice, do I have to call it on each form?, is there a way to make it general? – Juan Jimenez Aug 18 '12 at 06:03
  • Basically, pass in the highest container you can, but otherwise, yes. – MadProgrammer Aug 18 '12 at 06:05
  • If you change `comp instanceof JButton` by `comp.getClass().equals(JButton.class)`, you'll not have the side effects anymore... – aymeric Aug 18 '12 at 08:07
  • @MadProgrammer thanks for your advice but this is not a suitable solution as I have lots of screens and don't want to call this method on each, I know another approach would be to inherith from base JFrame or JButton classes with the cursor applied but again I'm looking for a more generic solution. – Juan Jimenez Aug 20 '12 at 00:18
  • @aymeric when I apply your suggestion my buttons dissapear – Juan Jimenez Aug 20 '12 at 00:18