0

I wanted to make my application nicely, without 100 inner classes and stuff. I have class that holds some lists with data. I have menu, with items that use AbstractActions, eg. I wanted to have and action in there to delete selected item from table. For that I need references for both table and table model. I want to add this action to menu item, I would need to pass there references on table and table model that are created later as I do like this:

    MainMenuBar menuBar = new MainMenuBar(db);
    MainTabbedPane tabbedPane = new MainTabbedPane(db);

    this.setLayout(new BorderLayout());
    add(menuBar, BorderLayout.PAGE_START);
    add(tabbedPane, BorderLayout.CENTER);

where tabbedPane has 2 tabs with 2 tables. So any help how to do this in a nice way?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 1) Your menu bar shouldn't be placed in content pane. Please see [Why JMenuBar is not place in the JFrame content pane, but JToolbar place in the content pane?](http://stackoverflow.com/questions/21660699/why-jmenubar-is-not-place-in-the-jframe-content-pane-but-jtoolbar-place-in-the/21661032#21661032). 2) *"For that I need references for both table and table model.*" If you have a reference to your [JTable](http://docs.oracle.com/javase/8/docs/api/javax/swing/JTable.html), then call [JTable#getModel()](http://docs.oracle.com/javase/8/docs/api/javax/swing/JTable.html#getModel--) – dic19 Jul 27 '14 at 22:10
  • 1
    Consider having a look at the [How to use `Action`s](http://docs.oracle.com/javase/tutorial/uiswing/misc/action.html) as it will allow you to design self contained units of work which can be applied to menus, buttons and key bindings, saving a lot of repeated work. You could even abstract the `Action` in such away as to generate a series of "table" actions – MadProgrammer Jul 27 '14 at 22:13
  • dic19 well, thank you, I used setJMenuBar(menuBar), getModel(), right I forgot. @MadProgrammer I don't quite get how to fix my problem. I have this class MainMenuBar that makes things like: menuItem = new JMenuItem(new RemovePurchaseAction(...)) and then I have MainTabbedPane that creates PurchasesPanel that creates PurchasesTable that I need to access from that menu item. I wanted to have the table in separated class, not all in one, this action has to do model.fireTableDataChanged() and it needs at least reference for this table that is created deep in those panels. – user3758262 Jul 27 '14 at 22:44
  • 2
    `this action has to do model.fireTableDataChanged()` no it doesn't. It is the responsibility of the TableMode to invoke that method, not your application code. `I wanted to have and action in there to delete selected item from table. For that I need references for both table and table model.` you only need access to the TableModel, not the JTable for the reason given above. – camickr Jul 27 '14 at 22:59
  • thats right, I noticed that MadProgrammer shown how to do this properly – user3758262 Jul 27 '14 at 23:14

1 Answers1

2

It would be nice if JTable supported generics, it would make life much easier, but it doesn't so we don't have much choice.

One solution would be to take advantage of the Actions API, which would allow you to define a series of self contained "actions" which can be applied to menus, buttons and key bindings equally.

For example...

public abstract class AbstractTableAction<M extends TableModel> extends AbstractAction {
    private JTable table;
    private M model;

    public AbstractTableAction(JTable table, M model) {
        this.table = table;
        this.model = model;
    }

    public JTable getTable() {
        return table;
    }

    public M getModel() {
        return model;
    }
}

Then you can define more focused actions...

public class DeleteRowAction extends AbstractTableAction<MutableTableModel> {
    public DeleteRowAction (JTable table, MutableTableModel model) {
        super(table, model);
        putValue(NAME, "Delete selected row(s)");
    }

    public void actionPerformed(ActionEvent evt) {
        JTable table = getTable();
        int rows[] = table.getSelectedRows();
        for (int index = 0; index < rows.length; index++) {
            rows[index] = table.convertRowIndexToModel(rows[index]);
        }
        getModel().removeRows(rows);
    }
}

Now, obviously, MutableTableModel is just example, but is a particular implementation of TableModel that provides the functionality that you need.

This approach would allow you to apply these actions to JMenuItem, JButton and key bindings, meaning you could, for example, assign the Action to the Delete, so that when pressed when a table has focus, the Action would be triggered

You could further abstract the concept by defining some kind of controller which provided access to the current table/model, so you would only need to create a single series of Actions, which took the "controller" as a reference. The controller then would provide context to the current state of the view/program (that is, which table/model was currently active) for example...

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • as I answer above when I make gui the table is deep created deep in other clases, and menu bar is on top if I even had this controller I would need to do like: menuItem = new JMenuItem(new RemoveAction(tabbedPane.getPurchasePanel().getPurchasesTable())) or even worse. I feel like there should be a way to avoid it... – user3758262 Jul 27 '14 at 22:51
  • 1+ to this great answer. @user3758262, if you want to decrease coupling, consider using MadProgrammer's idea together with dependency injection such as can be obtained via Spring or Guice. – Hovercraft Full Of Eels Jul 27 '14 at 22:56
  • thank you guys. So there is no easy way to avoid these getters in my case? As I am not familiar with dependency injection at all... – user3758262 Jul 27 '14 at 23:07
  • You will also want to separate concerns as much as possible a la MVC or something similar, use interfaces and observer type pattern to listen for changes to your model. – Hovercraft Full Of Eels Jul 27 '14 at 23:08
  • @HovercraftFullOfEels can you get me any piece of code to help me understand, cause I don't quite get it. – user3758262 Jul 27 '14 at 23:17
  • @user3758262: you're asking for a piece of code that will explain concepts that will require several book chapters. I wish I had to the time and energy to do this, but regretfully, life, family, work, kids have priorities. You will want to continue studying your Java, reading on design patterns, and on some of the topics touched on here. – Hovercraft Full Of Eels Jul 27 '14 at 23:19
  • You ask for a solution to 'A', I've given you 'B' but you say you can't make it fit with your code 'C'...is the problem with 'B' or 'C'? You could reverse the idea, instead of the menu trying to control everything, provide the tables with the ability to control the menu (ie context sensitive control), so that when a table gains focus, it can add/enable content on the menu and when it loses focus, it can remove/disable content on the menu. – MadProgrammer Jul 28 '14 at 00:10
  • Also have a look at [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) for some more details – MadProgrammer Jul 28 '14 at 00:12
  • well I probably don't understand something. You shown me the Actions that I wanted to use in the first place. So I will have actions that still need reference for the table. I wanted to avoid getting this reference deep from other classes to pass it to menu item, that is all. I figured in the night that I could make my tables singletons and get their instance anywhere but I feel like it's an ugly idea too. – user3758262 Jul 28 '14 at 08:30
  • No, I wouldn't make them singletons, I would make a "menu manager" a singleton instead and then a just the menu accordingly when the focus state of the table changes... – MadProgrammer Jul 28 '14 at 09:15