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        fileMenu.add(new jmri.configurexml.StoreMenu());
256
257        JMenuItem printItem = new JMenuItem(Bundle.getMessage("PrintTable"));
258        fileMenu.add(printItem);
259        printItem.addActionListener((ActionEvent e) -> {
260            try {
261                // MessageFormat headerFormat = new MessageFormat(getTitle());  // not used below
262                MessageFormat footerFormat = new MessageFormat(getTitle() + " page {0,number}");
263                if (item.getStandardTableModel()) {
264                    item.getDataTable().print(JTable.PrintMode.FIT_WIDTH, null, footerFormat);
265                } else {
266                    item.getAAClass().print(JTable.PrintMode.FIT_WIDTH, null, footerFormat);
267                }
268            } catch (java.awt.print.PrinterException e1) {
269                log.warn("Printing error", e1);
270            } catch (NullPointerException ex) {
271                log.error("Trying to print returned a NPE error");
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    TabbedTableItem<E> lastSelectedItem = null;
294
295    /* This is a bit of a bodge to add the contents to the bottom box and keep
296     * it backwardly compatible with the original views. When the original views
297     * are deprecated then this can be re-written
298     */
299    //@TODO Sort out the procedure to add to bottom box
300    @Override
301    protected void addToBottomBox(Component comp, String c) {
302        for (TabbedTableItem<E> tti : tabbedTableArray) {
303            if (tti.getClassAsString().equals(c)) {
304                tti.addToBottomBox(comp);
305                return;
306            }
307        }
308    }
309
310    protected static ArrayList<String> getChoices() {
311        ArrayList<String> choices = new ArrayList<>();
312        for (TabbedTableItemListArray ttila : tabbedTableItemListArrayArray) {
313            choices.add(ttila.getItemString());
314        }
315        return choices;
316    }
317
318    public void setDividerLocation(int loc) {
319        if (loc == 0) {
320            return;
321        }
322        cardHolder.setDividerLocation(loc);
323        InstanceManager.getDefault(UserPreferencesManager.class)
324                .setProperty(ListedTableFrame.class.getName(), "dividerLocation", loc);
325    }
326
327    public int getDividerLocation() {
328        try {
329            return Integer.parseInt(InstanceManager.getDefault(UserPreferencesManager.class)
330                    .getProperty(ListedTableFrame.class.getName(), "dividerLocation").toString());
331        } catch (NullPointerException | NumberFormatException ex) {
332            // ignore, this means the divider location has never been saved
333            return 0;
334        }
335    }
336
337    /**
338     * Flag Table initialisation started
339     * @param newVal true when started
340     */
341    private synchronized static void setInit(boolean newVal) {
342        init = newVal;
343    }
344
345    /**
346     * One tabbed item on the ListedTable containing the table(s) for a NamedBean class.
347     *
348     * @param <E> main class of the table(s)
349     */
350    public static class TabbedTableItem<E extends NamedBean> {
351
352        AbstractTableAction<E> tableAction;
353        String className;
354        String itemText;
355        BeanTableDataModel<E> dataModel;
356        JTable dataTable;
357        JScrollPane dataScroll;
358        Box bottomBox;
359        int bottomBoxIndex; // index to insert extra stuff
360        static final int bottomStrutWidth = 20;
361
362        boolean standardModel;
363
364        final JPanel dataPanel = new JPanel();
365
366        @SuppressWarnings("unchecked") // type ensured by reflection
367        TabbedTableItem(String aaClass, String choice, boolean stdModel) {
368            className = aaClass;
369            itemText = choice;
370            standardModel = stdModel;
371
372            bottomBox = Box.createHorizontalBox();
373            bottomBox.add(Box.createHorizontalGlue());
374            bottomBoxIndex = 0;
375
376            try {
377                Class<?> cl = Class.forName(aaClass);
378                java.lang.reflect.Constructor<?> co = cl.getConstructor(String.class);
379                tableAction = (AbstractTableAction<E>) co.newInstance(choice);  // this cast is handled by reflection
380            } catch (ClassNotFoundException | InstantiationException e1) {
381                log.error("Not a valid class : {}", aaClass);
382                return;
383            } catch (NoSuchMethodException e2) {
384                log.error("Not such method : {}", aaClass);
385                return;
386            } catch (ClassCastException e4) {
387                log.error("Not part of the abstractTableActions : {}", aaClass);
388                return;
389            } catch (IllegalAccessException | IllegalArgumentException | SecurityException | InvocationTargetException e) {
390                log.error("Exception accessing {}: {}", aaClass, e.getMessage());
391                return;
392            }
393
394            // If a panel model is used, it should really add to the bottom box
395            // but it can be done this way if required.
396            // In this case we "hijack" the TabbedTable for different (non-bean) tables to manage OBlocks.
397            dataPanel.setLayout(new BorderLayout());
398
399            if (stdModel) {
400                createDataModel(); // first table of a grouped set with the primary manager, see OBlockTable
401            } else {
402                addPanelModel(); // for any additional table using a different manager, see Audio, OBlock
403            }
404        }
405
406        void createDataModel() {
407            dataModel = tableAction.getTableDataModel();
408            TableRowSorter<BeanTableDataModel<E>> sorter = new TableRowSorter<>(dataModel);
409            dataTable = dataModel.makeJTable(dataModel.getMasterClassName() + ":" + getItemString(), dataModel, sorter);
410            dataScroll = new JScrollPane(dataTable);
411
412            // use NamedBean's built-in Comparator interface for sorting the system name column
413            RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.SYSNAMECOL, SortOrder.ASCENDING);
414
415            sorter.setComparator(BeanTableDataModel.USERNAMECOL, new AlphanumComparator());
416            RowSorterUtil.setSortOrder(sorter, BeanTableDataModel.USERNAMECOL, SortOrder.ASCENDING);
417
418            dataModel.configureTable(dataTable);
419
420            java.awt.Dimension dataTableSize = dataTable.getPreferredSize();
421            // width is fine, but if table is empty, it's not high
422            // enough to reserve much space.
423            dataTableSize.height = Math.max(dataTableSize.height, 400);
424            dataScroll.getViewport().setPreferredSize(dataTableSize);
425
426            // set preferred scrolling options
427            dataScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
428            dataScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
429
430            dataPanel.add(dataScroll, BorderLayout.CENTER);
431
432            dataPanel.add(bottomBox, BorderLayout.SOUTH);
433            if (tableAction.includeAddButton()) {
434                JButton addButton = new JButton(Bundle.getMessage("ButtonAdd"));
435                addToBottomBox(addButton);
436                addButton.addActionListener((ActionEvent e) -> tableAction.addPressed(e));
437            }
438            if (dataModel.getPropertyColumnCount() > 0) {
439                final JCheckBox propertyVisible = new JCheckBox(Bundle.getMessage
440                        ("ShowSystemSpecificProperties"));
441                propertyVisible.setToolTipText(Bundle.getMessage
442                        ("ShowSystemSpecificPropertiesToolTip"));
443                addToBottomBox(propertyVisible);
444                propertyVisible.addActionListener((ActionEvent e) -> dataModel.setPropertyColumnsVisible(dataTable, propertyVisible.isSelected()));
445                dataModel.setPropertyColumnsVisible(dataTable, false);
446            }
447            tableAction.addToFrame(this);
448            dataModel.persistTable(dataTable);
449        }
450
451        void addPanelModel() {
452            try {
453                dataPanel.add(tableAction.getPanel(), BorderLayout.CENTER);
454                dataPanel.add(bottomBox, BorderLayout.SOUTH);
455            } catch (NullPointerException e) {
456                log.error("An error occurred while trying to create the table for {}", itemText, e);
457            }
458        }
459
460        boolean getStandardTableModel() {
461            return standardModel;
462        }
463
464        String getClassAsString() {
465            return className;
466        }
467
468        String getItemString() {
469            return itemText;
470        }
471
472        AbstractTableAction<E> getAAClass() {
473            return tableAction;
474        }
475
476        JPanel getPanel() {
477            return dataPanel;
478        }
479
480        JTable getDataTable() {
481            return dataTable;
482        }
483
484        public void addToBottomBox(Component comp) {
485            bottomBox.add(Box.createHorizontalStrut(bottomStrutWidth), bottomBoxIndex);
486            ++bottomBoxIndex;
487            bottomBox.add(comp, bottomBoxIndex);
488            ++bottomBoxIndex;
489        }
490
491        void dispose() {
492            if (dataModel != null) {
493                dataModel.stopPersistingTable(dataTable);
494                dataModel.dispose();
495            }
496            if (tableAction != null) {
497                tableAction.dispose();
498            }
499            dataModel = null;
500            dataTable = null;
501            dataScroll = null;
502        }
503    }
504
505    static class TabbedTableItemListArray {
506
507        String className;
508        String itemText;
509        boolean standardModel;
510
511        TabbedTableItemListArray(String aaClass, String choice, boolean stdModel) {
512            className = aaClass;
513            itemText = choice;
514            standardModel = stdModel;
515        }
516
517        boolean getStandardTableModel() {
518            return standardModel;
519        }
520
521        String getClassAsString() {
522            return className;
523        }
524
525        String getItemString() {
526            return itemText;
527        }
528
529    }
530
531    /**
532     * ActionJList This deals with handling non-default mouse operations on the
533     * List panel and allows for right click popups and double click to open new
534     * windows of the items we are hovering over.
535     */
536    class ActionJList extends JmriMouseAdapter {
537
538        JPopupMenu popUp;
539        JMenuItem menuItem;
540
541        protected BeanTableFrame<E> frame;
542
543        ActionJList(BeanTableFrame<E> f) {
544            frame = f;
545            popUp = new JPopupMenu();
546            menuItem = new JMenuItem(Bundle.getMessage("MenuOpenInNewWindow"));
547            popUp.add(menuItem);
548            menuItem.addActionListener((ActionEvent e) -> openNewTableWindow(mouseItem));
549            currentItemSelected = 0;
550        }
551
552        private int currentItemSelected;
553
554        @Override
555        public void mousePressed(JmriMouseEvent e) {
556            if (e.isPopupTrigger()) {
557                showPopup(e);
558            }
559        }
560
561        @Override
562        public void mouseReleased(JmriMouseEvent e) {
563            if (e.isPopupTrigger()) {
564                showPopup(e);
565            }
566        }
567
568        // records the original pre-click index
569        private int beforeClickIndex;
570
571        //Records the item index that the mouse is currently over
572        private int mouseItem;
573
574        void showPopup(JmriMouseEvent e) {
575            popUp.show(e.getComponent(), e.getX(), e.getY());
576            mouseItem = list.locationToIndex(e.getPoint());
577        }
578
579        void setCurrentItem(int current) {
580            currentItemSelected = current;
581        }
582
583        @Override
584        public void mouseClicked(JmriMouseEvent e) {
585
586            mouseItem = list.locationToIndex(e.getPoint());
587            if (popUp.isVisible()) {
588                return;
589            }
590            if (e.isPopupTrigger()) {
591                showPopup(e);
592                return;
593            }
594            if (e.getClickCount() == 1) {
595                beforeClickIndex = currentItemSelected;
596                selectListItem(mouseItem);
597            } else if (e.getClickCount() == 2) {
598                list.setSelectedIndex(beforeClickIndex);
599                selectListItem(beforeClickIndex);
600                openNewTableWindow(mouseItem);
601            }
602        }
603
604        void openNewTableWindow(int index) {
605            TabbedTableItem<E> item = tabbedTableArray.get(index);
606            class WindowMaker implements Runnable {
607
608                final TabbedTableItem<E> item;
609
610                WindowMaker(TabbedTableItem<E> tItem) {
611                    item = tItem;
612                }
613
614                @Override
615                public void run() {
616                    ListedTableAction tmp = new ListedTableAction(item.getItemString(), item.getClassAsString(), cardHolder.getDividerLocation());
617                    tmp.actionPerformed();
618                }
619            }
620            WindowMaker t = new WindowMaker(item);
621            javax.swing.SwingUtilities.invokeLater(t);
622        }
623
624        void selectListItem(int index) {
625            currentItemSelected = index;
626            TabbedTableItem<E> item = tabbedTableArray.get(index);
627            CardLayout cl = (CardLayout) (detailPanel.getLayout());
628            cl.show(detailPanel, item.getClassAsString());
629            frame.setTitle(item.getItemString());
630            frame.generateWindowRef();
631            try {
632                item.getAAClass().setFrame(frame);
633                buildMenus(item);
634            } catch (Exception ex) {
635                log.error("Could not build table {}", item, ex);
636            }
637            list.ensureIndexIsVisible(index);
638            list.setSelectedIndex(index);
639        }
640    }
641
642    private final static Logger log = LoggerFactory.getLogger(ListedTableFrame.class);
643
644}