Custom/General Ok and Cancel Button Actions

Hi,

I have a big application, and I have custom JButtons with different states. What I want to do is to group these buttons, and when a button is pressed, it only changes state when the OK button is pressed. This would be for all the buttons in the group.

If the CANCEL button is pressed, the button gets it initial state.

And I want to make this dynamic and general, I want to make custom OK and CANCEL buttons, to use throughout my whole application.

My thoughts:

Have a custom Button Group class

Add the buttons to the class

Add Ok and Cancel buttons to the Button Group

The actions for the OK an CANCEL buttons should apply for all buttons in a group.

I am a little confused on how to implement this.

Help please, any ideas?

[795 byte] By [Miss_Ma] at [2007-10-2 4:00:08]
# 1

Well, first I'd have to say that that arrangement seems likely to be confusing for the users. There needs to be some kind of indication of whether you've clicked the button or not, so that you know what the new state will be when you click OK. You also need to be able to unclick a particular button. It's hard to imagine a situation where some combination of checkboxes and radio buttons wouldn't serve you better.

However, if you really must do this, then write three subclasses of JButton, one for the state buttons with an extra flag for the new state. One for your OK, which probably stores a list of the state buttons it controlls and a similar one for cancel.

malcolmmca at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 2

I'm sorry I was unclear in my description. My custom JButton actually have several states, and the user can ofcourse see when a button is pressed, before the Ok or Cancel button is pressed. The user can also unclick a button.

The thing is, I am considering to write a button group class, to group the buttons that shall belong for an Ok and Cancel button. Because this OK/Cancel scenario will occur many times in my application, I want to make a custom action for the OK and Cancel. Any ideas on how to do this?

Miss_Ma at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 3

Well, it clearly needs to contain a collection pointing to your detail buttons.

How about this: You define a class which contains an ArrayList of the multi-state controlls. An add method registers them. Inside this class you define two instances of (probably anonymous) Action objects derived from AbstractAction, one for OK, one for cancel. Getter methods retrieve these. You can use these to create JButtons, and also register them as keyboard mappings if you wish.

The actionPerformed methods of these inner classes invoke a private outer-class method to reset or confirm all the buttons.

