001package jmri.jmrix.can.cbus.swing.configtool;
002
003import java.awt.Color;
004import java.awt.Frame;
005import java.awt.GridBagConstraints;
006import java.awt.GridBagLayout;
007import java.awt.event.ActionEvent;
008import java.awt.event.FocusEvent;
009import java.awt.event.FocusListener;
010import javax.swing.*;
011import jmri.jmrix.AbstractMessage;
012import jmri.jmrix.can.CanFrame;
013import jmri.jmrix.can.CanListener;
014import jmri.jmrix.can.CanSystemConnectionMemo;
015import jmri.jmrix.can.cbus.CbusMessage;
016import jmri.jmrix.can.cbus.swing.console.CbusConsolePane;
017import jmri.jmrix.can.cbus.swing.CbusEventHighlightFrame;
018import jmri.jmrix.can.cbus.swing.CbusFilterFrame;
019
020import org.slf4j.Logger;
021import org.slf4j.LoggerFactory;
022
023
024/**
025 * Pane for user creation of Sensor, Turnouts and Lights that are linked to CBUS
026 * events.
027 *
028 * @author Bob Jacobsen Copyright (C) 2008
029 * @since 2.3.1
030 */
031public class ConfigToolPane extends jmri.jmrix.can.swing.CanPanel implements CanListener {
032
033    protected static int configtool_instance_num;
034    final static int NRECORDERS = 6;
035    private final CbusEventRecorder[] recorders = new CbusEventRecorder[NRECORDERS];
036    private CbusFilterFrame _filterFrame;
037    private CbusEventHighlightFrame _highlightFrame;
038    private final CbusConsolePane _console;
039    protected JButton filterButton;
040    protected JButton highlightButton;
041    private JButton resetCaptureButton;
042
043    public static void incrementInstance() {
044        configtool_instance_num++;
045    }
046
047    public static int getConfigToolInstanceNum() {
048        log.debug("instance num {}",configtool_instance_num);
049        return configtool_instance_num;
050    }
051    
052    public ConfigToolPane() {
053        super();
054        _filterFrame = null;
055        _highlightFrame = null;
056        _console = null;
057    }
058
059    public ConfigToolPane(CbusConsolePane console, CbusFilterFrame filterFrame, CbusEventHighlightFrame highlightFrame) {
060        super();
061        _filterFrame = filterFrame;
062        _highlightFrame = highlightFrame;
063        _console = console;
064    }
065
066    public void init() {
067        // log.debug("ConfigToolPane init");
068        
069        this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
070
071        // add event displays
072        JPanel p1 = new JPanel();
073        p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS));
074        
075        JPanel buttons = new JPanel();
076        buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
077        
078        filterButton = new JButton(Bundle.getMessage("ButtonFilter"));
079        filterButton.setVisible(true);
080        filterButton.setToolTipText(Bundle.getMessage("TooltipFilter"));
081        buttons.add(filterButton);
082        
083        filterButton.addActionListener(this::filterButtonActionPerformed);
084        
085       
086        highlightButton = new JButton(Bundle.getMessage("ButtonHighlight"));
087        highlightButton.setVisible(true);
088        highlightButton.setToolTipText(Bundle.getMessage("TooltipHighlighter"));
089        buttons.add(highlightButton);
090        
091        highlightButton.addActionListener(this::highlightButtonActionPerformed);
092        
093        resetCaptureButton = new JButton(Bundle.getMessage("ButtonResetCapture"));
094        resetCaptureButton.setVisible(true);
095        resetCaptureButton.setHorizontalAlignment(SwingConstants.RIGHT);
096        buttons.add(resetCaptureButton);
097        
098        resetCaptureButton.addActionListener(this::resetCaptureButtonActionPerformed);
099        
100        p1.add(buttons);
101        
102        for (int i = 0; i < recorders.length; i++) {
103            recorders[i] = new CbusEventRecorder();
104            p1.add(recorders[i]);
105        }
106        p1.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("BorderLayoutEvents")));
107        add(p1);
108
109        // add sensor
110        makeSensor = new MakeNamedBean("LabelEventActive", "LabelEventInactive") {
111            @Override
112            void create(String name) {
113                try {
114                    ((jmri.SensorManager) memo.get(jmri.SensorManager.class)).provideSensor(name);
115                }
116                catch (IllegalArgumentException ex) {
117                    JOptionPane.showMessageDialog(this, 
118                        (ex.getMessage()), Bundle.getMessage("WarningTitle"),
119                        JOptionPane.ERROR_MESSAGE);
120                }
121            }
122        };
123        makeSensor.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TitleAddX", Bundle.getMessage("BeanNameSensor"))));
124        add(makeSensor);
125
126        // add turnout
127        makeTurnout = new MakeNamedBean("LabelEventThrown", "LabelEventClosed") {
128            @Override
129            void create(String name) {
130                try {
131                    ((jmri.TurnoutManager) memo.get(jmri.TurnoutManager.class)).provideTurnout(name); 
132                }
133                catch (IllegalArgumentException ex) {
134                    JOptionPane.showMessageDialog(this, 
135                        (ex.getMessage()), Bundle.getMessage("WarningTitle"),
136                        JOptionPane.ERROR_MESSAGE);
137                }
138            }
139        };
140        makeTurnout.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TitleAddX", Bundle.getMessage("BeanNameTurnout"))));
141        add(makeTurnout);
142        
143        // add light
144        makeLight = new MakeNamedBean("LabelEventLightOn", "LabelEventLightOff") {
145            @Override
146            void create(String name) {
147                try {
148                    ((jmri.LightManager) memo.get(jmri.LightManager.class)).provideLight(name);
149                }
150                catch (Exception ex) {
151                    JOptionPane.showMessageDialog(this, 
152                        (ex.getMessage()), Bundle.getMessage("WarningTitle"),
153                        JOptionPane.ERROR_MESSAGE);
154                }
155            }
156        };
157        makeLight.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("TitleAddX", Bundle.getMessage("BeanNameLight"))));
158        add(makeLight);        
159        
160    }
161
162    @Override
163    public void initComponents(CanSystemConnectionMemo memo) {
164        super.initComponents(memo);
165        addTc(memo);
166        incrementInstance();
167        init();
168    }
169
170    @Override
171    public String getTitle() {
172        if (memo != null) {
173            StringBuilder title = new StringBuilder(20);
174            title.append(memo.getUserName());
175            title.append(" ");
176            title.append(Bundle.getMessage("CapConfigTitle"));
177            if (getConfigToolInstanceNum() > 1) {
178                title.append(" ");
179                title.append( getConfigToolInstanceNum() );
180            }
181            return title.toString();
182        }
183        return Bundle.getMessage("CapConfigTitle");
184    }
185
186    private MakeNamedBean makeSensor;
187    private MakeNamedBean makeTurnout;
188    private MakeNamedBean makeLight;
189
190    @Override
191    public void reply(jmri.jmrix.can.CanReply m) {
192        sendToListeners(m);
193    }
194
195    /** {@inheritDoc} */
196    @Override
197    public void message(jmri.jmrix.can.CanMessage m) {
198        sendToListeners(m);
199    }
200    
201    private void sendToListeners( AbstractMessage m ){
202        if ( ((CanFrame)m).extendedOrRtr()) {
203            return;
204        }
205        if ( ( _filterFrame!=null ) && ( _filterFrame.filter(m)) ) {
206            return;
207        }
208        // forward to anybody waiting to capture
209        makeSensor.processFrame(m);
210        makeTurnout.processFrame(m);
211        makeLight.processFrame(m);
212        for (CbusEventRecorder recorder : recorders) {
213            if (recorder.waiting()) {
214                recorder.processFrame(m);
215                break;
216            }
217        }
218    }
219    
220    /** {@inheritDoc} */
221    @Override
222    public String getHelpTarget() {
223        return "package.jmri.jmrix.can.cbus.swing.configtool.ConfigToolFrame";
224    }
225
226    @Override
227    public void dispose() {
228        removeTc(memo);
229        clearFilterFrame();
230        clearHighlightFrame();
231        super.dispose();
232    }
233
234    private void clearFilterFrame(){
235        if (_filterFrame != null) {
236            _filterFrame.dispose();
237        }
238        _filterFrame=null;
239    }
240
241    private void clearHighlightFrame(){
242        if (_highlightFrame != null) {
243            _highlightFrame.dispose();
244        }
245        _highlightFrame=null;
246    }
247
248
249    public void resetCaptureButtonActionPerformed(ActionEvent e) {
250        for (CbusEventRecorder recorder : recorders) {
251            recorder.capture.setSelected(true);
252        }
253    }
254
255    public void filterButtonActionPerformed(ActionEvent e) {
256        // log.debug("Cbus Console filter button action performed");
257        
258        if ( _console != null ) {
259            _console.displayPane.filterButton.doClick();
260            return;
261        }
262        
263        if (_filterFrame == null) {
264            _filterFrame = new CbusFilterFrame(_console,this);
265            _filterFrame.initComponents();
266        } else {
267            _filterFrame.setState(Frame.NORMAL);
268        }
269        _filterFrame.setVisible(true);
270    }
271
272    public void highlightButtonActionPerformed(ActionEvent e) {
273        // log.debug("Cbus Console filter button action performed");
274        if ( _console != null ) {
275            _console.displayPane.highlightButton.doClick();
276            return;
277        }
278        if (_highlightFrame == null) {
279            _highlightFrame = new CbusEventHighlightFrame(null,this);
280            _highlightFrame.initComponents();
281            
282        } else {
283            _highlightFrame.setState(Frame.NORMAL);
284        }
285        _highlightFrame.setVisible(true);
286    }
287
288    public void setHighlighter( CbusEventHighlightFrame highlightFrame){
289        _highlightFrame = highlightFrame;
290    }
291
292    public void setFilter( CbusFilterFrame filterFrame){
293        _filterFrame = filterFrame;
294    }
295
296    /**
297     * Class to build one NamedBean
298     */
299    private class MakeNamedBean extends JPanel {
300
301        private JTextField f1 = new JTextField(15);
302        private JTextField f2 = new JTextField(15);
303        private JTextField f3 = new JTextField(15);
304        
305        private final JButton bc;
306
307        JToggleButton b1 = new JToggleButton(Bundle.getMessage("ButtonCaptureNext"));
308        JToggleButton b2 = new JToggleButton(Bundle.getMessage("ButtonCaptureNext"));
309        JToggleButton b3 = new JToggleButton(Bundle.getMessage("ButtonCaptureNext"));
310        
311        private final JToggleButton[] captureNextButtons = new JToggleButton[]{b1,b2,b3};
312        private final JTextField[] fields = new JTextField[]{f1,f2,f3};
313
314        /**
315         * Create CBUS NamedBean using a JPanel for user interaction.
316         *
317         * @param name1 string for Label 1 in configuration pane
318         * @param name2 string for Label 2 in configuration pane
319         */
320        MakeNamedBean(String name1, String name2) {
321            // actions
322            bc = new JButton(Bundle.getMessage("ButtonCreate"));
323            bc.addActionListener((ActionEvent e) -> {
324                if (f2.getText().isEmpty()) {
325                    create(f1.getText());
326                } else {
327                    create(f1.getText() + ";" + f2.getText());
328                }
329            });
330            
331            initGui(name1, name2);
332        }
333
334        private void initGui(String name1, String name2){
335
336            // GUI
337            setLayout(new GridBagLayout());
338            GridBagConstraints c = new GridBagConstraints();
339            c.gridwidth = 1;
340            c.gridheight = 1;
341
342            c.gridx = 0;
343            c.gridy = 0;
344            c.anchor = GridBagConstraints.EAST;
345            add(new JLabel(Bundle.getMessage(name1)), c);
346
347            c.gridx = 1;
348            c.gridy = 0;
349            c.anchor = GridBagConstraints.WEST;
350            add(f1, c);
351
352            c.gridx = 2;
353            c.gridy = 0;
354            c.anchor = GridBagConstraints.WEST;
355            add(b1, c);
356            b1.setToolTipText(Bundle.getMessage("CaptureNextTooltip"));
357
358            c.gridx = 0;
359            c.gridy = 1;
360            c.anchor = GridBagConstraints.EAST;
361            add(new JLabel(Bundle.getMessage(name2)), c);
362
363            c.gridx = 1;
364            c.gridy = 1;
365            c.anchor = GridBagConstraints.EAST;
366            add(f2, c);
367
368            c.gridx = 2;
369            c.gridy = 1;
370            c.anchor = GridBagConstraints.WEST;
371            add(b2, c);
372            b2.setToolTipText(Bundle.getMessage("CaptureNextTooltip"));
373
374            c.gridx = 1;
375            c.gridy = 2;
376            c.anchor = GridBagConstraints.WEST;
377            add(bc, c);
378            bc.setToolTipText(Bundle.getMessage("CreateTooltip"));
379        }
380
381        void create(String name) {
382        }
383        
384        public void processFrame(AbstractMessage m){
385        
386            int high = (_highlightFrame != null) ? _highlightFrame.highlight(m) : -1;
387            for (int i=0; i < 3; i++){
388            
389                if (captureNextButtons[i].isSelected()) {
390                    fields[i].setText(CbusMessage.toAddress(m));
391                    captureNextButtons[i].setSelected(false);
392                    if ( high > -1 ) {
393                        fields[i].setBackground(CbusEventHighlightFrame.highlightColors[high]);
394                    } else {
395                        fields[i].setBackground(Color.WHITE);
396                    }
397                }
398            }
399        }
400    }
401
402    /**
403     * Class to handle recording and presenting one event.
404     */
405    private class CbusEventRecorder extends JPanel implements FocusListener {
406
407        CbusEventRecorder() {
408            super();
409            
410            init();
411            event.setEditable(false);
412            event.setDragEnabled(true);
413            event.setBackground(Color.WHITE);
414            capture.setSelected(true);
415            
416        }
417        
418        final void init() {
419            event.addFocusListener(this);
420            this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
421            add(event);
422            add(capture);
423        }
424
425        JCheckBox capture = new JCheckBox(Bundle.getMessage("MsgCaptureNext"));
426        JTextField event = new JTextField(15);
427
428        boolean waiting() {
429            return capture.isSelected();
430        }
431
432        public void processFrame(AbstractMessage m) {
433            if (capture.isSelected()) {
434                event.setText(CbusMessage.toAddress(m));
435                capture.setSelected(false);
436                int high = (_highlightFrame != null) ? _highlightFrame.highlight(m) : -1;
437                if ( high > -1 ) {
438                    event.setBackground(CbusEventHighlightFrame.highlightColors[high]);
439                } else {
440                    event.setBackground(Color.WHITE);
441                }
442            }
443        }
444
445        @Override
446        public void focusGained(FocusEvent fe) {
447            JTextField txt = (JTextField)fe.getComponent();
448            txt.selectAll();
449        }
450
451        @Override
452        public void focusLost(FocusEvent e) {
453        }
454
455    }
456
457    /**
458     * Nested class to create one of these using old-style defaults.
459     */
460    static public class Default extends jmri.jmrix.can.swing.CanNamedPaneAction {
461        public Default() {
462            super(Bundle.getMessage("CapConfigTitle"),
463                    new jmri.util.swing.sdi.JmriJFrameInterface(),
464                    ConfigToolPane.class.getName(),
465                    jmri.InstanceManager.getDefault(CanSystemConnectionMemo.class));
466        }
467    }
468
469    private static final Logger log = LoggerFactory.getLogger(ConfigToolPane.class);
470
471}