8

I have a JPanel which contains a JToolbar (including few buttons without text) and a JTable and I need to enable/disable (make internal widgets not clickable). I tried this:

 JPanel panel = ....;
 for (Component c : panel.getComponents()) c.setEnabled(enabled);

but it doesn't work. Is there a better and more generic solution to enable/disable all internal components in a JPanel?

I have partially solved my problem using JLayer starting from the example here http://docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html:

layer = new JLayer<JComponent>(myPanel, new BlurLayerUI(false));
.....
((BlurLayerUI)layer.getUI()).blur(...); // switch blur on/off


class BlurLayerUI extends LayerUI<JComponent> {
  private BufferedImage mOffscreenImage;
  private BufferedImageOp mOperation;

  private boolean blur;

  public BlurLayerUI(boolean blur) {
      this.blur = blur;
      float ninth = 1.0f / 9.0f;
        float[] blurKernel = {
          ninth, ninth, ninth,
          ninth, ninth, ninth,
          ninth, ninth, ninth
        };
        mOperation = new ConvolveOp(
                new Kernel(3, 3, blurKernel),
                ConvolveOp.EDGE_NO_OP, null);
        }

  public void blur(boolean blur) {
      this.blur=blur;
    firePropertyChange("blur", 0, 1);
   }

  @Override
  public void paint (Graphics g, JComponent c) {
      if (!blur) {
            super.paint (g, c);
            return;
        }

      int w = c.getWidth();
    int h = c.getHeight();



    if (w == 0 || h == 0) {
      return;
    }

    // Only create the offscreen image if the one we have
    // is the wrong size.
    if (mOffscreenImage == null ||
            mOffscreenImage.getWidth() != w ||
            mOffscreenImage.getHeight() != h) {
      mOffscreenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    }

    Graphics2D ig2 = mOffscreenImage.createGraphics();
    ig2.setClip(g.getClip());
    super.paint(ig2, c);
    ig2.dispose();

    Graphics2D g2 = (Graphics2D)g;
    g2.drawImage(mOffscreenImage, mOperation, 0, 0);
  }

  @Override
  public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) {
    if ("blur".equals(pce.getPropertyName())) {
      l.repaint();
    }
  }

}

I still have 2 problems:

  1. In the link above events are relative to mouse only. How can I manage the keyboard events?

  2. How can I create a "gray out" effect in place of blur?

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Randomize
  • 8,651
  • 18
  • 78
  • 133

4 Answers4

45

It requires a recursive call.

Disable All In Container

import java.awt.*;
import javax.swing.*;

public class DisableAllInContainer {

    public void enableComponents(Container container, boolean enable) {
        Component[] components = container.getComponents();
        for (Component component : components) {
            component.setEnabled(enable);
            if (component instanceof Container) {
                enableComponents((Container)component, enable);
            }
        }
    }

    DisableAllInContainer() {
        JPanel gui = new JPanel(new BorderLayout());

        final JPanel container = new JPanel(new BorderLayout());
        gui.add(container, BorderLayout.CENTER);

        JToolBar tb = new JToolBar();
        container.add(tb, BorderLayout.NORTH);
        for (int ii=0; ii<3; ii++) {
            tb.add(new JButton("Button"));
        }

        JTree tree = new JTree();
        tree.setVisibleRowCount(6);
        container.add(new JScrollPane(tree), BorderLayout.WEST);

        container.add(new JTextArea(5,20), BorderLayout.CENTER);

        final JCheckBox enable = new JCheckBox("Enable", true);
        enable.addActionListener(new ActionListener(){
            @Override
            public void actionPerformed(ActionEvent ae) {
                enableComponents(container, enable.isSelected());
            }
        });
        gui.add(enable, BorderLayout.SOUTH);

        JOptionPane.showMessageDialog(null, gui);
    }

    public static void main(String[] args)  {
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                new DisableAllInContainer();
            }
        });
    }}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Thanks that solution is very close to mine solution. But it has same problem: I have a JTable and if you apply setEnabled(false) on it, the table becomes not clickable but it is not "grayed" opposite to remaining components. – Randomize Jun 11 '12 at 19:55
  • 1
    It also has the problem of, if some of the components in the container were enabled and other disabled, then they will all be set to the same enabled status, eg: You can not do an enable, and then trivially undo it. – user1062589 Nov 21 '13 at 22:17
  • 1
    declare enableComponents(...) static. very nice utility function – Exceptyon Feb 08 '16 at 10:35
5

I used the following function:

void setPanelEnabled(JPanel panel, Boolean isEnabled) {
    panel.setEnabled(isEnabled);

    Component[] components = panel.getComponents();

    for(int i = 0; i < components.length; i++) {
        if(components[i].getClass().getName() == "javax.swing.JPanel") {
            setPanelEnabled((JPanel) components[i], isEnabled);
        }

        components[i].setEnabled(isEnabled);
    }
}
Kesavamoorthi
  • 959
  • 2
  • 11
  • 21
