001package jmri.jmrix.ecos.swing.locodatabase;
002
003import java.awt.Component;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.Hashtable;
007import java.util.List;
008
009import javax.annotation.Nonnull;
010import javax.annotation.CheckForNull;
011import javax.swing.BorderFactory;
012import javax.swing.BoxLayout;
013import javax.swing.DefaultCellEditor;
014import javax.swing.JButton;
015import javax.swing.JCheckBox;
016import javax.swing.JComboBox;
017import javax.swing.JDialog;
018import javax.swing.JLabel;
019import javax.swing.JOptionPane;
020import javax.swing.JPanel;
021import javax.swing.JTable;
022import javax.swing.JTextField;
023import javax.swing.RowSorter;
024import javax.swing.table.TableCellEditor;
025import javax.swing.table.TableCellRenderer;
026import javax.swing.table.TableColumn;
027import javax.swing.table.TableModel;
028import javax.swing.table.TableRowSorter;
029
030import jmri.Manager;
031import jmri.NamedBean;
032import jmri.jmrit.beantable.AbstractTableAction;
033import jmri.jmrit.beantable.BeanTableDataModel;
034import jmri.jmrit.roster.Roster;
035import jmri.jmrit.roster.RosterEntry;
036import jmri.jmrit.roster.swing.GlobalRosterEntryComboBox;
037import jmri.jmrix.ecos.EcosLocoAddress;
038import jmri.jmrix.ecos.EcosLocoAddressManager;
039import jmri.jmrix.ecos.EcosMessage;
040import jmri.jmrix.ecos.EcosSystemConnectionMemo;
041import jmri.jmrix.ecos.utilities.EcosLocoToRoster;
042import jmri.jmrix.ecos.utilities.RemoveObjectFromEcos;
043import jmri.util.swing.JmriMouseEvent;
044import jmri.util.swing.XTableColumnModel;
045
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049public class EcosLocoTableAction extends AbstractTableAction<NamedBean> {
050
051    /**
052     * Create an action with a specific title.
053     * <p>
054     * Note that the argument is the Action title, not the title of the
055     * resulting frame. Perhaps this should be changed?
056     * @param s Action title string.
057     */
058    public EcosLocoTableAction(String s) {
059        super(s);
060    }
061
062    public EcosLocoTableAction() {
063        this(Bundle.getMessage("EcosLocoTableTitle"));
064    }
065
066    public EcosLocoTableAction(String s, EcosSystemConnectionMemo memo) {
067        this(s);
068        setAdapterMemo(memo);
069        includeAddButton = false;
070    }
071
072    @Override
073    public void actionPerformed(ActionEvent e) {
074        // create the JTable model, with changes for specific NamedBean
075        createModel();
076        TableRowSorter<BeanTableDataModel<NamedBean>> sorter = new TableRowSorter<>(m);
077        JTable dataTable = m.makeJTable(getClassName(), m, sorter);
078        // create the frame
079        f = new jmri.jmrit.beantable.BeanTableFrame<NamedBean>(m, helpTarget(), dataTable) {
080        };
081        setMenuBar(f);
082        setTitle();
083        addToFrame(f);
084        f.pack();
085        f.setVisible(true);
086    }
087
088    protected EcosSystemConnectionMemo adaptermemo;
089    protected EcosLocoAddressManager locoManager;
090
091    @Override
092    public void setManager(Manager<NamedBean> man) {
093        if (!(man instanceof EcosLocoAddressManager)) {
094            throw new IllegalArgumentException("Manager is not an EcosLocoAddressManager");
095        }
096        locoManager = (EcosLocoAddressManager) man;
097    }
098    protected String rosterAttribute;
099
100    public void setAdapterMemo(EcosSystemConnectionMemo memo) {
101        adaptermemo = memo;
102        locoManager = adaptermemo.getLocoAddressManager();
103        rosterAttribute = adaptermemo.getPreferenceManager().getRosterAttribute();
104    }
105
106    protected EcosLocoAddress getByEcosObject(String object) {
107        return locoManager.getByEcosObject(object);
108    }
109
110    List<String> ecosObjectIdList = null;
111    JTable table;
112
113    static public final int PROTOCOL = 5;
114    static public final int ADDTOROSTERCOL = 6;
115    static public final int SPEEDDIR = 7;
116    static public final int STOP = 8;
117
118    @Override
119    protected void createModel() {
120        m = new BeanTableDataModel<NamedBean>() { // this is a hack to get this to compile, as EcosLocoAddress is itself not a NamedBean
121
122            //We have to set a manager first off, but this gets replaced.
123            @Override
124            protected EcosLocoAddressManager getManager() {
125                return locoManager;
126            }
127
128            protected String getRosterAttribute() {
129                return rosterAttribute;
130            }
131
132            @Override
133            public String getValue(String s) {
134                return Bundle.getMessage("ButtonSet");
135            }
136
137            @Override
138            protected String getMasterClassName() {
139                return getClassName();
140            }
141
142            @Override
143            public void clickOn(jmri.NamedBean t) {
144            }
145
146            @Override
147            protected synchronized void updateNameList() {
148                // first, remove listeners from the individual objects
149                if (ecosObjectIdList != null) {
150                    for (int i = 0; i < ecosObjectIdList.size(); i++) {
151                        // if object has been deleted, it's not here; ignore it
152                        jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(i));
153                        if (b != null) {
154                            b.removePropertyChangeListener(this);
155                        }
156                    }
157                }
158                ecosObjectIdList = getManager().getEcosObjectList();
159                // and add them back in
160                for (int i = 0; i < ecosObjectIdList.size(); i++) {
161                    getByEcosObject(ecosObjectIdList.get(i)).addPropertyChangeListener(this);
162                }
163            }
164
165            @Override
166            public void propertyChange(java.beans.PropertyChangeEvent e) {
167                updateNameList();
168                if (e.getPropertyName().equals("length")) {
169                    // a new jmri.jmrix.ecos.EcosLocoAddressManager is available in the manager
170                    updateNameList();
171                    fireTableDataChanged();
172                } else if (matchPropertyName(e)) {
173                    // a value changed.  Find it, to avoid complete redraw
174                    String object = ((jmri.jmrix.ecos.EcosLocoAddress) e.getSource()).getEcosObject();
175                    // since we can add columns, the entire row is marked as updated
176                    int row = ecosObjectIdList.indexOf(object);
177                    fireTableRowsUpdated(row, row);
178                }
179            }
180
181            @Override
182            public int getColumnCount() {
183                return STOP + 1;
184            }
185
186            @Override
187            public void setValueAt(Object value, int row, int col) {
188                if (col == COMMENTCOL) {
189                    RosterEntry re;
190                    String ecosObjectNo = ecosObjectIdList.get(row);
191                    if (value == null) {
192                        return;
193                    } else if (value instanceof RosterEntry) {
194                        re = (RosterEntry) value;
195                        if ((re.getAttribute(getRosterAttribute()) != null && !re.getAttribute(getRosterAttribute()).equals(""))) {
196                            JOptionPane.showMessageDialog(f,
197                                    Bundle.getMessage("EcosEditAssignedDialog", ecosObjectNo));
198                            log.error("{} This roster entry already has an ECoS loco assigned to it", ecosObjectNo);
199                            return;
200                        }
201                        String oldRoster = getByEcosObject(ecosObjectNo).getRosterId();
202                        RosterEntry oldre;
203                        if (oldRoster != null) {
204                            oldre = Roster.getDefault().getEntryForId(oldRoster);
205                            if (oldre != null) {
206                                oldre.deleteAttribute(getRosterAttribute());
207                            }
208                        }
209                        re.putAttribute(getRosterAttribute(), ecosObjectNo);
210                        getByEcosObject(ecosObjectNo).setRosterId(re.getId());
211                        re.updateFile();
212                    } else if (value instanceof String) {
213                        List<RosterEntry> r = Roster.getDefault().getEntriesWithAttributeKeyValue(getRosterAttribute(), ecosObjectNo);
214                        if (r.isEmpty()) {
215                            r.get(0).deleteAttribute(getRosterAttribute());
216                            getByEcosObject(ecosObjectNo).setRosterId(null);
217                            r.get(0).updateFile();
218                        }
219
220                    }
221                    Roster.getDefault().writeRoster();
222                } else if (col == ADDTOROSTERCOL) {
223                    addToRoster(row, col);
224                } else if (col == STOP) {
225                    stopLoco(row, col);
226                } else if (col == DELETECOL) {
227                    // button fired, delete Bean
228                    deleteLoco(row, col);
229                } else if (col == USERNAMECOL) {
230                    jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(row));
231                    EcosMessage m = new EcosMessage("request(" + b.getEcosObject() + ", control, force)");
232                    adaptermemo.getTrafficController().sendEcosMessage(m, null);
233                    m = new EcosMessage("set(" + b.getEcosObject() + ", name[\"" + (String) value + "\"])");
234                    adaptermemo.getTrafficController().sendEcosMessage(m, null);
235                    m = new EcosMessage("release(" + b.getEcosObject() + ", control)");
236                    adaptermemo.getTrafficController().sendEcosMessage(m, null);
237                }
238            }
239
240            @Override
241            public JTable makeJTable(@Nonnull String name, @Nonnull TableModel model, @CheckForNull RowSorter<? extends TableModel> sorter) {
242                return this.configureJTable(name, this.makeJTable(model), sorter);
243            }
244
245            private JTable makeJTable(@Nonnull TableModel model) {
246                JTable table = new JTable(model) {
247
248                    @Override
249                    public TableCellRenderer getCellRenderer(int row, int column) {
250                        if (column == COMMENTCOL) {
251                            return getRenderer(row);
252                        } else {
253                            return super.getCellRenderer(row, column);
254                        }
255                    }
256
257                    @Override
258                    public TableCellEditor getCellEditor(int row, int column) {
259                        if (column == COMMENTCOL) {
260                            return getEditor(row);
261                        } else {
262                            return super.getCellEditor(row, column);
263                        }
264                    }
265
266                    TableCellRenderer getRenderer(int row) {
267                        TableCellRenderer retval = rendererMap.get(ecosObjectIdList.get(row));
268                        if (retval == null) {
269                            jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(row));
270                            RosterEntry re = null;
271                            if (b != null) {
272                                re = Roster.getDefault().getEntryForId(b.getRosterId());
273                            }
274                            retval = new RosterBoxRenderer(re);
275                            rendererMap.put(ecosObjectIdList.get(row), retval);
276                        }
277                        return retval;
278                    }
279                    Hashtable<Object, TableCellRenderer> rendererMap = new Hashtable<>();
280
281                    TableCellEditor getEditor(int row) {
282                        TableCellEditor retval = editorMap.get(ecosObjectIdList.get(row));
283                        if (retval == null) {
284                            jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(row));
285                            RosterEntry re = null;
286                            if (b != null) {
287                                re = Roster.getDefault().getEntryForId(b.getRosterId());
288                            }
289                            GlobalRosterEntryComboBox cb = new GlobalRosterEntryComboBox();
290                            cb.setNonSelectedItem(" ");
291                            if (re == null) {
292                                cb.setSelectedIndex(0);
293                            } else {
294                                cb.setSelectedItem(re);
295                            }
296                            // create a new one with right aspects
297                            retval = new RosterComboBoxEditor(cb);
298                            editorMap.put(ecosObjectIdList.get(row), retval);
299                        }
300                        return retval;
301                    }
302                    Hashtable<Object, TableCellEditor> editorMap = new Hashtable<>();
303                };
304                table.getTableHeader().setReorderingAllowed(true);
305                table.setColumnModel(new XTableColumnModel());
306                table.createDefaultColumnsFromModel();
307
308                addMouseListenerToHeader(table);
309                return table;
310            }
311
312            /**
313             * Is this property event announcing a change this table should
314             * display?
315             * <p>
316             * Note that events will come both from the
317             * jmri.jmrix.ecos.EcosLocoAddressManagers and also from the manager
318             */
319            @Override
320            protected boolean matchPropertyName(java.beans.PropertyChangeEvent e) {
321                if (!showLocoMonitor && (e.getPropertyName().equals("Speed") || e.getPropertyName().equals("Direction"))) {
322                    return false;
323                }
324                return true;
325            }
326
327            @Override
328            public String getColumnName(int col) {
329                switch (col) {
330                    case SYSNAMECOL:
331                        return Bundle.getMessage("ObjectIdCol");
332                    case USERNAMECOL:
333                        return Bundle.getMessage("DescriptionCol");
334                    case VALUECOL:
335                        return Bundle.getMessage("AddressCol");
336                    case COMMENTCOL:
337                        return Bundle.getMessage("JmriIdCol");
338                    case DELETECOL:
339                        return ""; // no heading on Delete column
340                    case PROTOCOL:
341                        return Bundle.getMessage("ProtocolCol");
342                    case ADDTOROSTERCOL:
343                        return ""; // no heading on Add to Roster column
344                    case SPEEDDIR:
345                        return Bundle.getMessage("SpeedCol") + " " + Bundle.getMessage("DirectionCol");
346                    case STOP:
347                        return ""; // no heading on Stop column
348                    default:
349                        return "unknown"; // NOI18N
350                }
351            }
352
353            @Override
354            public Class<?> getColumnClass(int col) {
355                switch (col) {
356                    case SYSNAMECOL:
357                    case USERNAMECOL:
358                    case PROTOCOL:
359                    case SPEEDDIR:
360                        return String.class;
361                    case VALUECOL:
362                        return Integer.class;
363                    case ADDTOROSTERCOL:
364                    case DELETECOL:
365                    case STOP:
366                        return JButton.class;
367                    case COMMENTCOL:
368                        return JComboBox.class;
369                    default:
370                        return null;
371                }
372            }
373
374            @Override
375            public boolean isCellEditable(int row, int col) {
376                switch (col) {
377                    case COMMENTCOL:
378                        return true;
379                    case ADDTOROSTERCOL:
380                        jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(row));
381                        if (b.getRosterId() == null || b.getRosterId().equals("")) {
382                            return true;
383                        } else {
384                            return false;
385                        }
386                    case USERNAMECOL:
387                    case DELETECOL:
388                    case STOP:
389                        return true;
390                    default:
391                        return false;
392                }
393            }
394
395            @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES",
396                                justification="better to keep cases in column order rather than to combine")
397            @Override
398            public int getPreferredWidth(int col) {
399                switch (col) {
400                    case SYSNAMECOL:
401                        return new JTextField(5).getPreferredSize().width;
402                    case COMMENTCOL:
403                        return new JTextField(20).getPreferredSize().width;
404                    case USERNAMECOL:
405                        return new JTextField(20).getPreferredSize().width;
406                    case ADDTOROSTERCOL: // not actually used due to the configureTable, setColumnToHoldButton, configureButton
407                        return new JTextField(12).getPreferredSize().width;
408                    case STOP: // not actually used due to the configureTable, setColumnToHoldButton, configureButton
409                        return new JTextField(6).getPreferredSize().width;
410                    case VALUECOL:
411                        return new JTextField(5).getPreferredSize().width;
412                    case SPEEDDIR:
413                        return new JTextField(10).getPreferredSize().width;
414                    case PROTOCOL:
415                        return new JTextField(5).getPreferredSize().width;
416                    default:
417                        //log.warn("Unexpected column in getPreferredWidth: "+col);
418                        return super.getPreferredWidth(col);
419                    //return new JTextField(8).getPreferredSize().width;
420                }
421            }
422
423            @Override
424            public void configureTable(JTable tbl) {
425                table = tbl;
426                setColumnToHoldButton(table, ADDTOROSTERCOL,
427                        new JButton(Bundle.getMessage("ButtonAddRoster")));
428                setColumnToHoldButton(table, STOP, stopButton());
429                super.configureTable(table);
430                XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel();
431                TableColumn column = columnModel.getColumnByModelIndex(SPEEDDIR);
432                columnModel.setColumnVisible(column, false);
433                column = columnModel.getColumnByModelIndex(STOP);
434                columnModel.setColumnVisible(column, false);
435            }
436
437            @Override
438            public NamedBean getBySystemName(@Nonnull String name) {
439                return null;
440            }
441
442            @Override
443            public NamedBean getByUserName(@Nonnull String name) {
444                return null;
445            }
446
447            @Override
448            synchronized public void dispose() {
449                showLocoMonitor = false;
450                getManager().removePropertyChangeListener(this);
451                if (ecosObjectIdList != null) {
452                    for (int i = 0; i < ecosObjectIdList.size(); i++) {
453                        jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(i));
454                        if (b != null) {
455                            b.removePropertyChangeListener(this);
456                        }
457                    }
458                }
459            }
460
461            protected void deleteLoco(final int row, int col) {
462                if (row >= ecosObjectIdList.size()) {
463                    log.debug("row is greater than list size");
464                    return;
465                }
466                jmri.jmrix.ecos.EcosLocoAddress b = getByEcosObject(ecosObjectIdList.get(row));
467                final JDialog dialog = new JDialog();
468                dialog.setTitle(Bundle.getMessage("RemoveLocoTitle"));
469                dialog.setLocation(300, 200);
470                dialog.setDefaultCloseOperation(javax.swing.JFrame.DISPOSE_ON_CLOSE);
471                JPanel container = new JPanel();
472                container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
473                container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
474
475                JLabel question = new JLabel(Bundle.getMessage("RemoveLocoXDialog", b.getEcosDescription()));
476                question.setAlignmentX(Component.CENTER_ALIGNMENT);
477                container.add(question);
478                JButton yesButton = new JButton(Bundle.getMessage("ButtonYes"));
479                JButton noButton = new JButton(Bundle.getMessage("ButtonNo"));
480                JPanel button = new JPanel();
481                button.setAlignmentX(Component.CENTER_ALIGNMENT);
482                button.add(yesButton);
483                button.add(noButton);
484                container.add(button);
485
486                noButton.addActionListener(new ActionListener() {
487                    @Override
488                    public void actionPerformed(ActionEvent e) {
489                        dialog.dispose();
490                    }
491                });
492
493                yesButton.addActionListener(new ActionListener() {
494                    @Override
495                    public void actionPerformed(ActionEvent e) {
496                        RemoveObjectFromEcos removeObjectFromEcos = new RemoveObjectFromEcos();
497                        removeObjectFromEcos.removeObjectFromEcos(ecosObjectIdList.get(row), adaptermemo.getTrafficController());
498                        dialog.dispose();
499                    }
500                });
501                container.setAlignmentX(Component.CENTER_ALIGNMENT);
502                container.setAlignmentY(Component.CENTER_ALIGNMENT);
503                dialog.getContentPane().add(container);
504                dialog.pack();
505                dialog.setModal(true);
506                dialog.setVisible(true);
507            }
508
509            @Override
510            public int getRowCount() {
511                return ecosObjectIdList.size();
512            }
513
514            @Override
515            public Object getValueAt(int row, int col) {
516                if (row >= ecosObjectIdList.size()) {
517                    log.debug("row is greater than list size");
518                    return null;
519
520                }
521                jmri.jmrix.ecos.EcosLocoAddress b;
522                switch (col) {
523                    case SYSNAMECOL:
524                        return ecosObjectIdList.get(row);
525                    case USERNAMECOL:  // return user name
526                        // sometimes, the TableSorter invokes this on rows that no longer exist, so we check
527                        b = getByEcosObject(ecosObjectIdList.get(row));
528                        return (b != null) ? b.getEcosDescription() : null;
529                    case VALUECOL:  //
530                        b = getByEcosObject(ecosObjectIdList.get(row));
531                        return (b != null) ? b.getNumber() : null;
532                    case COMMENTCOL:
533                        b = getByEcosObject(ecosObjectIdList.get(row));
534                        RosterEntry re = null;
535                        if (b != null) {
536                            re = Roster.getDefault().getEntryForId(b.getRosterId());
537                        }
538                        GlobalRosterEntryComboBox cb = (GlobalRosterEntryComboBox) table.getCellRenderer(row, col);
539                        if (re == null) {
540                            cb.setSelectedIndex(0);
541                        } else {
542                            cb.setSelectedItem(re);
543                        }
544                        return re;
545                    case PROTOCOL:
546                        b = getByEcosObject(ecosObjectIdList.get(row));
547                        return (b != null) ? b.getECOSProtocol() : null;
548                    case ADDTOROSTERCOL:  //
549                        b = getByEcosObject(ecosObjectIdList.get(row));
550                        if (b.getRosterId() == null || b.getRosterId().equals("")) {
551                            return Bundle.getMessage("ButtonAddRoster");
552                        } else {
553                            return " ";
554                        }
555                    case STOP:
556                        return Bundle.getMessage("ButtonStop");
557                    case SPEEDDIR:
558                        b = getByEcosObject(ecosObjectIdList.get(row));
559                        return (b != null) ? (b.getDirectionAsString() + " : " + b.getSpeed()) : null;
560                    case DELETECOL:  //
561                        return Bundle.getMessage("ButtonDelete");
562                    default:
563                        //log.error("internal state inconsistent with table requst for "+row+" "+col);
564                        return null;
565                }
566            }
567
568            @Override
569            protected String getBeanType() {
570                return Bundle.getMessage("EcosLocoTitle");
571            }
572
573            @Override
574            protected void showPopup(JmriMouseEvent e) {
575
576            }
577        };
578    }
579
580    boolean showLocoMonitor = false;
581
582    void showMonitorChanged() {
583        showLocoMonitor = showMonitorLoco.isSelected();
584        XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel();
585        TableColumn column = columnModel.getColumnByModelIndex(SPEEDDIR);
586        columnModel.setColumnVisible(column, showLocoMonitor);
587        column = columnModel.getColumnByModelIndex(STOP);
588        columnModel.setColumnVisible(column, showLocoMonitor);
589    }
590
591    JCheckBox showMonitorLoco = new JCheckBox("Monitor Loco Speed");
592
593    /**
594     * Create a JButton to edit a turnout operation.
595     *
596     * @return the JButton
597     */
598    protected JButton stopButton() {
599        JButton stopButton = new JButton("STOP");
600        return (stopButton);
601    }
602
603    void stopLoco(int row, int col) {
604
605        String objectNumber = ecosObjectIdList.get(row);
606        EcosMessage msg;
607        //We will repeat this three times to make sure it gets through.
608        for (int x = 0; x < 3; x++) {
609            msg = new EcosMessage("request(" + objectNumber + ", control, force)");
610            adaptermemo.getTrafficController().sendEcosMessage(msg, null);
611            msg = new EcosMessage("set(" + objectNumber + ", stop)");
612            adaptermemo.getTrafficController().sendEcosMessage(msg, null);
613            msg = new EcosMessage("release(" + objectNumber + ", control)");
614            adaptermemo.getTrafficController().sendEcosMessage(msg, null);
615        }
616    }
617
618    public void addToPanel(EcosLocoTableTabAction f) {
619        f.addToBottomBox(showMonitorLoco, adaptermemo.getUserName());
620        showMonitorLoco.setToolTipText("Show extra columns for configuring turnout feedback?");
621        showMonitorLoco.addActionListener(new ActionListener() {
622            @Override
623            public void actionPerformed(ActionEvent e) {
624                showMonitorChanged();
625            }
626        });
627    }
628
629    void addToRoster(int row, int col) {
630        if (getByEcosObject(ecosObjectIdList.get(row)).getRosterId() == null) {
631            EcosLocoToRoster addLoco = new EcosLocoToRoster(adaptermemo);
632            getByEcosObject(ecosObjectIdList.get(row)).allowAddToRoster();
633            addLoco.addToQueue(getByEcosObject(ecosObjectIdList.get(row)));
634            addLoco.processQueue();
635            m.fireTableRowsUpdated(row, row);
636        }
637    }
638
639    @Override
640    protected void setTitle() {
641        if (adaptermemo != null) {
642            f.setTitle(Bundle.getMessage("XLocoTableTitle", adaptermemo.getUserName()));
643        }
644        f.setTitle(Bundle.getMessage("EcosLocoTableTitle"));
645    }
646
647    @Override
648    protected String helpTarget() {
649        return "package.jmri.jmrix.ecos.swing.locodatabase.EcosLocoTable"; // very simple help page
650    }
651
652    static class RosterBoxRenderer extends GlobalRosterEntryComboBox implements TableCellRenderer {
653
654        public RosterBoxRenderer(RosterEntry re) {
655            super();
656            setNonSelectedItem(" ");
657            if (re == null) {
658                setSelectedIndex(0);
659            } else {
660                setSelectedItem(re);
661            }
662        }
663
664        @Override
665        public Component getTableCellRendererComponent(JTable table, Object value,
666                boolean isSelected, boolean hasFocus, int row, int column) {
667            if (isSelected) {
668                setForeground(table.getSelectionForeground());
669                super.setBackground(table.getSelectionBackground());
670            } else {
671                setForeground(table.getForeground());
672                setBackground(table.getBackground());
673            }
674            if (value == null) {
675                setSelectedIndex(0);
676            } else {
677                setSelectedItem(value);
678            }
679            return this;
680        }
681    }
682
683    static class RosterComboBoxEditor extends DefaultCellEditor {
684
685        public RosterComboBoxEditor(GlobalRosterEntryComboBox cb) {
686            super(cb);
687        }
688    }
689
690    @Override
691    protected void addPressed(ActionEvent e) {
692    }
693
694    @Override
695    protected String getClassName() {
696        return EcosLocoTableAction.class.getName();
697    }
698
699    private final static Logger log = LoggerFactory.getLogger(EcosLocoTableAction.class);
700
701}