Tinmar
Tinmar

Reputation: 370

Creating mouse listener for every JLabel newly created

I am building a family tree. The application is creating one JLabel by pressing JButton (with the icon as the person's picture) with this code:

JButton newPersonButton = new JButton("New Person");
newPersonButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {

        final Data data = NewPerson.createPerson(frame);
            if (data != null) {

                if (Val == "mother") {

                    JLabel lblHomer = new JLabel(data.names);
                    lblHomer.setIcon(new ImageIcon(data.fileID));
                    cooX = cooX + 20;
                    cooY = cooY - 20;
                    panel_1.add(lblHomer, "cell " + cooX + " " + cooY);
                    panel_1.revalidate();
                }

I need a JLabel created dynamically by clicking an existing JLabel. I can create another JLabel the same way I created the first one, and the new JLabel use the coordinates of the created JLabel as reference. Not just one, I need ALL of the JLabels to generated like that. When I click on them then it creates another. I know how to make one new JLabel by clicking on it:

JLabel lblHomer = new JLabel(data.names);
lblHomer.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent arg0) {

        final Data data = NewPerson.createPerson(frame);
        if (data != null) {

            String Val = data.CBvalue;

            if (Val == "mother") {

                JLabel lblHomer = new JLabel(data.names);    
                lblHomer.setIcon(new ImageIcon(data.fileID));    
                cooX = cooX + 20;
                cooY = cooY - 20;
                panel_1.add(lblHomer, "cell " + cooX + " " + cooY);
                panel_1.revalidate(); 
            }
        }
   }

But, I can't just copy/paste that same code infinitely. I need to write a while loop or something to keep creating mouse listener for every JLabel I create. I need help with that.

Whole code: http://pastebin.com/zLx1zK9Z

EDIT: Data.java:

public class Data {

    public final String names;
    public final String dateBirth;
    public final String bio;
    public final String fileID;
    public final String CBvalue;

    public Data(String names, String dateBirth, String bio, String fileID, String CBvalue) {
        this.CBvalue = CBvalue;
        this.names = names;
        this.dateBirth = dateBirth;
        this.bio = bio;
        this.fileID = fileID;
    }
// getters
}

Upvotes: 0

Views: 390

Answers (2)

MadProgrammer
MadProgrammer

Reputation: 347332

First...

 if (Val == "mother") {

Is not how you compare Strings in Java, instead you should be using "mother".equals(Val)

Next, you could create a factory method which creates your labels, maybe even a utility class that has several different methods for creating yr labels, although I might consider a builder pattern of you got to this point...

public static JLabel createPersonLabel(Data data, MouseListener listener) {
    JLabel lblHomer = new JLabel(data.names);
    lblHomer.setIcon(new ImageIcon(data.fileID));
    lblHomer.addMouseListener(listener);
}

I would then create a custom MouseListener to handle the core functionary

public class DataMouseHandler extends MouseAdapter {
    public void mouseClicked(MouseEvent evt) {

        final Data data = NewPerson.createPerson(frame);
        if (data != null) {

            String Val = data.CBvalue;

            if ("mother".equals(Val)) {

                JLabel lblHomer = createPersonalLabel(data, this);

                cooX = cooX + 20;
                cooY = cooY - 20;
                panel_1.add(lblHomer, "cell " + cooX + " " + cooY);
                panel_1.revalidate(); 
            }
        }
    }
}

As an example. Your even create a custom MouseListener for each type data you might need

Updated

There are any number of options, for example, you could create a factory style method in your NewFamilyTree class that creates and adds the data label based on your needs and the data type...

protected void createAndAddDataLabel(Data data) {

    JLabel lblHomer = new JLabel(data.names);
    lblHomer.setIcon(new ImageIcon(data.fileID));

    cooX = cooX + 20;
    cooY = cooY - 20;

    panel_1.add(lblHomer, "cell " + cooX + " " + cooY);
    panel_1.revalidate();

    if (!"mother".equals(data.CBvalue)) {
        lblHomer.addMouseListener(new DataMouseHandler());
    }

}

Then call it from within your button...

JButton newPersonButton = new JButton("New Person");
newPersonButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent arg0) {

        final Data data = NewPerson.createPerson(frame);
        if (data != null) {

            createAndAddDataLabel(data);

        }
    }
});