class MultiButtonGroup {

List<MultiButton> buttons = new ArrayList<MultiButton>(10);

private Action okAction = new AbstractAction("OK") {

public void actionPerformed(ActionEvent e) {

confirmAll();

}

};

public Action getOKAction() { return okAction; }

public void add(MutliButton b) {

buttons.add(b);

}

private void confirmAll() {

...

}

.... etc.

malcolmmca at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 4

Thank you, I'm gonna try your suggestion. I am a little unsure on how to use the AbstractAction, though. Why AbstractAction, and not ActionListener?

You say I can use the actions to create JButtons, but I don't quite understand...sorry, can you explain or direct me to where I can read about this?

Miss_Ma at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 5

> Thank you, I'm gonna try your suggestion. I am a

> little unsure on how to use the AbstractAction,

> though. Why AbstractAction, and not ActionListener?

>

Abstract action defines a number of methods in the action interface that you would otherwise have to supply yourself.

> You say I can use the actions to create JButtons, but

> I don't quite understand...sorry, can you explain or

> direct me to where I can read about this?

There's a constructor for JButton which takes an Action as its argument. If you create a button that way it's hooked into the Action, copies it's lablel from the action and delivers it's clicks to it.

You can also place an action in the form's ActionMap, which, in conjuction with the InputMap, allows you to perform the action in response to, say, hitting the ENTER key.

malcolmmca at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 6

Ok, thanks, I'm getting there, I think... I have made a class containing the buttons in a group, OkCancelGroup.

I also have custom JButton classes for okButton and cancelButton, with constructors that takes an action, and sets addActionListener(action).

Something like this:

public class OkCancelGroup {

//Properties

private Vector components;

/** Creates a new instance of OkCancelGroup */

public OkCancelGroup() {

RRApplyButton okButton = new OkButton(getApplyAction());

RRCancelButton cancelButton = new CancelButton(getCancelAction());

}

/**

*Add a component to the group

*/

public void add(Component c){

if(c == null) {

return;

}

components.add(c);

}

/**

*Removes the component from the group.

*/

public void remove(Component c){

if(c == null) {

return;

}

components.remove(c);

}

/**

*Returns the number of components in the group.

*/

public int getComponentCount(){

if (components == null) {

return 0;

} else {

return components.size();

}

}

/**

* Returns all the components that are participating in this group.

*/

public Enumeration getElements(){

return components.elements();

}

/**

* action when ok button for the group is called

*/

private Action okAction = new AbstractAction("Ok") {

public void actionPerformed(ActionEvent e) {

okAll();

}

};

/**

* action when cancel button for the group is called

*/

private Action cancelAction = new AbstractAction("Cancel") {

public void actionPerformed(ActionEvent e) {

cancelAll();

}

};

/**

* Get okAction

*

*/

public Action getOkAction() {

return okAction;

}

/**

* Get cancelAction

*

*/

public Action getCancelAction() {

return cancelAction;

}

/**

* Ok for all components in the group

*/

private void okAll() {

//set value and state for all components

}

/**

* Cancels all components in the group

*/

private void cancelAll() {

//reset to initial state for all components

}

Am I totally wrong, or am I getting on the right track? Now how do I paint the ok and cancel buttons? How do I put it all together?

Really appreciate your help!!

Miss_Ma at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 7

As far as I can see you don't need special classes for OK and Cancel buttons. JButton will do it.

If you just do:

JButton okButton = new JButton(ocgroup.getOkAction());

That will generate a suitable button which will trigger the action when clicked.

I'd use ArrayList, not Vector which is a bit obsolete.

Otherwise I think you're heading the right way.

malcolmmca at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...
# 8

My StandardDialog class is either along the lines of what you want, or exactly what you want.

StandardDialog is an abstract class where you have to fill in 2 methods: apply() and showApplyButton(). Ok button calls apply() close, apply button just calls apply(), and cancel button just calls close.

Is this what you're after? If so, you are welcome to have and to modify the code, but please do not change the package or take credit for it as your own work.

==================================================

StandardDialog.java

package tjacobs.ui;

import java.awt.Dialog;

import java.awt.Frame;

import java.awt.GraphicsConfiguration;

import java.awt.HeadlessException;

import java.awt.*;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.*;

import javax.swing.JDialog;

public abstract class StandardDialog extends JDialog {

public StandardDialog() throws HeadlessException {

super();

_init();

}

public StandardDialog(Dialog owner) throws HeadlessException {

super(owner);

_init();

}

public StandardDialog(Dialog owner, boolean modal) throws HeadlessException {

super(owner, modal);

_init();

}

public StandardDialog(Frame owner) throws HeadlessException {

super(owner);

_init();

}

public StandardDialog(Frame owner, boolean modal) throws HeadlessException {

super(owner, modal);

_init();

}

public StandardDialog(Dialog owner, String title) throws HeadlessException {

super(owner, title);

_init();

}

public StandardDialog(Dialog owner, String title, boolean modal)

throws HeadlessException {

super(owner, title, modal);

_init();

}

public StandardDialog(Frame owner, String title) throws HeadlessException {

super(owner, title);

_init();

}

public StandardDialog(Frame owner, String title, boolean modal)

throws HeadlessException {

super(owner, title, modal);

_init();

}

public StandardDialog(Dialog owner, String title, boolean modal,

GraphicsConfiguration gc) throws HeadlessException {

super(owner, title, modal, gc);

_init();

}

public StandardDialog(Frame owner, String title, boolean modal,

GraphicsConfiguration gc) {

super(owner, title, modal, gc);

_init();

}

public void setMainContent(Component c) {

mContent.add(c, BorderLayout.CENTER);

mMainPane = c;

}

protected void _init() {

mContent = getContentPane();

mContent.setLayout(new BorderLayout());

mButtonPane = new JPanel(new FlowLayout(FlowLayout.RIGHT));

mContent.add(mButtonPane, BorderLayout.SOUTH);

mButtonListener = new ButtonListener();

if (showApplyButton()) {

mApply = new JButton("Apply");

mButtonPane.add(mApply);

mApply.addActionListener(mButtonListener);

}

mOk = new JButton("OK");

mButtonPane.add(mOk);

mOk.addActionListener(mButtonListener);

mCancel = new JButton("Cancel");

mButtonPane.add(mCancel);

mCancel.addActionListener(mButtonListener);

//mMainPane = new JPanel();

//mContent.add(mMainPane, BorderLayout.CENTER);

}

public Component getMainPane() {

return mMainPane;

}

//public void setMainPane(Component c) {

//mMainPane = c;

//}

protected void ok (){

mCancelState = OK;

apply();

close();

}

protected void cancel() {

mCancelState = CANCEL;

close();

}

protected void close () {

dispose();

}

public abstract boolean showApplyButton();

public abstract void apply();

public void dispose() {

if (mCancelState != OK) {

mCancelState = CANCEL;

}

synchronized(this) {

notifyAll();

}

super.dispose();

}

protected class ButtonListener implements ActionListener {

public void actionPerformed (ActionEvent ae) {

JButton b = (JButton) ae.getSource();

if (b == mApply) {

apply();

}

else if (b == mOk) {

ok();

}

else if (b == mCancel) {

cancel();

}

}

}

public int getCancelState() {

return mCancelState;

}

public static int showStandardDialog(StandardDialog sd) {

Window w = sd.getOwner();

if (w == null) {

Dimension d = Toolkit.getDefaultToolkit().getScreenSize();

Dimension ps = sd.getPreferredSize();

return showStandardDialog(sd, (d.width - ps.width) / 2, (d.height - ps.height) / 2);

}

else {

Point p = w.getLocation();

Dimension d = w.getSize();

return showStandardDialog(sd, p.x + 100, p.y + 20);

}

}

public static int showStandardDialog(StandardDialog sd, int x, int y) {

sd.setLocation(x, y);

sd.pack();

sd.setVisible(true);

return sd.getCancelState();

}

public static final int OK = 1;

public static final int SHOWN = 0;

public static final int CANCEL = -1;

private int mCancelState = 0;

protected boolean mBlockInCode;

protected Container mContent;

protected Component mMainPane;

protected JButton mApply;

protected JButton mOk;

protected JButton mCancel;

protected JPanel mButtonPane;

protected ActionListener mButtonListener;

}

tjacobs01a at 2007-7-15 23:22:11 > top of Java-index,Desktop,Core GUI APIs...