2

Current I can add a bunch of customed component objects to the JPanel by pressing "add" JButton. I also got a "delete" JButton which I wish to do the opposite of "add".

My intention is that I can select a component with a mouse and click the delete button and pressto!, the component is gone.

I hook a MouseListener to the panel, and use MouseEvent, e.getComponent() to get w/e current component the mouse clicks on. So if it returns a custom component then a variable "private myComponent current" (already set to null) will point to that component. Then I can just click on "delete" button to remove it. An actionListener already added in "delete" button and in the body it calls this.remove(current) (if current is not null).

However, this doesn't work as I can't remove a component! Any pointer?

If there is an elegant way to managing add/remove components please suggest!

public class MainDisplayPanel extends JPanel implements ActionListener, MouseListener{

private JButton newClassButton;
private JButton deleteButton;
private Resizable current;
private Resizable resizer;
public MainDisplayPanel(LayoutManager layout) {
    super(layout);

    newClassButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
            addResizer();
     }          
    });

    deleteButton = new JButton("Delete");
    deleteButton.addActionListener(this);
    this.addMouseListener(this);
    this.add(newClassButton);
    this.add(deleteButton);

}
public void addResizer() {
    //JPanel panel = new JPanel();
    //panel.setBackground(Color.white);
     resizer = new Resizable( new ClassBox());
    this.add(resizer);
    this.revalidate();
    this.repaint();
}

public void actionPerformed(ActionEvent e) {
          if(current!=null)
          {
              this.remove(current);
              this.revalidate();
              this.repaint();

          }
}

public void mouseClicked(MouseEvent e) {
    System.out.println(e);
            Component component = e.getComponent();

            if(component instanceof Resizable)
                current= (Resizable) e.getComponent();

}


    public static void main(String[] args) {
    JFrame jframe = new JFrame();
    jframe.add(new MainDisplayPanel(null));
    jframe.setSize(new Dimension(600,400));
    jframe.setVisible(true);
    jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

} Doh!

Now, in the addResizer() method. Every time I press the add button to add a new Resizable object, what'd happen to the previously added objects? I'm certain that they become null because resizer variable no longer referring to it them??? Even if this is the case, they are still displayed on the panel...And if I pressed delete only the newly added Resizable object gets removed. So am I on the right track here?

Edit: to sum up my problem, I hooked the MouseListener to wrong object. It should be Resizable object instead of the panel. Therefore, variable current is always null.

bili
  • 610
  • 2
  • 9
  • 20
  • Little hard to tell the exact problem without some of your code, but my guess would be some sort of scoping issue (especially with any anonymous listeners). Otherwise, I think this is roughly what I'd attempt too... – Clockwork-Muse Aug 23 '11 at 20:47
  • Yes, I uses anonymous listeners. Added in some code too. – bili Aug 23 '11 at 21:22

3 Answers3

3

it very crazy idea, but everything is possible, but

1) in case that you Layed JComponent by using some of LayoutManager you can remove JComponents from Container, and thenafter you must/have to call revalidate() + repaint(), but this actions has side effect -> ReLayout Container and then Container's contents could be look very ***

2) in case that you layed Container with AbsoluteLayout, that should be maybe nicest but question is what with emtpy space inside Container

there is very easy way how to do it, you need to add JPopupMenu to the Container,

  • on RightMouseClick you have to finding JComponent under the MouseCursor

  • then call Container#remove(myComponent), thenafter you have to call revalidate() + repaint() for refresh GUI

or is same for me

Community
  • 1
  • 1
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • The bigPane contains 2 other JPanel, mainDisplayPane is one of them and initilise with null LayoutManager because my customed component can be drawn drag & resize anywhere. Other pane is just there for the toolbar. I will try the popup menu one see if it helps. – bili Aug 23 '11 at 21:57
  • @bili then result is here http://stackoverflow.com/questions/7062154/java-define-unit-component-for-mouse-events last code by @dacwe about `Component component = SwingUtilities.getDeepestComponentAt(...)` should be work for that – mKorbel Aug 23 '11 at 22:04
  • the code from that link looks pretty scary to me. I will try not to resort to it if I really can't sort this out with any other way. And thank you! – bili Aug 24 '11 at 01:57
3

Your problem is your MouseLisetener. You are listening to the MainDisplayPanel, and so when you click on the JPanel, the MouseEvent#getComponent method returned by, e, in your mousePressed method will return the MainDisplayPanel instance since that is what is being listened to, not the Resizable instance that is under the mouse.

Solutions include:

  • creating one MouseListener object and adding this same object to each Resizable as a MouseListener for the Resizable, or
  • using your current set up, but hold your Resizable's in an ArrayList and then iterating through the array list in the mousePressed method to see if any Resizable has been clicked by using the componentAt(...) method.

Note that I had to create my own SSCCE to solve this. Again in the future, please do us all a favor and do this for us as it really is in your and our best interest, and shows that you respect our time and our help.

Edit 1
My SSCCE:

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.*;