And, as an inner class, you could define the DataMouseHandler...

public class DataMouseHandler extends MouseAdapter {

    @Override
    public void mouseClicked(MouseEvent e) {

        final Data data = NewPerson.createPerson(frame);
        if (data != null) {
            if ("mother".equals(Val)) {
                createAndAddDataLabel(data);
            }
        }
    }
}

If you need this as an external class, you would need to pass the reference of the NewFamilyTree

public class DataMouseHandler extends MouseAdapter {

    private NewFamilyTree tree;

    public DataMouseHandler(NewFamilyTree tree) {
        this.tree = tree;
    }

    @Override
    public void mouseClicked(MouseEvent e) {

        final Data data = NewPerson.createPerson(frame);
        if (data != null) {
            if ("mother".equals(Val)) {
                tree.createAndAddDataLabel(data);
            }
        }
    }
}

Updated

Now, having said all that, this is a classic example of where a Model-view-controller approach would be most suitable.

What you should be doing is modelling the family tree in some way, which is divorced from the UI, so it simply focuses on the requirements of the model, how it's structured and how it's managed.

The model would provide feedback from event notification when it's changed in some way.

From there you would wrap a UI around it, so the UI would simply be responsible for rendering the output of the model.

The controller would take actions from the UI and update the model accordingly, which will in turn trigger updates from the model to the UI...

Upvotes: 2

arcy
arcy

Reputation: 13153

As said in comments, there are a couple of ways to do this. I think the first suggestion is probably best, based on what little I/we understand of what you want to do.

public class ListenerLabel extends JLabel implements MouseListener
{
  public void mouseExited() {}
  public void mouseEntered() {}
  // etc.
  public void mouseClicked() {}
  {
    // here put the code you have for creating another person
    // 'this' refers to the label that was clicked on; you can get coordinates
    // and whatever you need from that.
  }
}

Now instead of creating JLabels to hold your people, you create ListenerLabels; they will interact with your UI just the same as JLabels, but have this extra feature of listening for their own clicks. And their name might be something do with people instead of listening, if they're going to contain code specific to people. ('PeopleLabels', or whatever).

-- edit -- more detail, as requested.

As I look at this again, it seems to me your NewPerson.createPerson() method must be popping up a dialog or something to get information for the new person. I will continue the example assuming that works, though I'd probably have done that differently.

public void mouseClicked()
{
  final Data data = NewPerson.createPerson(frame);
  if (data != null)
  {
    if (data.CBvalue.equals("mother"))  // I would use a constant or enums here instead
    {
      ListenerLabel label = new ListenerLabel(data.names);
      label.setIcon(new ImageIcon(data.fileID));
      int xPosition = this.getX() + 20;
      int yPosition = this.getY() - 20;
      JPanel enclosingPanel = (JPanel)this.getParent();
      enclosingPanel.add(label, "cell " + cooX + " " + cooY);
      // set position of new label here?
      enclosingPanel.revalidate();
    }
  }
}

Adding the new panel to the enclosing panel (which is panel_1 in your example, I assume) is a little 'black magic' to me -- unless that panel has a special layout manager, or you've extended JPanel so that add() means something special, I don't see how that's going to do anything useful. But this more-filled-out method shows where to get the existing label coordinates, a way to get the panel enclosing the two labels, and a suggestion where I would expect you to set the position of the new label.

There are a lot of assumptions here, because you haven't shown us executable code. I know it's difficult to extract running code out of a larger system, but be aware (you and other readers) that any answer for incomplete code is based on partly on assumptions made filling it in and partly on assumptions not made about what else is needed or coded elsewhere.

Upvotes: 1

Related Questions