-1

I have a Main class that stores a TabbedComponent(extending JTabbedPane) as a variable. Another class (ToolbarComponent(extending JMenuBar) is also stored as a variable within my main class.

Upon a user event on the Toolbar, it calls the parent class (main), to get the TabbedComponent object and call a method to create a new tab. Which all works fine.

My issue is that when I attempt to click on a ta with my mouse, nothing changes. I'm pretty sure that I don't need a listener on MouseAdapter for something that simple, but will add it if I need it I guess.

Below is are stripped down versions of classes relevant to this issue

public class ExampleClass extends JFrame {

private TabbedBrowserPaneComponent cTabbedBrowserPane;

public ExampleClass() {
    super("");

    // Set up Components
    this.cTabbedBrowserPane = new TabbedBrowserPaneComponent(this);

    // Set up behaviour
    setSize(500, 300);
    setVisible(true);
}

/**
 * @return the cTabbedBrowserPane
 */
public TabbedBrowserPaneComponent getTabbedBrowserPane() {
    return cTabbedBrowserPane;
}


/**
 * @param cTabbedBrowserPane the cTabbedBrowserPane to set
 */
public void setTabbedBrowserPane(TabbedBrowserPaneComponent cTabbedBrowserPane) {
    this.cTabbedBrowserPane = cTabbedBrowserPane;
}

}

public class TabbedBrowserPaneComponent extends JTabbedPane {

// Parent class of the component
private JFrame parent = null;

public TabbedBrowserPaneComponent(JFrame parent) {
    super();
    setParent(parent);

    // Add an initial pane
    createNewTab();
    parent.getContentPane().add(this);
}

public void createNewTab() {
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(new JScrollPane(), BorderLayout.CENTER);
    this.addTab("Tab " + this.getTabCount(), panel);
}

/**
 * @return the parent
 */
public JFrame getParent() {
    return parent;
}

/**
 * @param parent the parent to set
 */
public void setParent(JFrame parent) {
    this.parent = parent;
}
}

To create a new tab, ToolBarComponent's listener calls like this

public class CreateNewTabAction extends AbstractAction {

// Parent
private JMenu parent;

public CreateNewTabAction(JMenu parent) {
    super();
    this.setParent(parent);

    // Values for the tab
    putValue(Action.NAME, "New Tab");
}

@Override
public void actionPerformed(ActionEvent e) {
    ExampleClass.class.cast((parent.getParent().getParent())).getTabbedBrowserPane().createNewTab();
}

/**
 * @return the parent
 */
public JMenu getParent() {
    return parent;
}

/**
 * @param parent the parent to set
 */
public void setParent(JMenu parent) {
    this.parent = parent;
}
}

It this something really simply that I am missing?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
DominicEU
  • 3,585
  • 2
  • 21
  • 32
  • 1
    I think what you may be missing is an [sscce](http://sscce.org) otherwise we may not have enough pieces of the puzzle to be able to help you. – Hovercraft Full Of Eels Dec 14 '12 at 02:43
  • Also have you done any debugging in a debugger or with println statements? And how do you know that the code that you are showing us is the cause of your problems and is sufficient for us to be able to help you? – Hovercraft Full Of Eels Dec 14 '12 at 02:47
  • Added more code to try and help you guys help me. I'm pretty certain that it's an issue with the above code somehow. I've disabled other components to try and focus solely on this one. – DominicEU Dec 14 '12 at 02:48
  • Additionally, I have done some debugging. For instance, I know (and tested with output) that using keys I can individually change tabs (left and right buttons) by setting up a listener. It is just my mouse that is not working. – DominicEU Dec 14 '12 at 02:49
  • 1
    *"Added more code.."* Post an *[SSCCE](http://sscce.org/)* as opposed to 'more code'. – Andrew Thompson Dec 14 '12 at 02:57
  • That code is as SSCCE as possible. it's short (as it only contains the code that I feel is application to this issue), it's correct And i've given you an example of it works and doesn't. For instance I can change tabs using my keyboard, but not my mouse. I don't know why. – DominicEU Dec 14 '12 at 03:09
  • 2
    Since your code above cannot run for me, I cannot help you. If you create a true SSCCE, I am quite sure that I will be able to help you better, so if you don't get a decent answer soon, consider creating a compliant SSCCE if you are desiring a helpful answer quickly. – Hovercraft Full Of Eels Dec 14 '12 at 03:13
  • You'd think that if he were getting an exception, he'd show it to us, right? At least I'd hope he would! – Hovercraft Full Of Eels Dec 14 '12 at 03:18
  • 1
    @DominicGunn: You might compare your code to this working [example](http://stackoverflow.com/a/11949899/230513). – trashgod Dec 14 '12 at 03:19
  • *"That code is as SSCCE as possible"* It is black or white. It is either 'an SSCCE' or 'not an SSCCE'. That which is posted is ***not an SSCCE***. – Andrew Thompson Dec 14 '12 at 03:24
  • @HovercraftFullOfEels The exception I got could just be the way I laied out his example, but it demonstrates part of the lack of proper design – MadProgrammer Dec 14 '12 at 03:26

1 Answers1

3

Your code demonstrates a significant lack of design, sorry (I'm not trying to be mean, but I've spent the better part of 3 years undoing this kind of behavior so it gives me a nasty twitch).

Your problem is you are overriding getParent, which is method of Component used to determine where the component is actually added to. This is causing issues for the internal workings of the system.

There is no need to supply the parent frame to the tab component. If you REALLY need to get access back to the parent frame for some reason, consider using SwingUtilities.getWindowAncestor. If you are just planing to supply functionality for the tab in the frame, create a interface that can establish the contract between the tab and the controller/engine.

Don't get me started on the Action ...

An Example

I'm not sure what is you're actually trying to achieve, but there is absolutely no need to pass around a reference to the browser tab or the main frame. The elements of your program your passing them to just don't need to know that much information about their parents in order to achieve there work, also, you are significantly limiting the flexibility and re-usability of your components.

Below, I use a simple interface\controller that provides a contract between the various views and controls within the example. None of the various elements really need to know much more...

public class ExampleClass {

    public static void main(String[] args) {
        new ExampleClass();
    }

    public ExampleClass() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                BrowserPane browserPane = new BrowserPane();
                CreateNewTabAction createNewTabAction = new CreateNewTabAction(browserPane);

                JMenu mnu = new JMenu("Stuff");
                mnu.add(createNewTabAction);

                JMenuBar mb = new JMenuBar();
                mb.add(mnu);

                JToolBar tb = new JToolBar();
                tb.add(createNewTabAction);

                JFrame frame = new JFrame();
                frame.setJMenuBar(mb);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(browserPane);
                frame.add(tb, BorderLayout.NORTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface TabController {

        public void createNewTab();
    }

    public class BrowserPane extends JPanel implements TabController {

        private TabbedBrowserPaneComponent cTabbedBrowserPane;

        public BrowserPane() {
            setLayout(new BorderLayout());
            // Set up Components
            this.cTabbedBrowserPane = new TabbedBrowserPaneComponent();
            add(cTabbedBrowserPane);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        public void createNewTab() {

            cTabbedBrowserPane.createNewTab();

        }
    }

    public class TabbedBrowserPaneComponent extends JTabbedPane {

        public TabbedBrowserPaneComponent() {
            super();
            createNewTab();
        }

        public void createNewTab() {
            JPanel panel = new JPanel(new BorderLayout());
            panel.add(new JScrollPane(), BorderLayout.CENTER);
            this.addTab("Tab " + this.getTabCount(), panel);
        }
    }

    public class CreateNewTabAction extends AbstractAction {

        private TabController controller;

        public CreateNewTabAction(TabController controller) {
            super();
            this.controller = controller;
            putValue(Action.NAME, "New Tab");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            getController().createNewTab();
        }

        /**
         * @return the parent
         */
        public TabController getController() {
            return controller;
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    *"Don't get me started on the `Action` ..."* I've been musing over the fragility of this classic - `ExampleClass.class.cast((parent.getParent().getParent())).getTabbedBrowserPane().createNewTab();` – Andrew Thompson Dec 14 '12 at 03:26
  • @AndrewThompson Well, it blew up in my face :P – MadProgrammer Dec 14 '12 at 03:27
  • i tried using a singleton instance of Main, but it was causing insane errors. It's the only way I could even get the tabs to show. – DominicEU Dec 14 '12 at 03:56
  • @DominicGunn: But it is borked beyond belief and is a kludge that is masking a deeper problem. – Hovercraft Full Of Eels Dec 14 '12 at 03:59
  • The thing is. Even calling MainClass.getInstance().getContentPane().add(new JTabbedPane()); without anything else causes a StackOverflow. I don't know why? – DominicEU Dec 14 '12 at 04:01
  • @DominicGunn: and not to belabor a point, but with an sscce, we'd be able to tell you why! – Hovercraft Full Of Eels Dec 14 '12 at 04:09
  • @DominicGunn I've added a rewrite to demonstrate a `interface` controller that acts as the main interaction point between the view and the controls – MadProgrammer Dec 14 '12 at 04:10
  • 2
    @DominicGunn Never assume component hierarchy in this way. You don't know if I've moved the component or not. You simply locking your self down, making your application inflexible and reducing it's potential re-use of it's components in the future – MadProgrammer Dec 14 '12 at 04:12
  • Thanks @MadProgrammer. That helped alot, appreciate the advice too. – DominicEU Dec 14 '12 at 04:14
  • and thanks mad for creating and posting an sscce. @DominicGunn, please note that his code is compilable, runnable, and demonstrates his point. Please consider creating one of these if you have future questions in the forum, so that he doesn't have to do this for you. The onus of effort should be yours. – Hovercraft Full Of Eels Dec 14 '12 at 04:19