@SuppressWarnings("serial")
public class MainDisplayPanel extends JPanel {
   private static final int RESIZABLE_COUNT = 40;
   private JButton deleteButton;
   private Resizable current;
   private Resizable resizer;
   private List<Resizable> resizableList = new ArrayList<Resizable>();

   public MainDisplayPanel(LayoutManager layout) {
      super(layout);

      deleteButton = new JButton("Delete");
      deleteButton.addActionListener(new ActionListener() {
         @Override
         public void actionPerformed(ActionEvent e) {
            deleteButtonActionPerformed(e);
         }
      });
      this.addMouseListener(new MyMouseAdapter());
      this.add(deleteButton);

      for (int i = 0; i < RESIZABLE_COUNT; i++) {
         addResizer();
      }
   }

   private void deleteButtonActionPerformed(ActionEvent e) {
      if (current != null) {
         this.remove(current);
         resizableList.remove(current);
         current = null;
         this.revalidate();
         this.repaint();
      }
   }

   public void addResizer() {
      resizer = new Resizable();
      this.add(resizer);
      resizableList.add(resizer);
      this.revalidate();
      this.repaint();
   }

   private class MyMouseAdapter extends MouseAdapter {

      @Override
      public void mousePressed(MouseEvent e) {
         current = null;
         Component c = getComponentAt(e.getPoint());
         for (Resizable resizable : resizableList) {
            if (resizable == c) {
               current = resizable;
               resizable.setFill(true);
            } else {
               resizable.setFill(false);
            }
         }
      }
   }

   public static void main(String[] args) {
      JFrame jframe = new JFrame();
      // !! jframe.add(new MainDisplayPanel(null));
      jframe.add(new MainDisplayPanel(new FlowLayout()));
      jframe.setSize(new Dimension(600, 400));
      jframe.setVisible(true);
      jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }

}

@SuppressWarnings("serial")
class Resizable extends JPanel {

   private static final int RESIZE_WIDTH = 50;
   private static final int RESIZE_HEIGHT = 40;
   private static final int THICKNESS = 5;
   private static final Color FILL_COLOR = Color.pink;

   public Resizable() {
      Random rand = new Random();

      // different color border so we can see that it was the clicked one that was deleted.
      Color color = new Color(
               rand.nextInt(255), 
               rand.nextInt(255), 
               rand.nextInt(255));
      setBorder(BorderFactory.createLineBorder(color, THICKNESS));
   }

   @Override // so we can see it
   public Dimension getPreferredSize() {
      return new Dimension(RESIZE_WIDTH, RESIZE_HEIGHT);
   }

   public void setFill(boolean fill) {
      Color fillColor = fill ? FILL_COLOR : null;
      setBackground(fillColor);
      repaint();
   }

}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you Hovercraft! I added a MouseListener to resizer variable in addResizer(), so everytime the mouse is clicked, it sets current to resizer and it works! The reason I didn't post EECS is that I admit that I was being lazy. But the reason is that I didn't think about reduce Resizer to a single JPanel class. What happens is that Resizer constructor takes in an object and this object contains other objects which in turn take another object with bunch of customised components with ActionListener and MouseListener. Some code make use of FactoryMethod so the whole code is pretty long. – bili Aug 24 '11 at 11:43
  • I understand this problem is quite subtle that I needed to post EECS up front. After I saw your ealier post that you mentioned bug I looked harder at the code but obviously I wont be able to spot the listener issue because it never occured to me in the first place that I hook up listener to wrong object. I duly noted about posting EECS. In furture, if I post any code at all, I will think harder about reducing code before posting. And I appreciate the time and efford you and everyone else spends to look at and give guidance to the question that I have. Thanks again! – bili Aug 24 '11 at 11:56
2

I believe the problem is you need to force Swing to layout the components again after removing one. After you remove(current), call revalidate().

antlersoft
  • 14,636
  • 4
  • 35
  • 55
  • `revalidate` and then `repaint` the latter especially if a component is removed. – Hovercraft Full Of Eels Aug 23 '11 at 21:24
  • Yes, I do use them but the problem seems to be nothing get removed in the first place. – bili Aug 23 '11 at 21:25
  • Then you've got a bug, perhaps a misplaced reference -- in that the JPanel you're trying to remove is not the one that's been added in the first place, but who knows since we can't compile or run your code. I suggest you create and post an [SSCCE](http://sscce.org) (please click on and check out the link), and we'll be in a much better position to be able to give you real help and not silly wild @ssed guesses. – Hovercraft Full Of Eels Aug 23 '11 at 21:58
  • @Hovercraft: Sorry, I missed this comment of yours earlier. I think I'm closed to solve this problem and it's a fun one to me. Please look at the new edit from my original post. I'm testing on the MainDisplayPanel so there is no other JPanel involved. If I still can't solve this I will post an SSCCE. And thank you. – bili Aug 24 '11 at 02:21
  • @bili: this is really not fair. For us to help you we have to take your code and make an SSCCE ourselves because you are for some reason not willing to do so (I'm not sure why, hopefully it's not because you're too lazy). This is so we can run it, test it and modify it. But you are the one requesting the help, and so you should be the one doing this, not us. Please rectify this situation. – Hovercraft Full Of Eels Aug 24 '11 at 03:11