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