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