3

How would I call an Action from another class in Java? I got a CloseTabButton class online that allows a simple close tab button on each JTabbedPane, but when the tab is closed, I would like a dialog to pop up based on information (if file is not saved, ask to save it, etc.). This is the file:

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

class CloseTabButton extends JPanel implements ActionListener {
    private JTabbedPane pane;
    public CloseTabButton(JTabbedPane pane, int index) {
        this.pane = pane;
        setOpaque(false);

        // CloseIcon class just had a button with an x painted on it
        Icon closeIcon = new CloseIcon();
        JButton close = new JButton(closeIcon);

        close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
        close.addActionListener(this);

        add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
        add(close);

        pane.setTabComponentAt(index, this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int i = pane.indexOfTabComponent(this);

        String fileName = pane.getToolTipTextAt(i);

        // Where I want to ask if user wants to save, etc.
        if (fileName == "Untitled") {
            // Do stuff
        }

        pane.remove(i); // Removes the tab

        // If tab count < 1, then disable the save and save as buttons on menu
        if (pane.getTabCount() < 1) {
            JFrame frame = (JFrame) pane.getParent().getParent().getParent().getParent().getParent(); // Yes, there is that many in my code to get the parent JFrame

            int menuCount = frame.getJMenuBar().getMenuCount();

            for (int a = 0; a < menuCount; a++) {
                int itemCount = frame.getJMenuBar().getMenu(a).getItemCount();

                for (int b = 0; b < itemCount; b++) {
                    Component component = frame.getJMenuBar().getMenu(a).getMenuComponent(b);

                    if (!(component instanceof JSeparator)) {
                        // Not a seperator
                        String itemName = frame.getJMenuBar().getMenu(a).getItem(b).getAccessibleContext().getAccessibleName();
                        if (itemName == "Save As..") {
                            frame.getJMenuBar().getMenu(a).getItem(b).setEnabled(false);
                        }
                    }
                }
            }
        }
    }
}

In my main class I have actions listed like this:

static Action Close = new AbstractAction("Close") {
    public void actionPerformed(ActionEvent e) {
        closeCurrentWindow(); // function that will close tab
    }
}

The other menu items are Actions as well, and as you can see, what I'm currently doing in the CloseTabButton class is quite frustrating, and most likely the wrong way to code it. Is there a much simpler way to do what I'm doing?

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Vince
  • 2,596
  • 11
  • 43
  • 76

1 Answers1

0

The first thing I might do is provide ActionListener support to the CloseTabButton, for example...

public class CloseTabButton extends JPanel {

    private JTabbedPane pane;

    public CloseTabButton(JTabbedPane pane, int index) {
        this.pane = pane;
        setOpaque(false);

        // CloseIcon class just had a button with an x painted on it
        Icon closeIcon = new CloseIcon();
        JButton close = new JButton(closeIcon);

        close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
        close.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                fireActionPerformed();
            }

        });

        add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
        add(close);

        pane.setTabComponentAt(index, this);
    }

    public void addActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }

    public void removeActionListener(ActionListener listener) {
        listenerList.remove(ActionListener.class, listener);
    }

    protected void fireActionPerformed() {
        ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
        ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "You could provide you own action command for each tab here");
        for (ActionListener listener : listeners) {
            listener.actionPerformed(evt);
        }
    }

}

Basically, this now allows you to register your own ActionListeners to the CloseTabButton

Next, this, fileName == "Untitled", is not how you compare Strings in Java, you should be using something more like "Untitled".equals(fileName)

If you're menus are based on actual Actions, then you can simply disable the Actions themselves. This would require a little bit of work, but a lot less of "guess" work then you're doing now.

Basically, you would monitor the JTabbedPane itself, monitoring for changes to the selected tab and updating the states of the individual Actions themselves

There a number of ways you could do this, like passing a reference of the JTabbedPane to the Actions so they can perform there own monitoring (but I'd use some kind of management interface which could more easily provide information to the Actions and decouple the code and the reliance on JTabbedPane directly, then you could be free to use JInternalFrames instead).

You could have a "menu manager" which did a similar job, monitoring changes to the document container and changing the state of the menu Actions based on the current state, as an example

Updated

If you're making use of the Action API (which I would recommend), then you could simply do something like...

public class CloseTabButton extends JPanel {

    private JTabbedPane pane;

    public CloseTabButton(JTabbedPane pane, Action action, int index) {
        this.pane = pane;
        setOpaque(false);

        // CloseIcon class just had a button with an x painted on it
        Icon closeIcon = new CloseIcon();
        JButton close = new JButton(action);
        close.setIcon(closeIcon);
        close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));

        add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
        add(close);

        pane.setTabComponentAt(index, this);
    }

}

Passing in the Action for the close operation, then use the same action for both the JMenuItem and JTabbedPane.

The "core" issue would be how you would identify the "current" tab and document in a uniform manner

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Even if theres a way to "trigger" the menu command as if the menuItem was clicked. I'm trying not to change my code too much haha. – Vince Jul 31 '15 at 02:44
  • Sorry, I don't understand – MadProgrammer Jul 31 '15 at 02:45
  • I can get access to the JMenuBar. But then there could possibly be a way to trigger a menu click event, such as... frame.getJMenuBar.getMenu(a).getItem(b).click(); (I know in Java that's not correct, but in Javascript it can be done xD) – Vince Jul 31 '15 at 02:49
  • `doClick` is what you're after, having said that though, I advise against, hang on a sec – MadProgrammer Jul 31 '15 at 03:10
  • Why would you go against doClick()? – Vince Aug 02 '15 at 18:58
  • @vince, because, frankly, using a shared Action is simpler – MadProgrammer Aug 02 '15 at 21:58
  • Ah, okay. But nothing wrong with using it though, right? I mean, once I get my project done and I start cleaning up my code, I'll probably used shared Actions, but just to get it working for now, it should be fine, right? – Vince Aug 03 '15 at 05:41
  • So long as you have the `ActionListener`s set up properly. The way you're digging around the menu's is really a good idea either, it assumes way to much about the state of the application – MadProgrammer Aug 03 '15 at 05:42