001package jmri.jmrit.beantable;
002
003import java.awt.Container;
004import java.awt.FlowLayout;
005import java.awt.event.ItemEvent;
006import java.beans.PropertyVetoException;
007import java.util.List;
008import java.util.Map;
009
010import javax.annotation.Nonnull;
011import javax.swing.*;
012
013import jmri.*;
014import jmri.jmrit.logixng.*;
015import jmri.jmrit.logixng.SymbolTable.InitialValueType;
016import jmri.util.JmriJFrame;
017
018
019import jmri.jmrit.logixng.tools.swing.AbstractLogixNGEditor;
020import jmri.jmrit.logixng.tools.swing.LocalVariableTableModel;
021// import jmri.jmrit.logixng.tools.swing.GlobalVariableEditor;
022
023/**
024 * Swing action to create and register a LogixNG Global Variables Table.
025 * <p>
026 * Also contains the panes to create, edit, and delete a LogixNG Global Variable.
027 * <p>
028 * Most of the text used in this GUI is in BeanTableBundle.properties, accessed
029 * via Bundle.getMessage().
030 *
031 * @author Dave Duchamp Copyright (C) 2007 (LogixTableAction)
032 * @author Pete Cressman Copyright (C) 2009, 2010, 2011 (LogixTableAction)
033 * @author Matthew Harris copyright (c) 2009 (LogixTableAction)
034 * @author Dave Sand copyright (c) 2017 (LogixTableAction)
035 * @author Daniel Bergqvist copyright (c) 2022
036 */
037public class LogixNGGlobalVariableTableAction extends AbstractLogixNGTableAction<GlobalVariable> {
038
039    /**
040     * Create a LogixNGGlobalVariablesTableAction instance.
041     *
042     * @param s the Action title, not the title of the resulting frame. Perhaps
043     *          this should be changed?
044     */
045    public LogixNGGlobalVariableTableAction(String s) {
046        super(s);
047    }
048
049    /**
050     * Create a LogixNGTableAction instance with default title.
051     */
052    public LogixNGGlobalVariableTableAction() {
053        this(Bundle.getMessage("TitleLogixNGGlobalVariablesTable"));
054    }
055
056    @Override
057    protected void setTitle() {
058        f.setTitle(Bundle.getMessage("TitleLogixNGGlobalVariablesTable"));
059    }
060
061    @Override
062    public String getClassDescription() {
063        return Bundle.getMessage("TitleLogixNGGlobalVariablesTable");        // NOI18N
064    }
065
066    @Override
067    protected AbstractLogixNGEditor<GlobalVariable> getEditor(BeanTableDataModel<GlobalVariable> m, String sName) {
068        return null;
069    }
070
071    @Override
072    protected boolean isEditSupported() {
073        return false;
074    }
075
076    @Override
077    protected Manager<GlobalVariable> getManager() {
078        return InstanceManager.getDefault(GlobalVariableManager.class);
079    }
080
081    @Override
082    public void setEnabled(GlobalVariable globalVariable, boolean enable) {
083    }
084
085    @Override
086    protected boolean isEnabled(GlobalVariable globalVariable) {
087        return true;
088    }
089
090    @Override
091    public void enableAll(boolean enable) {
092    }
093
094    @Override
095    protected GlobalVariable createBean(String userName) {
096        GlobalVariable globalVariable =
097                InstanceManager.getDefault(GlobalVariableManager.class)
098                        .createGlobalVariable(userName);
099        return globalVariable;
100    }
101
102    @Override
103    protected GlobalVariable createBean(String systemName, String userName) {
104        GlobalVariable globalVariable =
105                InstanceManager.getDefault(GlobalVariableManager.class)
106                        .createGlobalVariable(systemName, userName);
107        return globalVariable;
108    }
109
110    @Override
111    public void deleteBean(GlobalVariable globalVariable) {
112        try {
113            InstanceManager.getDefault(GlobalVariableManager.class).deleteBean(globalVariable, "DoDelete");
114        } catch (PropertyVetoException e) {
115            //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto
116            log.error("{} : Could not Delete.", e.getMessage());
117        }
118    }
119/*
120    @Override
121    protected void copyBean(@Nonnull LogixNG sourceBean, @Nonnull LogixNG targetBean) {
122        for (int i = 0; i < sourceBean.getNumConditionalNGs(); i++) {
123            copyConditionalNGToLogixNG(sourceBean.getConditionalNG(i), targetBean);
124        }
125    }
126*/
127    @Override
128    protected boolean isCopyBeanSupported() {
129        return false;
130    }
131
132    @SuppressWarnings("unchecked")  // Checked cast is not possible due to type erasure
133    @Override
134    protected String getBeanText(GlobalVariable e, Base.PrintTreeSettings printTreeSettings) {
135        var content = new StringBuilder(Bundle.getMessage("LogixNG_GlobalVar_Browse_Header",
136                e.getSystemName(),
137                e.getUserName(),
138                e.getInitialValueType()));
139        content.append(Bundle.getMessage("LogixNG_GlobalVar_Browse_Value"));
140        var value = e.getValue();
141
142        if (value instanceof Map) {
143            var map = ((Map<? extends Object, ? extends Object>)value);
144            for (var entry : map.entrySet()) {
145                var line = String.format("%n        %s -> %s",
146                        entry.getKey(),
147                        entry.getValue());
148                content.append(line);
149            }
150        } else if (value instanceof List) {
151            var list = ((List<? extends Object>)value);
152            for (int i=0; i < list.size(); i++) {
153                var line = String.format("%n        %s", list.get(i));
154                content.append(line);
155            }
156        } else {
157            content.append(value);
158        }
159        return content.toString();
160    }
161
162    @Override
163    protected String getBrowserTitle() {
164        return Bundle.getMessage("LogixNG_GlobalVar_Browse_Title");
165    }
166
167    @Override
168    protected String getAddTitleKey() {
169        return "TitleAddLogixNGGlobalVariable";
170    }
171
172    @Override
173    protected String getCreateButtonHintKey() {
174        return "LogixNGCreateGlobalVariableButtonHint";
175    }
176
177    @Override
178    protected String helpTarget() {
179        return "package.jmri.jmrit.beantable.LogixNGGlobalVariables";  // NOI18N
180    }
181
182    /**
183     * Create or copy bean frame.
184     *
185     * @param titleId   property key to fetch as title of the frame (using Bundle)
186     * @param startMessageId part 1 of property key to fetch as user instruction on
187     *                  pane, either 1 or 2 is added to form the whole key
188     * @return the button JPanel
189     */
190    @Override
191    protected JPanel makeAddFrame(String titleId, String startMessageId) {
192        addLogixNGFrame = new JmriJFrame(Bundle.getMessage(titleId));
193        addLogixNGFrame.addHelpMenu(
194                "package.jmri.jmrit.beantable.LogixNGGlobalVariables", true);     // NOI18N
195        addLogixNGFrame.setLocation(50, 30);
196        Container contentPane = addLogixNGFrame.getContentPane();
197        contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS));
198
199        JPanel p;
200        p = new JPanel();
201        p.setLayout(new FlowLayout());
202        p.setLayout(new java.awt.GridBagLayout());
203        java.awt.GridBagConstraints c = new java.awt.GridBagConstraints();
204        c.gridwidth = 1;
205        c.gridheight = 1;
206        c.gridx = 0;
207        c.gridy = 0;
208        c.anchor = java.awt.GridBagConstraints.EAST;
209        p.add(_sysNameLabel, c);
210        _sysNameLabel.setLabelFor(_systemName);
211        c.gridy = 1;
212        p.add(_userNameLabel, c);
213        _userNameLabel.setLabelFor(_addUserName);
214        c.gridx = 1;
215        c.gridy = 0;
216        c.anchor = java.awt.GridBagConstraints.WEST;
217        c.weightx = 1.0;
218        c.fill = java.awt.GridBagConstraints.HORIZONTAL;  // text field will expand
219        p.add(_systemName, c);
220        c.gridy = 1;
221        p.add(_addUserName, c);
222        c.gridx = 2;
223        c.gridy = 1;
224        c.anchor = java.awt.GridBagConstraints.WEST;
225        c.weightx = 1.0;
226        c.fill = java.awt.GridBagConstraints.HORIZONTAL;  // text field will expand
227        c.gridy = 0;
228        p.add(_autoSystemName, c);
229        _addUserName.setToolTipText(Bundle.getMessage("LogixNGGlobalVariableUserNameHint"));    // NOI18N
230        _systemName.setToolTipText(Bundle.getMessage("LogixNGGlobalVariableSystemNameHint"));   // NOI18N
231        contentPane.add(p);
232        // set up message
233        JPanel panel3 = new JPanel();
234        panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS));
235        JPanel panel31 = new JPanel();
236        panel31.setLayout(new FlowLayout());
237        JLabel message1 = new JLabel(Bundle.getMessage(startMessageId + "LogixNGMessage1"));  // NOI18N
238        panel31.add(message1);
239        JPanel panel32 = new JPanel();
240        JLabel message2 = new JLabel(Bundle.getMessage(startMessageId + "LogixNGMessage2"));  // NOI18N
241        panel32.add(message2);
242        panel3.add(panel31);
243        panel3.add(panel32);
244        contentPane.add(panel3);
245
246        // set up create and cancel buttons
247        JPanel panel5 = new JPanel();
248        panel5.setLayout(new FlowLayout());
249        // Cancel
250        JButton cancel = new JButton(Bundle.getMessage("ButtonCancel"));    // NOI18N
251        panel5.add(cancel);
252        cancel.addActionListener(this::cancelAddPressed);
253        cancel.setToolTipText(Bundle.getMessage("CancelLogixNGGlobalVariableButtonHint"));      // NOI18N
254
255        addLogixNGFrame.addWindowListener(new java.awt.event.WindowAdapter() {
256            @Override
257            public void windowClosing(java.awt.event.WindowEvent e) {
258                cancelAddPressed(null);
259            }
260        });
261        contentPane.add(panel5);
262
263        _autoSystemName.addItemListener((ItemEvent e) -> {
264            autoSystemName();
265        });
266        return panel5;
267    }
268
269    @Override
270    protected void getListenerRefsIncludingChildren(GlobalVariable globalVariable, java.util.List<String> list) {
271        globalVariable.getListenerRefsIncludingChildren(list);
272    }
273
274    @Override
275    protected boolean hasChildren(GlobalVariable globalVariable) {
276        return false;
277    }
278
279    /**
280     * Create the JTable DataModel, along with the changes (overrides of
281     * BeanTableDataModel) for the specific case of a LogixNG table.
282     */
283    @Override
284    protected void createModel() {
285        m = new TableModel();
286    }
287
288    @Override
289    public void addToFrame(@Nonnull BeanTableFrame<GlobalVariable> f) {
290        f.addToBottomBox(new JLabel(Bundle.getMessage("LogixNGGlobalVariable_InfoAboutGlobalVariables")), null);
291    }
292
293    @Override
294    public void addToFrame(@Nonnull ListedTableFrame.TabbedTableItem<GlobalVariable> tti) {
295        tti.addToBottomBox(new JLabel(Bundle.getMessage("LogixNGGlobalVariable_InfoAboutGlobalVariables")));
296    }
297
298
299    protected class TableModel extends AbstractLogixNGTableAction<GlobalVariable>.TableModel {
300
301        // overlay the state column with the edit column
302        static private final int VARIABLE_TYPE_COL = NUMCOLUMN;
303        static private final int VARIABLE_INIT_VALUE_COL = NUMCOLUMN + 1;
304        static private final int SET_TO_INIT_VALUE_COL = VARIABLE_INIT_VALUE_COL + 1;
305        static private final int NUM_COLUMNS = SET_TO_INIT_VALUE_COL + 1;
306
307        @Override
308        protected boolean isClearUserNameAllowed() {
309            return false;
310        }
311
312        @Override
313        public int getColumnCount() {
314            return NUM_COLUMNS;
315        }
316
317        @Override
318        public String getColumnName(int col) {
319            switch (col) {
320                case VARIABLE_TYPE_COL:
321                    return Bundle.getMessage("LogixNGGlobalVariableColumnHeadInitialType");
322                case VARIABLE_INIT_VALUE_COL:
323                    return Bundle.getMessage("LogixNGGlobalVariableColumnHeadInitialValue");
324                case SET_TO_INIT_VALUE_COL:
325                    return Bundle.getMessage("LogixNGGlobalVariableColumnHeadSetToInitValue");
326                default:
327                    return super.getColumnName(col);
328            }
329        }
330
331        @Override
332        public Class<?> getColumnClass(int col) {
333            switch (col) {
334                case VARIABLE_TYPE_COL:
335                    return InitialValueType.class;
336                case VARIABLE_INIT_VALUE_COL:
337                    return String.class;
338                case SET_TO_INIT_VALUE_COL:
339                    return JButton.class;
340                default:
341                    return super.getColumnClass(col);
342            }
343        }
344
345        @Override
346        public int getPreferredWidth(int col) {
347            switch (col) {
348                case VARIABLE_TYPE_COL:
349                    return new JTextField(12).getPreferredSize().width;
350                case VARIABLE_INIT_VALUE_COL:
351                    return new JTextField(17).getPreferredSize().width;
352                case SET_TO_INIT_VALUE_COL:
353                    return new JButton(Bundle.getMessage(
354                            "LogixNGGlobalVariableColumnHeadSetToInitValue"))
355                            .getPreferredSize().width;
356                default:
357                    return super.getPreferredWidth(col);
358            }
359        }
360
361        @Override
362        public boolean isCellEditable(int row, int col) {
363            switch (col) {
364                case VARIABLE_TYPE_COL:
365                case VARIABLE_INIT_VALUE_COL:
366                case SET_TO_INIT_VALUE_COL:
367                    return true;
368                default:
369                    return super.isCellEditable(row, col);
370            }
371        }
372
373        @Override
374        public Object getValueAt(int row, int col) {
375            GlobalVariable gv;
376            switch (col) {
377                case VARIABLE_TYPE_COL:
378                    gv = (GlobalVariable) getValueAt(row, SYSNAMECOL);
379                    if (gv == null) { return null; }     // Error
380                    return gv.getInitialValueType();
381
382                case VARIABLE_INIT_VALUE_COL:
383                    gv = (GlobalVariable) getValueAt(row, SYSNAMECOL);
384                    if (gv == null) { return null; }     // Error
385                    return gv.getInitialValueData();
386
387                case SET_TO_INIT_VALUE_COL:
388                    return Bundle.getMessage(
389                            "LogixNGGlobalVariableColumnHeadSetToInitValue");
390
391                default:
392                    return super.getValueAt(row, col);
393            }
394        }
395
396        @SuppressWarnings("unchecked")  // Unchecked cast from Object to E
397        @Override
398        public void setValueAt(Object value, int row, int col) {
399            GlobalVariable gv;
400            switch (col) {
401                case VARIABLE_TYPE_COL:
402                    gv = (GlobalVariable) getValueAt(row, SYSNAMECOL);
403                    gv.setInitialValueType((InitialValueType) value);
404                    break;
405
406                case VARIABLE_INIT_VALUE_COL:
407                    gv = (GlobalVariable) getValueAt(row, SYSNAMECOL);
408                    gv.setInitialValueData((String) value);
409                    break;
410
411                case SET_TO_INIT_VALUE_COL:
412                    gv = (GlobalVariable) getValueAt(row, SYSNAMECOL);
413                    try {
414                        gv.initialize();
415                    } catch (JmriException | RuntimeException e) {
416                        jmri.util.swing.JmriJOptionPane.showMessageDialog(null,
417                                e.getLocalizedMessage(),
418                                Bundle.getMessage("ErrorTitle"), // NOI18N
419                                jmri.util.swing.JmriJOptionPane.ERROR_MESSAGE);
420                    }
421                    break;
422
423                default:
424                    super.setValueAt(value, row, col);
425                    break;
426            }
427        }
428
429        @Override
430        public void configureTable(JTable table) {
431            table.setDefaultRenderer(InitialValueType.class,
432                    new LocalVariableTableModel.TypeCellRenderer());
433            table.setDefaultEditor(InitialValueType.class,
434                    new LocalVariableTableModel.TypeCellEditor());
435
436            super.configureTable(table);
437        }
438    }
439
440
441    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGGlobalVariableTableAction.class);
442
443}