001package jmri.jmrix.openlcb.swing;
002
003import java.awt.event.ActionEvent;
004import java.awt.event.ActionListener;
005import java.awt.event.WindowEvent;
006
007import javax.swing.JButton;
008import javax.swing.JPanel;
009import javax.swing.JTextField;
010import javax.swing.Timer;
011import javax.swing.text.JTextComponent;
012
013import jmri.ShutDownTask;
014import jmri.jmrix.can.CanSystemConnectionMemo;
015import jmri.jmrix.openlcb.OlcbEventNameStore;
016import jmri.util.JmriJFrame;
017import jmri.util.swing.WrapLayout;
018
019import org.openlcb.NodeID;
020import org.openlcb.OlcbInterface;
021import org.openlcb.cdi.impl.ConfigRepresentation;
022import org.openlcb.cdi.swing.CdiPanel;
023import org.openlcb.swing.EventIdTextField;
024
025/**
026 * Shared code for creating UI elements from different places in the application.
027 * <p>
028 * Created by bracz on 11/21/16.
029 */
030
031public class ClientActions {
032    private final OlcbInterface iface;
033    private final CanSystemConnectionMemo memo;
034
035    public ClientActions(OlcbInterface iface, CanSystemConnectionMemo memo) {
036        this.iface = iface;
037        this.memo = memo;
038    }
039
040    CdiPanel cdiPanel;
041    ShutDownTask shutDownTask;
042    
043    public void openCdiWindow(NodeID destNode, String description) {
044        final java.util.ArrayList<JButton> sensorButtonList = new java.util.ArrayList<>();
045        final java.util.ArrayList<JButton> turnoutButtonList = new java.util.ArrayList<>();
046
047        JmriJFrame f = new NodeSpecificFrame(destNode);
048        f.setTitle(Bundle.getMessage("CdiPanelConfigure", description));
049        f.setLayout(new javax.swing.BoxLayout(f.getContentPane(), javax.swing.BoxLayout.Y_AXIS));
050        f.addHelpMenu("package.jmri.jmrix.openlcb.swing.networktree.NetworkTreePane_CDItool", true);
051        
052        cdiPanel = new CdiPanel(){
053            // override and extend window closing behavior
054            @Override
055            protected void targetWindowClosingEvent(WindowEvent evt) { // evt is ignored here
056                log.trace("overridden targetWindowClosingEvent runs");
057                super.targetWindowClosingEvent(evt);
058            }
059            // when actually closing the window, also deregister the safety shutdown class
060            @Override
061            public void release() {
062                super.release();
063                jmri.InstanceManager.getDefault(jmri.ShutDownManager.class).deregister(shutDownTask);
064            }
065        };
066        f.add(cdiPanel);
067        cdiPanel.setEventTable(iface.getNodeStore().getSimpleNodeIdent(destNode).getUserName(),
068                iface.getEventTable());
069                
070        // Add a shutdown task to handle "Cancel" selections should there be unsaved
071        // changed at Shutdown
072        jmri.InstanceManager.getDefault(jmri.ShutDownManager.class).register(
073            shutDownTask = new ShutDownTask() {
074                @Override
075                public String getName() { return "CDI Window Check"; }
076                @Override
077                public Boolean call() {
078                    log.trace("call( checks contents)");
079                    boolean result = cdiPanel.checkOnWindowClosing(); // true to continue shutdown, false to not
080                    if (result) {
081                        // you don't want a second check on automatic window closing during shutdown
082                        cdiPanel.setWindowCloseCheckAlreadyHandled();
083                    }
084                    return result; 
085                }
086
087                @Override
088                public void propertyChange(java.beans.PropertyChangeEvent e) {
089                    // don't care if somebody else cancels
090                }
091                
092                @Override
093                public void run() {
094                    // we're shutting down, nothing to do 
095                }
096        });
097        
098        // create an object to add "New Sensor" buttons
099        CdiPanel.GuiItemFactory factory = new CdiPanel.GuiItemFactory() {
100            private boolean haveButtons = false;
101            @Override
102            public JButton handleReadButton(JButton button) {
103                return button;
104            }
105
106            @Override
107            public JButton handleWriteButton(JButton button) {
108                return button;
109            }
110
111            @Override
112            public void handleGroupPaneStart(JPanel pane) {
113                this.gpane = pane;
114                evt1 = null;
115                evt2 = null;
116                desc = null;
117            }
118
119            public void installButtonsOnEndOfGroup(JPanel pane, boolean buttonsCountTowardAllButtons) {
120                JPanel p = new JPanel();
121                p.setLayout(new WrapLayout());
122                p.setAlignmentX(-1.0f);
123                pane.add(p);
124                JButton button = new JButton(Bundle.getMessage("CdiPanelMakeSensor"));
125                p.add(button);
126                if (buttonsCountTowardAllButtons) {
127                    sensorButtonList.add(button);
128                }
129                button.addActionListener(new java.awt.event.ActionListener() {
130                    @Override
131                    public void actionPerformed(java.awt.event.ActionEvent e) {
132                        log.info("make sensor MS{};{} [{}]", mevt1.getEventIDAsDottedString(), mevt2.getEventIDAsDottedString(), mdesc.getText());
133                        jmri.Sensor sensor = jmri.InstanceManager.sensorManagerInstance()
134                                .provideSensor(memo.getSystemPrefix() + "S" + mevt1.getEventIDAsDottedString() + ";" + mevt2.getEventIDAsDottedString());
135                        if (mdesc.getText().length() > 0) {
136                            sensor.setUserName(mdesc.getText());
137                        }
138                    }
139
140                    final JTextField mdesc = desc;
141                    final NamedEventIdTextField mevt1 = evt1;
142                    final NamedEventIdTextField mevt2 = evt2;
143                });
144                button = new JButton(Bundle.getMessage("CdiPanelMakeTurnout"));
145                p.add(button);
146                if (buttonsCountTowardAllButtons) {
147                    turnoutButtonList.add(button);
148                }
149                button.addActionListener(new java.awt.event.ActionListener() {
150                    @Override
151                    public void actionPerformed(java.awt.event.ActionEvent e) {
152                        log.info("make turnout MT{};{} [{}]", mevt1.getEventIDAsDottedString(), mevt2.getEventIDAsDottedString(), mdesc.getText());
153                        jmri.Turnout turnout = jmri.InstanceManager.turnoutManagerInstance()
154                                .provideTurnout(memo.getSystemPrefix() + "T" + mevt1.getEventIDAsDottedString() + ";" + mevt2.getEventIDAsDottedString());
155                        if (mdesc.getText().length() > 0) {
156                            turnout.setUserName(mdesc.getText());
157                        }
158                    }
159
160                    final JTextField mdesc = desc;
161                    final NamedEventIdTextField mevt1 = evt1;
162                    final NamedEventIdTextField mevt2 = evt2;
163                });
164                if (!haveButtons && buttonsCountTowardAllButtons) {
165                    log.debug("create Make Turnout/Sensor buttons, starting with {}", desc.getText());
166                    log.debug("events {} {}", evt1.getText(), evt2.getText());
167                    
168                    haveButtons = true;
169                    cdiPanel.addButtonToFooter(buttonForList(sensorButtonList, Bundle.getMessage("CdiPanelMakeAllSensors")));
170                    cdiPanel.addButtonToFooter(buttonForList(turnoutButtonList, Bundle.getMessage("CdiPanelMakeAllTurnouts")));
171                }
172                gpane = null;
173                evt1 = null;
174                evt2 = null;
175                desc = null;
176            }
177            
178            @Override
179            public void handleManualEnd(JPanel pane) {
180                installButtonsOnEndOfGroup(pane, false);
181            }
182
183            @Override
184            public void handleGroupPaneEnd(JPanel pane) {
185                if (gpane != null 
186                        && evt1 != null && !evt1.getText().isEmpty() 
187                        && evt2 != null && !evt2.getText().isEmpty()
188                        && desc != null && !desc.getText().isEmpty()) {
189                    log.debug("handleGroupPaneEnd for {}", desc.getText());
190                    installButtonsOnEndOfGroup(pane, true);
191                }
192            }
193
194            @Override
195            public JTextComponent handleEventIdTextField(EventIdTextField input) {
196                var field = new NamedEventIdTextField(memo);  // return our own constructed field
197
198                // What does this field do entry for?
199                if (evt1 == null) {
200                    evt1 = field;
201                } else if (evt2 == null) {
202                    evt2 = field;
203                } else {
204                    gpane = null;  // flag too many
205                }
206                return field;
207            }
208            
209            @Override
210            public JTextField handleStringValue(JTextField value) {
211                desc = value;
212                return value;
213            }
214
215            @Override
216            /**
217             * Make a sensor from a single Event ID.
218             * Set the user name from the CDI description (if available)
219             * {@inheritDoc}
220             */
221            public void makeSensor(String ev, String mdesc) {
222                log.debug("MakeSensor() invoked for {}",mdesc, new Exception("for traceback"));
223                jmri.Sensor sensor =
224                        jmri.InstanceManager.sensorManagerInstance()
225                                .provideSensor(memo.getSystemPrefix() + "S" + ev);
226                if (mdesc.length() > 0) {
227                    sensor.setUserName(mdesc);
228                }
229                log.debug("make sensor MS{} [{}]", ev, mdesc);
230            }
231
232            JPanel gpane = null;
233            JTextField desc = null;
234            jmri.jmrix.openlcb.swing.NamedEventIdTextField evt1 = null;
235            jmri.jmrix.openlcb.swing.NamedEventIdTextField evt2 = null;
236        };
237        ConfigRepresentation rep = iface.getConfigForNode(destNode);
238        rep.eventNameStore = memo.get(OlcbEventNameStore.class);
239        
240        cdiPanel.initComponents(rep, factory);
241
242        f.pack();
243        f.setVisible(true);
244    }
245
246    JButton buttonForList(final java.util.ArrayList<JButton> list, String label) {
247        JButton b = new JButton(label);
248        b.addActionListener(e -> {
249            int delay = 0; //milliseconds
250            for (final JButton b1 : list) {
251
252                ActionListener taskPerformer = new ActionListener() {
253                    @Override
254                    public void actionPerformed(ActionEvent evt) {
255                        target.doClick();
256                    }
257                    final JButton target = b1;
258                };
259                Timer t = new Timer(delay, taskPerformer);
260                t.setRepeats(false);
261                t.start();
262                delay = delay + 150;
263            }
264        });
265        return b;
266    }
267
268    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ClientActions.class);
269}