2

you can overlay whole Container / JComponent

  1. GlassPane block by default MouseEvents, but not Keyboard, required consume all keyevents from ToolKit

  2. JLayer (Java7) based on JXLayer (Java6)

  3. can't see reason(s) why not works for you

enter image description here

enter image description here

enter image description here

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

public class AddComponentsAtRuntime {

    private JFrame f;
    private JPanel panel;
    private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;

    public AddComponentsAtRuntime() {
        JButton b = new JButton();
        //b.setBackground(Color.red);
        b.setBorder(new LineBorder(Color.black, 2));
        b.setPreferredSize(new Dimension(600, 20));
        panel = new JPanel(new GridLayout(0, 1));
        panel.add(b);
        f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel, "Center");
        f.add(getCheckBoxPanel(), "South");
        f.setLocation(200, 200);
        f.pack();
        f.setVisible(true);
    }

    private JPanel getCheckBoxPanel() {
        checkValidate = new JCheckBox("validate");
        checkValidate.setSelected(false);
        checkReValidate = new JCheckBox("revalidate");
        checkReValidate.setSelected(true);
        checkRepaint = new JCheckBox("repaint");
        checkRepaint.setSelected(true);
        checkPack = new JCheckBox("pack");
        checkPack.setSelected(true);
        JButton addComp = new JButton("Add New One");
        addComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JButton b = new JButton();
                //b.setBackground(Color.red);
                b.setBorder(new LineBorder(Color.black, 2));
                b.setPreferredSize(new Dimension(400, 10));
                panel.add(b);
                makeChange();
                System.out.println(" Components Count after Adds :" + panel.getComponentCount());
            }
        });
        JButton removeComp = new JButton("Remove One");
        removeComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                int count = panel.getComponentCount();
                if (count > 0) {
                    panel.remove(0);
                }
                makeChange();
                System.out.println(" Components Count after Removes :" + panel.getComponentCount());
            }
        });
        JButton disabledComp = new JButton("Disabled All");
        disabledComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                for (Component c : panel.getComponents()) {
                    c.setEnabled(false);
                }
            }
        });
        JButton enabledComp = new JButton("Enabled All");
        enabledComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                for (Component c : panel.getComponents()) {
                    c.setEnabled(true);
                }
            }
        });
        JPanel panel2 = new JPanel();
        panel2.add(checkValidate);
        panel2.add(checkReValidate);
        panel2.add(checkRepaint);
        panel2.add(checkPack);
        panel2.add(addComp);
        panel2.add(removeComp);
        panel2.add(disabledComp);
        panel2.add(enabledComp);
        return panel2;
    }

    private void makeChange() {
        if (checkValidate.isSelected()) {
            panel.validate();
        }
        if (checkReValidate.isSelected()) {
            panel.revalidate();
        }
        if (checkRepaint.isSelected()) {
            panel.repaint();
        }
        if (checkPack.isSelected()) {
            f.pack();
        }
    }

    public static void main(String[] args) {
        AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • thanks for the multi answers: 1) GlassPane is interesting tho I don't know how to get a blurred or grayed effect. 2) I am working with Java 6 and I don't want (for the moment) using external libraries. 3) This solution has problems with JTable. – Randomize Jun 11 '12 at 19:52
  • 1) no problem put there JLabel.setOpaque(false) and put there some Color (int, int, int, alpa), 2) JXLabel isn't external libraries, this is personal project accepted as JLayer, 3) put JLabel to the (same as in first point) JViewPort (I hope that JTable is into JScrollPane) 4) yo can to check my posts GlassPane and JViewport tagged – mKorbel Jun 11 '12 at 20:00
  • I am trying with JLayer and I solved partially the problem. Please look at my re-edited question. – Randomize Jun 11 '12 at 21:11
  • [maybe (nothing better around) by @camickr](http://tips4java.wordpress.com/?s=glasspane) and you can [possitioning GlassPane too](http://stackoverflow.com/a/9734016/714968) – mKorbel Jun 12 '12 at 05:55
0

@Kesavamoorthi if you want to make it more general:

void setPanelEnabled(java.awt.Container cont, Boolean isEnabled) {
    cont.setEnabled(isEnabled);

    java.awt.Component[] components = cont.getComponents();

    for (int i = 0; i < components.length; i++) {
        if (components[i] instanceof java.awt.Container) {
            setPanelEnabled((java.awt.Container) components[i], isEnabled);
        }
        components[i].setEnabled(isEnabled);
    }
}
Daniel
  • 403
  • 2
  • 15