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            @Override
120            public void handleGroupPaneEnd(JPanel pane) {
121                if (gpane != null && evt1 != null && evt2 != null && desc != null) {
122                    JPanel p = new JPanel();
123                    p.setLayout(new WrapLayout());
124                    p.setAlignmentX(-1.0f);
125                    pane.add(p);
126                    JButton button = new JButton(Bundle.getMessage("CdiPanelMakeSensor"));
127                    p.add(button);
128                    sensorButtonList.add(button);
129                    button.addActionListener(new java.awt.event.ActionListener() {
130                        @Override
131                        public void actionPerformed(java.awt.event.ActionEvent e) {
132                            jmri.Sensor sensor = jmri.InstanceManager.sensorManagerInstance()
133                                    .provideSensor(memo.getSystemPrefix() + "S" + mevt1.getText() + ";" + mevt2.getText());
134                            if (mdesc.getText().length() > 0) {
135                                sensor.setUserName(mdesc.getText());
136                            }
137                            log.info("make sensor MS{};{} [{}]", mevt1.getText(), mevt2.getText(), mdesc.getText());
138                        }
139
140                        final JTextField mdesc = desc;
141                        final JTextComponent mevt1 = evt1;
142                        final JTextComponent mevt2 = evt2;
143                    });
144                    button = new JButton(Bundle.getMessage("CdiPanelMakeTurnout"));
145                    p.add(button);
146                    turnoutButtonList.add(button);
147                    button.addActionListener(new java.awt.event.ActionListener() {
148                        @Override
149                        public void actionPerformed(java.awt.event.ActionEvent e) {
150                            jmri.Turnout turnout = jmri.InstanceManager.turnoutManagerInstance()
151                                    .provideTurnout(memo.getSystemPrefix() + "T" + mevt1.getText() + ";" + mevt2.getText());
152                            if (mdesc.getText().length() > 0) {
153                                turnout.setUserName(mdesc.getText());
154                            }
155                            log.info("make turnout MT{};{} [{}]", mevt1.getText(), mevt2.getText(), mdesc.getText());
156                        }
157
158                        final JTextField mdesc = desc;
159                        final JTextComponent mevt1 = evt1;
160                        final JTextComponent mevt2 = evt2;
161                    });
162                    if (!haveButtons) {
163                        haveButtons = true;
164                        cdiPanel.addButtonToFooter(buttonForList(sensorButtonList, Bundle.getMessage("CdiPanelMakeAllSensors")));
165                        cdiPanel.addButtonToFooter(buttonForList(turnoutButtonList, Bundle.getMessage("CdiPanelMakeAllTurnouts")));
166                    }
167                    gpane = null;
168                    evt1 = null;
169                    evt2 = null;
170                    desc = null;
171                }
172            }
173
174            @Override
175            public JTextComponent handleEventIdTextField(EventIdTextField input) {
176                var field = new NamedEventIdTextField(memo);  // return our own constructed field
177
178                // What does this field do entry for?
179                if (evt1 == null) {
180                    evt1 = field;
181                } else if (evt2 == null) {
182                    evt2 = field;
183                } else {
184                    gpane = null;  // flag too many
185                }
186                return field;
187            }
188            
189            @Override
190            public JTextField handleStringValue(JTextField value) {
191                desc = value;
192                return value;
193            }
194
195            @Override
196            /**
197             * Make a sensor from a single Event ID.
198             * Set the user name from the CDI description (if available)
199             * {@inheritDoc}
200             */
201            public void makeSensor(String ev, String mdesc) {
202                jmri.Sensor sensor =
203                        jmri.InstanceManager.sensorManagerInstance()
204                                .provideSensor(memo.getSystemPrefix() + "S" + ev);
205                if (mdesc.length() > 0) {
206                    sensor.setUserName(mdesc);
207                }
208                log.debug("make sensor MS{} [{}]", ev, mdesc);
209            }
210
211            JPanel gpane = null;
212            JTextField desc = null;
213            JTextComponent evt1 = null;
214            JTextComponent evt2 = null;
215        };
216        ConfigRepresentation rep = iface.getConfigForNode(destNode);
217        rep.eventNameStore = memo.get(OlcbEventNameStore.class);
218        
219        cdiPanel.initComponents(rep, factory);
220
221        f.pack();
222        f.setVisible(true);
223    }
224
225    JButton buttonForList(final java.util.ArrayList<JButton> list, String label) {
226        JButton b = new JButton(label);
227        b.addActionListener(e -> {
228            int delay = 0; //milliseconds
229            for (final JButton b1 : list) {
230
231                ActionListener taskPerformer = new ActionListener() {
232                    @Override
233                    public void actionPerformed(ActionEvent evt) {
234                        target.doClick();
235                    }
236                    final JButton target = b1;
237                };
238                Timer t = new Timer(delay, taskPerformer);
239                t.setRepeats(false);
240                t.start();
241                delay = delay + 150;
242            }
243        });
244        return b;
245    }
246
247    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ClientActions.class);
248}