Proper way to use JMenuBar?

I want to use a JMenuBar to produce some "redundant" functionality into my program. Say, for instance, the user wants to print something. I can have a print button on screen an a print option in the file menu. I can have both objects call a static printRecords() menu in one of my other classes. This part isn't a problem.

The problem I think I'm going to run into is JMenu items that try to reproduce the functionality of actions in classes that it doesn't have access to. Say, for instance, I have a JPanel full of JTextFields and it also has a button that says "Clear Fields." Currently, my design has the implementation for the button in the JPanel class. This works except that the JMenu does not have access to this method.

What's the best approach to reducing the amount of code that I have to reuse without passing information all over the place?

[877 byte] By [Brimstone7a] at [2007-10-1 20:14:27]
# 1

> Currently, my design has the

> implementation for the button in the JPanel class.

>

> What's the best approach to reducing the amount of

> code that I have to reuse without passing information

> all over the place?

Don't subclass swing components, unless you want to modify the graphical capabilities of the components.

Implement Listener classes instead, and helper classes if needed. Use a composer classe to create the JComponents and wire them together and plug the listeners. Use Mediator pattern if you have lots of inter-widget interactions.

In your case, add the same ActionListener on the "Clear" menu item and on the "Clear" button, or create them with the same Action (sorry my Swing memories are far behind).

Brgds.

jdupreza at 2007-7-13 2:15:47 > top of Java-index,Other Topics,Patterns & OO Design...
# 2

The only class that I extend is the JPanel class because it makes it easier create and manage the complex layout that I need. Are you suggesting there is a better way to do this without extending JPanel? I looked up the term "composer class" but couldn't find anything. Can you go into more detail about this term? Also, I figured I could use the same Action for the different components but my problem is that this action won't have access to the objects in needs to access. For example, how would a "Clear" Action have a handle to the text fields it needs to clear?

Brimstone7a at 2007-7-13 2:15:47 > top of Java-index,Other Topics,Patterns & OO Design...
# 3

Something like this?import java.awt.*;

import java.awt.event.*;

import javax.swing.*;

public class TestFrame extends JFrame

{

private InfoPanel infoPanel = new InfoPanel();

public TestFrame()

{

super("Shared Listener Test");

setJMenuBar(createMenuBar());

getContentPane().add(infoPanel);

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

setSize(180,140);

setLocation(300,300);

}

private JMenuBar createMenuBar()

{

JMenuBar menuBar = new JMenuBar();

JMenu menu;

JMenuItem item;

menu = new JMenu("File");

item = new JMenuItem("Clear");

item.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent evt)

{

//just tell the panel to clear himself

infoPanel.clear();

}

});

menu.add(item);

item = new JMenuItem("Exit");

item.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent evt)

{

System.exit(0);

}

});

menu.add(item);

menuBar.add(menu);

return menuBar;

}

public static void main(String[] args)

{

JFrame f = new TestFrame();

f.setVisible(true);

}

}

class InfoPanel extends JPanel

{

private JTextField name = new JTextField(10);

private JTextField address = new JTextField(30);

private JTextField phone = new JTextField(20);

private JButton button = new JButton("Clear");

public InfoPanel()

{

button.addActionListener(new ActionListener()

{

public void actionPerformed(ActionEvent evt)

{

clear();

}

});

setLayout(new GridLayout(4,2));

add(new JLabel("Name"));

add(name);

add(new JLabel("Address"));

add(address);

add(new JLabel("Phone"));

add(phone);

add(button);

}

public void clear()

{

name.setText("");

address.setText("");

phone.setText("");

}

}

KelVarnsona at 2007-7-13 2:15:47 > top of Java-index,Other Topics,Patterns & OO Design...
# 4

Thanks for the reply. Unfortunately things are a little more complicated in my case because I've nested several JPanel classes inside of each other, so I have to do something like:

JFrame --> SearchPanel --> SearchViewPanel

but I got everything to work so no worries. Thanks again.

Brimstone7a at 2007-7-13 2:15:47 > top of Java-index,Other Topics,Patterns & OO Design...
# 5

Assuming you still care about this issue (or that others do), I know I'm coming back long time after...

> The only class that I extend is the JPanel class because it makes it easier

> to create and manage the complex layout that I need.

> Are you suggesting there is a better way (...) without extending JPanel?

Yes.

Write business logic in non-graphic classes, write interaction logic into dedicated classes (depending on swing API but not extending any JComponent), and write the building and wiring of graphical components to the intercation classes in a dedicated composer class (you're right I made up this term, I don't know if a generally accepted one exists).

> I looked up the term "composer class" but couldn't find anything.

> Can you go into more detail about this term?

Something along the line of:

// Simple class, one text field, one clear button

public class MyGUIComposer

{

public void JFrame composeScreen()

{

JFrame theFrame = new JFrame();

JPanel mainPanel = new JPanel();

mainPanel.setLayout(...)

JPanel subPanel1 = new JPanel();

...

final JTestField myTextField = new JTextField(...);

Action buttonLogic = new ClearAction(myTextField);

JButton clearButton = new JButton(buttonLogic);

}

With ClearAction.actionperformed() methods being as simple as the TextField.cleartext();

If the configuration of the screen is more complex (e.g. lots of text fields, other widgets,...), you can use a Mediator class (this one is a known and documented design pattern). See below.

> Also, I figured I could use the same Action for the different components

> but (...)

> how would a "Clear" Action have a handle to the fields it needs to clear?

It should have a handle to a Mediator object: this latter has already references to all widgets. And the clear action does not even bother clearing all buttons but just ask the Mediator to do so.

The Mediator can itself be the composer, in this case the creation of the action becomes:

Action buttonLogic = new ClearAction(this); // this being the mediator instance

and ClearAction.actionPerformed() becomes as simple as

public void actionPerformed() { theMediator.clearAllFields();}

And of course you write the mediator class with the method clearAllFields() which iterates through all relevant fields and clears them.

At first it looks much more convoluted, but the Mediator really comes handy when you add features to your UI : more widgets, more interactions, more conditions (e.g. clear button clears all fields and adds a message in the status bar, and greys out the submit button only if the "Enable empty names" option is false.

I find it better than having a JPanel subclass having to pass around the value of all options.

jdupreza at 2007-7-13 2:15:47 > top of Java-index,Other Topics,Patterns & OO Design...