001package jmri.jmrit.beantable;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005import java.beans.PropertyChangeEvent;
006import java.lang.reflect.InvocationTargetException;
007import java.text.MessageFormat;
008import java.util.ArrayList;
009import java.util.Vector;
010
011import javax.swing.Box;
012import javax.swing.BoxLayout;
013import javax.swing.JButton;
014import javax.swing.JCheckBox;
015import javax.swing.JLabel;
016import javax.swing.JList;
017import javax.swing.JMenu;
018import javax.swing.JMenuBar;
019import javax.swing.JMenuItem;
020import javax.swing.JPanel;
021import javax.swing.JPopupMenu;
022import javax.swing.JScrollPane;
023import javax.swing.JSplitPane;
024import javax.swing.JTable;
025import javax.swing.ListSelectionModel;
026import javax.swing.SortOrder;
027import javax.swing.table.TableRowSorter;
028
029import jmri.*;
030import jmri.swing.RowSorterUtil;
031import jmri.util.AlphanumComparator;
032import jmri.util.gui.GuiLafPreferencesManager;
033import jmri.util.swing.JmriMouseAdapter;
034import jmri.util.swing.JmriMouseEvent;
035import jmri.util.swing.JmriMouseListener;
036
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040/**
041 * Provide access to the various tables in the tabbed Tables interface via a listed pane (normally to the left).
042 * <p>
043 * Based upon the {@link apps.gui3.tabbedpreferences.TabbedPreferences} by Bob Jacobsen
044 *
045 * @author Kevin Dickerson Copyright 2010
046 * @author Bob Jacobsen Copyright 2010
047 */
048public class ListedTableFrame<E extends NamedBean> extends BeanTableFrame<E> {
049
050    ActionJList actionList;
051
052    public boolean isMultipleInstances() {
053        return true;
054    }
055
056    static ArrayList<TabbedTableItemListArray> tabbedTableItemListArrayArray = new ArrayList<>();
057    ArrayList<TabbedTableItem<E>> tabbedTableArray = new ArrayList<>();
058
059    final UserPreferencesManager pref = InstanceManager.getDefault(UserPreferencesManager.class);
060    JSplitPane cardHolder;
061    JList<String> list;
062    JScrollPane listScroller;
063    JPanel listPanel;
064    JPanel detailPanel;
065    static boolean init = false;
066
067    /**
068     * Create a new Listed Table Frame.
069     * Call initTables() before initComponents()
070     */
071    public ListedTableFrame() {
072        this(Bundle.getMessage("TitleListedTable"));
073    }
074
075    /**
076     * Create a new Listed Table Frame.
077     * Call initTables() before initComponents()
078     * @param s Initial Frame Title
079     */
080    public ListedTableFrame(String s) {
081        super(s);
082        if (InstanceManager.getNullableDefault(jmri.jmrit.beantable.ListedTableFrame.class) == null) {
083            // We add this to the InstanceManager so that other components can add to the table
084            InstanceManager.store(ListedTableFrame.this, jmri.jmrit.beantable.ListedTableFrame.class);
085        }
086    }
087
088    /**
089     * Initialise all tables to be added to Frame.
090     * Should be called after ListedTableFrame construction and before initComponents()
091     */
092    public void initTables() {
093        if (!init) {
094            // Add the default tables to the static list array,
095            // this should only be done once on first loading
096            addTable("jmri.jmrit.beantable.TurnoutTableTabAction", Bundle.getMessage("MenuItemTurnoutTable"), false);
097            addTable("jmri.jmrit.beantable.SensorTableTabAction", Bundle.getMessage("MenuItemSensorTable"), false);
098            addTable("jmri.jmrit.beantable.LightTableTabAction", Bundle.getMessage("MenuItemLightTable"), false);
099            addTable("jmri.jmrit.beantable.SignalHeadTableAction", Bundle.getMessage("MenuItemSignalTable"), true);
100            addTable("jmri.jmrit.beantable.SignalMastTableAction", Bundle.getMessage("MenuItemSignalMastTable"), true);
101            addTable("jmri.jmrit.beantable.SignalGroupTableAction", Bundle.getMessage("MenuItemSignalGroupTable"), true);
102            addTable("jmri.jmrit.beantable.SignalMastLogicTableAction", Bundle.getMessage("MenuItemSignalMastLogicTable"), true);
103            addTable("jmri.jmrit.beantable.ReporterTableTabAction", Bundle.getMessage("MenuItemReporterTable"), false);
104            addTable("jmri.jmrit.beantable.MemoryTableAction", Bundle.getMessage("MenuItemMemoryTable"), true);
105            addTable("jmri.jmrit.beantable.RouteTableAction", Bundle.getMessage("MenuItemRouteTable"), true);
106            addTable("jmri.jmrit.beantable.LRouteTableAction", Bundle.getMessage("MenuItemLRouteTable"), true);
107            addTable("jmri.jmrit.beantable.LogixTableAction", Bundle.getMessage("MenuItemLogixTable"), true);
108            addTable("jmri.jmrit.beantable.LogixNGTableAction", Bundle.getMessage("MenuItemLogixNGTable"), true);
109            addTable("jmri.jmrit.beantable.LogixNGModuleTableAction", Bundle.getMessage("MenuItemLogixNGModuleTable"), true);
110            addTable("jmri.jmrit.beantable.LogixNGTableTableAction", Bundle.getMessage("MenuItemLogixNGTableTable"), true);
111            addTable("jmri.jmrit.beantable.LogixNGGlobalVariableTableAction", Bundle.getMessage("MenuItemLogixNGGlobalVariableTableAction"), true);
112            addTable("jmri.jmrit.beantable.BlockTableAction", Bundle.getMessage("MenuItemBlockTable"), true);
113            if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed()) { // select _tabbed in prefs
114                addTable("jmri.jmrit.beantable.OBlockTableAction", Bundle.getMessage("MenuItemOBlockTable"), false);
115            } // requires restart after changing the interface setting (on Display tab)
116            addTable("jmri.jmrit.beantable.SectionTableAction", Bundle.getMessage("MenuItemSectionTable"), true);
117            addTable("jmri.jmrit.beantable.TransitTableAction", Bundle.getMessage("MenuItemTransitTable"), true);
118            addTable("jmri.jmrit.beantable.AudioTableAction", Bundle.getMessage("MenuItemAudioTable"), false);
119            addTable("jmri.jmrit.beantable.IdTagTableTabAction", Bundle.getMessage("MenuItemIdTagTable"), false);
120            addTable("jmri.jmrit.beantable.RailComTableAction", Bundle.getMessage("MenuItemRailComTable"), true);
121            ListedTableFrame.setInit(true);
122        }
123    }
124
125    /**
126     * Initialise Frame Components.
127     * Should be called after initTables()
128     * {@inheritDoc}
129     */
130    @Override
131    public void initComponents() {
132        if (tabbedTableItemListArrayArray.isEmpty()) {
133            log.error("No tables loaded: {}",this);
134            return;
135        }
136        actionList = new ActionJList(this);
137
138        detailPanel = new JPanel();
139        detailPanel.setLayout(new CardLayout());
140        tabbedTableArray = new ArrayList<>(tabbedTableItemListArrayArray.size());
141        ArrayList<TabbedTableItemListArray> removeItem = new ArrayList<>(5);
142        for (TabbedTableItemListArray item : tabbedTableItemListArrayArray) {
143            // Here we add all the tables into the panel
144            try {
145                TabbedTableItem<E> itemModel = new TabbedTableItem<>(item.getClassAsString(), item.getItemString(), item.getStandardTableModel());
146                detailPanel.add(itemModel.getPanel(), itemModel.getClassAsString());
147                tabbedTableArray.add(itemModel);
148                itemModel.getAAClass().addToFrame(this);
149            } catch (Exception ex) {
150                detailPanel.add(errorPanel(item.getItemString()), item.getClassAsString());
151                log.error("Error when adding {} to display", item.getClassAsString(), ex);
152                removeItem.add(item);
153            }
154        }
155
156        for (TabbedTableItemListArray dead : removeItem) {
157            tabbedTableItemListArrayArray.remove(dead);
158        }
159
160        list = new JList<>(new Vector<>(getChoices()));
161        listScroller = new JScrollPane(list);
162
163        list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
164        list.setLayoutOrientation(JList.VERTICAL);
165        list.addMouseListener(JmriMouseListener.adapt(actionList));
166
167        listPanel = new JPanel();
168        listPanel.setLayout(new BorderLayout(5, 0));
169        listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
170        listPanel.add(listScroller);
171        listPanel.setMinimumSize(new Dimension(140, 400)); // guarantees minimum width of left divider list
172
173        buildMenus(tabbedTableArray.get(0));
174        setTitle(tabbedTableArray.get(0).getItemString());
175
176        cardHolder = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
177                listPanel, detailPanel);
178
179        cardHolder.setDividerSize(8);
180        if (this.getDividerLocation() != 0) {
181            cardHolder.setDividerLocation(this.getDividerLocation());
182        } else { // if no specific size has been given we set it to the lists preferred width
183            cardHolder.setDividerLocation(listScroller.getPreferredSize().width);
184        }
185        cardHolder.addPropertyChangeListener((PropertyChangeEvent e) -> {
186            if (e.getPropertyName().equals("dividerLocation")) {
187                InstanceManager.getDefault(UserPreferencesManager.class)
188                        .setProperty(ListedTableFrame.class.getName(), "dividerLocation", e.getNewValue());
189            }
190        });
191
192        cardHolder.setOneTouchExpandable(true);
193        getContentPane().add(cardHolder);
194        pack();
195        actionList.selectListItem(0);
196    }
197
198    JPanel errorPanel(String text) {
199        JPanel error = new JPanel();
200        error.add(new JLabel(Bundle.getMessage("ErrorAddingTable", text)));
201        return error;
202    }
203
204    /* Method allows for the table to go to a specific list item */
205    public void gotoListItem(String selection) {
206        for (int x = 0; x < tabbedTableArray.size(); x++) {
207            try {
208                if (tabbedTableArray.get(x).getClassAsString().equals(selection)) {
209                    actionList.selectListItem(x);
210                    return;
211                }
212            } catch (Exception ex) {
213                log.error("An error occurred in the goto list for {}, {}", selection,ex.getMessage());
214            }
215        }
216    }
217
218    public void addTable(String aaClass, String choice, boolean stdModel) {
219        TabbedTableItemListArray itemToAdd = null;
220        for (TabbedTableItemListArray ttila : tabbedTableItemListArrayArray) {
221            if (ttila.getClassAsString().equals(aaClass)) {
222                log.info("Class {} is already added", aaClass);
223                itemToAdd = ttila;
224                break;
225            }
226        }
227        if (itemToAdd == null) {
228            itemToAdd = new TabbedTableItemListArray(aaClass, choice, stdModel);
229            tabbedTableItemListArrayArray.add(itemToAdd);
230        }
231    }
232
233    @Override
234    public void dispose() {
235        pref.setSaveAllowed(false);
236        for (TabbedTableItem<E> tti : tabbedTableArray) {
237            tti.dispose();
238        }
239        if (list != null && list.getListSelectionListeners().length > 0) {
240            list.removeListSelectionListener(list.getListSelectionListeners()[0]);
241        }
242        super.dispose();
243        pref.setSaveAllowed(true);
244    }
245
246    void buildMenus(final TabbedTableItem<E> item) {
247        JMenuBar menuBar = new JMenuBar();
248        JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile"));
249        menuBar.add(fileMenu);
250
251        JMenuItem newItem = new JMenuItem(Bundle.getMessage("MenuNewWindow"));
252        fileMenu.add(newItem);
253        newItem.addActionListener((ActionEvent e) -> actionList.openNewTableWindow(list.getSelectedIndex()));
254
255        // do not display Store All Table Content in IdTag Table 
256        if (!( item.getAAClass() instanceof IdTagTableAction || 
257            item.getAAClass() instanceof IdTagTableTabAction ) ) {
258            fileMenu.add(new jmri.configurexml.StoreMenu());
259        }
260
261        JMenuItem printItem = new JMenuItem(Bundle.getMessage("PrintTable"));
262        fileMenu.add(printItem);
263        printItem.addActionListener((ActionEvent e) -> {
264            try {
265                // MessageFormat headerFormat = new MessageFormat(getTitle());  // not used below
266                MessageFormat footerFormat = new MessageFormat(getTitle() + " page {0,number}");
267                if (item.getStandardTableModel()) {
268                    item.getDataTable().print(JTable.PrintMode.FIT_WIDTH, null, footerFormat);
269                } else {
270                    item.getAAClass().print(JTable.PrintMode.FIT_WIDTH, null, footerFormat);
271                }
272            } catch (java.awt.print.PrinterException e1) {
273                log.warn("Printing error", e1);
274            } catch (NullPointerException ex) {
275                log.error("Trying to print returned a NPE error");
276            }
277        });
278
279        JMenu viewMenu = new JMenu(Bundle.getMessage("MenuView"));
280        menuBar.add(viewMenu);
281        for (final TabbedTableItemListArray itemList : tabbedTableItemListArrayArray) {
282            JMenuItem viewItem = new JMenuItem(itemList.getItemString());
283            viewMenu.add(viewItem);
284            viewItem.addActionListener((ActionEvent e) -> gotoListItem(itemList.getClassAsString()));
285        }
286
287        this.setJMenuBar(menuBar);
288        try {
289            item.getAAClass().setMenuBar(this);
290            this.addHelpMenu(item.getAAClass().helpTarget(), true);
291        } catch (Exception ex) {
292            log.error("Error when trying to set menu bar for {}", item.getClassAsString(), ex);
293        }
294        this.revalidate();
295    }
296
297    TabbedTableItem<E> lastSelectedItem = null;
298
299    /* This is a bit of a bodge to add the contents to the bottom box and keep
300     * it backwardly compatible with the original views. When the original views
301     * are deprecated then this can be re-written
302     */
303    //@TODO Sort out the procedure to add to bottom box
304    @Override
305    protected void addToBottomBox(Component comp, String c) {
306        for (TabbedTableItem<E> tti : tabbedTableArray) {
307            if (tti.getClassAsString().equals(c)) {
308                tti.addToBottomBox(comp);
309                return;
310            }
311        }
312    }
313
314    protected static ArrayList<String> getChoices() {
315        ArrayList<String> choices = new ArrayList<>();
316        for (TabbedTableItemListArray ttila : tabbedTableItemListArrayArray) {
317            choices.add(ttila.getItemString());
318        }
319        return choices;
320    }
321
322    public void setDividerLocation(int loc) {
323        if (loc == 0) {
324            return;
325        }
326        cardHolder.setDividerLocation(loc);
327        InstanceManager.getDefault(UserPreferencesManager.class)
328                .setProperty(ListedTableFrame.class.getName(), "dividerLocation", loc);
329    }
330
331    public int getDividerLocation() {
332        try {
333            return Integer.parseInt(InstanceManager.getDefault(UserPreferencesManager.class)
334                    .getProperty(ListedTableFrame.class.getName(), "dividerLocation").toString());
335        } catch (NullPointerException | NumberFormatException ex) {
336            // ignore, this means the divider location has never been saved
337            return 0;
338        }
339    }
340
341    /**
342     * Flag Table initialisation started
343     * @param newVal true when started
344     */
345    private synchronized static void setInit(boolean newVal) {
346        init = newVal;
347    }
348
349    /**
350     * One tabbed item on the ListedTable containing the table(s) for a NamedBean class.
351     *
352     * @param <E> main class of the table(s)
353     */
354    public static class TabbedTableItem<E extends NamedBean> {
355
356        AbstractTableAction<E> tableAction;
357        String className;
358        String itemText;
359        BeanTableDataModel<E> dataModel;
360        JTable dataTable;
361        JScrollPane dataScroll;
362        Box bottomBox;
363        int bottomBoxIndex; // index to insert extra stuff
364
365        boolean standardModel;
366
367        final JPanel dataPanel = new JPanel();
368
369        @SuppressWarnings("unchecked") // type ensured by reflection
370        TabbedTableItem(String aaClass, String choice, boolean stdModel) {
371            className = aaClass;
372            itemText = choice;
373            standardModel = stdModel;
374
375            bottomBox = Box.createHorizontalBox();
376            bottomBox.add(Box.createHorizontalGlue());
377            bottomBoxIndex = 0;
378
379            try {
380                Class<?> cl = Class.forName(aaClass);
381                java.lang.reflect.Constructor<?> co = cl.getConstructor(String.class);
382                tableAction = (AbstractTableAction<E>) co.newInstance(choice);  // this cast is handled by reflection
383            } catch (ClassNotFoundException | InstantiationException e1) {
384                log.error("Not a valid class : {}", aaClass);
385                return;
386            } catch (NoSuchMethodException e2) {
387                log.error("Not such method : {}", aaClass);
388                return;
389            } catch (ClassCastException e4) {
390                log.error("Not part of the abstractTableActions : {}", aaClass);
391                return;
392            } catch (IllegalAccessException | IllegalArgumentException | SecurityException | InvocationTargetException e) {
393                log.error("Exception accessing {}: {}", aaClass, e.getMessage());
394                return;
395            }
396
397            // If a panel model is used, it should really add to the bottom box
398            // but it can be done this way if required.
399            // In this case we "hijack" the TabbedTable for different (non-bean) tables to manage OBlocks.
400            dataPanel.setLayout(new BorderLayout());
401
402            if (stdModel) {
403                createDataModel(); // first table of a grouped set with the primary manager, see OBlockTable
404            } else {
405                addPanelModel(); // for any additional table using a different manager, see Audio, OBlock
406            }
407        }
408
409        void createDataModel() {
410            dataModel = tableAction.getTableDataModel();
411            TableRowSorter<BeanTableDataModel<E>> sorter = new TableRowSorter<>(dataModel);
412            dataTable = dataModel.makeJTable(dataModel.getMasterClassName() + ":" + getItemString(), dataModel, sorter);
413            dataScroll = new JScrollPane(dataTable);
414
415            // use NamedBean's built-in Comparator interface for sorting the system name column
416            RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.SYSNAMECOL, SortOrder.ASCENDING);
417
418            sorter.setComparator(BeanTableDataModel.USERNAMECOL, new AlphanumComparator());
419            RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.USERNAMECOL, SortOrder.ASCENDING);
420
421            dataModel.configureTable(dataTable);
422
423            java.awt.Dimension dataTableSize = dataTable.getPreferredSize();
424            // width is fine, but if table is empty, it's not high
425            // enough to reserve much space.
426            dataTableSize.height = Math.max(dataTableSize.height, 400);
427            dataScroll.getViewport().setPreferredSize(dataTableSize);
428
429            // set preferred scrolling options
430            dataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
431            dataScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
432
433            dataPanel.add(dataScroll, BorderLayout.CENTER);
434
435            dataPanel.add(bottomBox, BorderLayout.SOUTH);
436            if (tableAction.includeAddButton()) {
437                JButton addButton = new JButton(Bundle.getMessage("ButtonAdd"));
438                addToBottomBox(addButton);
439                addButton.addActionListener((ActionEvent e) -> tableAction.addPressed(e));
440            }
441            if (dataModel.getPropertyColumnCount() > 0) {
442                final JCheckBox propertyVisible = new JCheckBox(Bundle.getMessage
443                        ("ShowSystemSpecificProperties"));
444                propertyVisible.setToolTipText(Bundle.getMessage
445                        ("ShowSystemSpecificPropertiesToolTip"));
446                addToBottomBox(propertyVisible);
447                propertyVisible.addActionListener((ActionEvent e) -> dataModel.setPropertyColumnsVisible(dataTable, propertyVisible.isSelected()));
448                dataModel.setPropertyColumnsVisible(dataTable, false);
449            }
450            tableAction.addToFrame(this);
451            dataModel.persistTable(dataTable);
452        }
453
454        void addPanelModel() {
455            try {
456                dataPanel.add(tableAction.getPanel(), BorderLayout.CENTER);
457                dataPanel.add(bottomBox, BorderLayout.SOUTH);
458            } catch (NullPointerException e) {
459                log.error("An error occurred while trying to create the table for {}", itemText, e);
460            }
461        }
462
463        boolean getStandardTableModel() {
464            return standardModel;
465        }
466
467        String getClassAsString() {
468            return className;
469        }
470
471        String getItemString() {
472            return itemText;
473        }
474
475        AbstractTableAction<E> getAAClass() {
476            return tableAction;
477        }
478
479        JPanel getPanel() {
480            return dataPanel;
481        }
482
483        JTable getDataTable() {
484            return dataTable;
485        }
486
487        public void addToBottomBox(Component comp) {
488            bottomBox.add(Box.createHorizontalStrut(bottomStrutWidth), bottomBoxIndex);
489            ++bottomBoxIndex;
490            bottomBox.add(comp, bottomBoxIndex);
491            ++bottomBoxIndex;
492        }
493
494        void dispose() {
495            if (dataModel != null) {
496                dataModel.stopPersistingTable(dataTable);
497                dataModel.dispose();
498            }
499            if (tableAction != null) {
500                tableAction.dispose();
501            }
502            dataModel = null;
503            dataTable = null;
504            dataScroll = null;
505        }
506    }
507
508    static class TabbedTableItemListArray {
509
510        String className;
511        String itemText;
512        boolean standardModel;
513
514        TabbedTableItemListArray(String aaClass, String choice, boolean stdModel) {
515            className = aaClass;
516            itemText = choice;
517            standardModel = stdModel;
518        }
519
520        boolean getStandardTableModel() {
521            return standardModel;
522        }
523
524        String getClassAsString() {
525            return className;
526        }
527
528        String getItemString() {
529            return itemText;
530        }
531
532    }
533
534    /**
535     * ActionJList This deals with handling non-default mouse operations on the
536     * List panel and allows for right click popups and double click to open new
537     * windows of the items we are hovering over.
538     */
539    class ActionJList extends JmriMouseAdapter {
540
541        JPopupMenu popUp;
542        JMenuItem menuItem;
543
544        protected BeanTableFrame<E> frame;
545
546        ActionJList(BeanTableFrame<E> f) {
547            frame = f;
548            popUp = new JPopupMenu();
549            menuItem = new JMenuItem(Bundle.getMessage("MenuOpenInNewWindow"));
550            popUp.add(menuItem);
551            menuItem.addActionListener((ActionEvent e) -> openNewTableWindow(mouseItem));
552            currentItemSelected = 0;
553        }
554
555        private int currentItemSelected;
556
557        @Override
558        public void mousePressed(JmriMouseEvent e) {
559            if (e.isPopupTrigger()) {
560                showPopup(e);
561            }
562        }
563
564        @Override
565        public void mouseReleased(JmriMouseEvent e) {
566            if (e.isPopupTrigger()) {
567                showPopup(e);
568            }
569        }
570
571        // records the original pre-click index
572        private int beforeClickIndex;
573
574        //Records the item index that the mouse is currently over
575        private int mouseItem;
576
577        void showPopup(JmriMouseEvent e) {
578            popUp.show(e.getComponent(), e.getX(), e.getY());
579            mouseItem = list.locationToIndex(e.getPoint());
580        }
581
582        void setCurrentItem(int current) {
583            currentItemSelected = current;
584        }
585
586        @Override
587        public void mouseClicked(JmriMouseEvent e) {
588
589            mouseItem = list.locationToIndex(e.getPoint());
590            if (popUp.isVisible()) {
591                return;
592            }
593            if (e.isPopupTrigger()) {
594                showPopup(e);
595                return;
596            }
597            if (e.getClickCount() == 1) {
598                beforeClickIndex = currentItemSelected;
599                selectListItem(mouseItem);
600            } else if (e.getClickCount() == 2) {
601                list.setSelectedIndex(beforeClickIndex);
602                selectListItem(beforeClickIndex);
603                openNewTableWindow(mouseItem);
604            }
605        }
606
607        void openNewTableWindow(int index) {
608            TabbedTableItem<E> item = tabbedTableArray.get(index);
609            class WindowMaker implements Runnable {
610
611                final TabbedTableItem<E> item;
612
613                WindowMaker(TabbedTableItem<E> tItem) {
614                    item = tItem;
615                }
616
617                @Override
618                public void run() {
619                    ListedTableAction tmp = new ListedTableAction(item.getItemString(), item.getClassAsString(), cardHolder.getDividerLocation());
620                    tmp.actionPerformed();
621                }
622            }
623            WindowMaker t = new WindowMaker(item);
624            javax.swing.SwingUtilities.invokeLater(t);
625        }
626
627        void selectListItem(int index) {
628            currentItemSelected = index;
629            TabbedTableItem<E> item = tabbedTableArray.get(index);
630            CardLayout cl = (CardLayout) (detailPanel.getLayout());
631            cl.show(detailPanel, item.getClassAsString());
632            frame.setTitle(item.getItemString());
633            frame.generateWindowRef();
634            try {
635                item.getAAClass().setFrame(frame);
636                buildMenus(item);
637            } catch (Exception ex) {
638                log.error("Could not build table {}", item, ex);
639            }
640            list.ensureIndexIsVisible(index);
641            list.setSelectedIndex(index);
642        }
643    }
644
645    private final static Logger log = LoggerFactory.getLogger(ListedTableFrame.class);
646
647}