001package jmri.jmrit.beantable.signalmast;
002
003import java.awt.Color;
004import java.awt.Component;
005import java.awt.event.ActionEvent;
006
007import java.util.ArrayList;
008import java.util.Enumeration;
009import java.util.HashSet;
010import java.util.LinkedHashMap;
011import java.util.List;
012import java.util.Map;
013import java.util.Set;
014
015import javax.annotation.Nonnull;
016import javax.swing.BorderFactory;
017import javax.swing.BoxLayout;
018import javax.swing.JCheckBox;
019import javax.swing.JLabel;
020import javax.swing.JOptionPane;
021import javax.swing.JPanel;
022import javax.swing.JScrollPane;
023import javax.swing.border.TitledBorder;
024
025import org.openide.util.lookup.ServiceProvider;
026
027import jmri.InstanceManager;
028import jmri.SignalAppearanceMap;
029import jmri.SignalHead;
030import jmri.SignalHeadManager;
031import jmri.SignalMast;
032import jmri.SignalMastManager;
033import jmri.SignalSystem;
034import jmri.implementation.DefaultSignalAppearanceMap;
035import jmri.implementation.SignalHeadSignalMast;
036import jmri.swing.NamedBeanComboBox;
037import jmri.util.StringUtil;
038import jmri.util.javaworld.GridLayout2;
039
040/**
041 * A pane for configuring SignalHeadSignalMast objects.
042 *
043 * @see jmri.jmrit.beantable.signalmast.SignalMastAddPane
044 * @author Bob Jacobsen Copyright (C) 2018
045 * @since 4.11.2
046 */
047public class SignalHeadSignalMastAddPane extends SignalMastAddPane {
048
049    public SignalHeadSignalMastAddPane() {
050        initPane();
051    }
052    
053    final void initPane() {
054        
055        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
056        // lit/unlit controls
057        JPanel p = new JPanel();
058        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
059        p.add(new JLabel(Bundle.getMessage("AllowUnLitLabel") + ": "));
060        p.add(allowUnLit);
061        p.setAlignmentX(Component.LEFT_ALIGNMENT);
062        add(p);
063
064        TitledBorder border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black));
065        border.setTitle(Bundle.getMessage("MenuItemSignalTable")); // Signal Heads
066        signalHeadPanel.setBorder(border);
067        add(signalHeadPanel);
068
069        includeUsed.addActionListener((ActionEvent e) -> {
070            refreshHeadComboBox();
071        });
072
073        // disabled aspects controls
074        TitledBorder disableborder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black));
075        disableborder.setTitle(Bundle.getMessage("DisableAspectsLabel"));
076        JScrollPane disabledAspectsScroll = new JScrollPane(disabledAspectsPanel);
077        disabledAspectsScroll.setBorder(disableborder);
078        add(disabledAspectsScroll);
079    }
080
081    /** {@inheritDoc} */
082    @Override
083    @Nonnull public String getPaneName() {
084        return Bundle.getMessage("HeadCtlMast");
085    }
086
087
088    JPanel signalHeadPanel = new JPanel();
089    ArrayList<NamedBeanComboBox<SignalHead>> headList = new ArrayList<>(5);
090    private final JCheckBox includeUsed = new JCheckBox(Bundle.getMessage("IncludeUsedHeads"));
091
092    private final JCheckBox allowUnLit = new JCheckBox();
093
094    LinkedHashMap<String, JCheckBox> disabledAspects = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT);
095    JPanel disabledAspectsPanel = new JPanel();
096
097    SignalHeadSignalMast currentMast = null;
098    Set<SignalHead> alreadyUsed = new HashSet<>();
099    DefaultSignalAppearanceMap map;
100
101    /** {@inheritDoc} */
102    @Override
103    public void setAspectNames(@Nonnull SignalAppearanceMap newMap, @Nonnull SignalSystem sigSystem) {
104        log.debug("setAspectNames(...)");
105
106        map = (DefaultSignalAppearanceMap)newMap;
107
108        int count = map.getAspectSettings(map.getAspects().nextElement()).length;
109        log.trace(" head count is {}", count);
110
111        Enumeration<String> aspects = map.getAspects();
112
113        headList = new ArrayList<>(count);
114
115        signalHeadPanel.removeAll();
116        for (int i = 0; i < count; i++) {
117            NamedBeanComboBox<SignalHead> head = new NamedBeanComboBox<>(InstanceManager.getDefault(SignalHeadManager.class));
118            head.setExcludedItems(alreadyUsed);
119            headList.add(head);
120            signalHeadPanel.add(head);
121        }
122        signalHeadPanel.add(includeUsed);
123
124        signalHeadPanel.setLayout(new GridLayout2(0, 1)); // 0 means enough
125        signalHeadPanel.revalidate();
126
127        disabledAspects = new LinkedHashMap<>(10);
128        disabledAspectsPanel.removeAll();
129        while (aspects.hasMoreElements()) {
130            String aspect = aspects.nextElement();
131            JCheckBox disabled = new JCheckBox(aspect);
132            disabledAspects.put(aspect, disabled);
133        }
134
135        for (Map.Entry<String, JCheckBox> entry : disabledAspects.entrySet()) {
136            disabledAspectsPanel.add(entry.getValue());
137        }
138
139        disabledAspectsPanel.setLayout(new GridLayout2(0, 1)); // 0 means enough
140        disabledAspectsPanel.revalidate();
141    }
142
143    /** {@inheritDoc} */
144    @Override
145    public boolean canHandleMast(@Nonnull SignalMast mast) {
146        return mast instanceof SignalHeadSignalMast;
147    }
148
149    /** {@inheritDoc} */
150    @Override
151    public void setMast(SignalMast mast) {
152        log.debug("setMast({})", mast);
153        if (mast == null) {
154            currentMast = null;
155            return;
156        }
157
158        if (! (mast instanceof SignalHeadSignalMast) ) {
159            log.error("mast was wrong type: {} {}", mast.getSystemName(), mast.getClass().getName());
160            return;
161        }
162
163        currentMast = (SignalHeadSignalMast) mast;
164
165        // can't actually edit the heads in this kind of mast
166        int count = map.getAspectSettings(map.getAspects().nextElement()).length;
167        log.trace(" head count is {}", count);
168        signalHeadPanel.removeAll();
169        signalHeadPanel.setLayout(new GridLayout2(count + 1, 1));
170        for (int i = 0; i < count; i++) {
171            NamedBeanComboBox<SignalHead> head = new NamedBeanComboBox<>(InstanceManager.getDefault(SignalHeadManager.class));
172            head.setExcludedItems(alreadyUsed);
173            headList.add(head);
174
175            head.setEnabled(false);
176            head.setSelectedItem(currentMast.getHeadsUsed().get(i).getBean()); // must match NamedBeanComboBox above
177            signalHeadPanel.add(head);
178        }
179        signalHeadPanel.add(includeUsed);
180        signalHeadPanel.revalidate();
181
182        List<String> disabled = currentMast.getDisabledAspects();
183        if (disabled != null) {
184            for (String aspect : disabled) {
185                if (disabledAspects.containsKey(aspect)) {
186                    disabledAspects.get(aspect).setSelected(true);
187                }
188            }
189        }
190
191        allowUnLit.setSelected(currentMast.allowUnLit());
192
193        log.trace("setMast {} end", mast);
194    }
195
196    /** {@inheritDoc} */
197    @Override
198    public boolean createMast(@Nonnull String sigsysname, @Nonnull String mastname, @Nonnull String username) {
199        log.debug("createMast({},{})", sigsysname, mastname);
200        String name;
201        if (currentMast == null) {
202                StringBuilder build = new StringBuilder();
203                build.append("IF$shsm:").append(sigsysname).append(":").append(mastname.substring(11, mastname.length() - 4));
204                for (NamedBeanComboBox<SignalHead> head : headList) {
205                    if (head != null && head.getSelectedItemDisplayName() != null) {
206                        build.append("(").append(StringUtil.parenQuote(head.getSelectedItemDisplayName())).append(")");
207                    }
208                }
209                name = build.toString();
210                log.debug("add signal: {}", name);
211
212                // see if it exists (remember, we're not handling a current mast)
213                SignalMast m = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(name);
214                if (m != null) {
215                    JOptionPane.showMessageDialog(null, java.text.MessageFormat.format(Bundle.getMessage("DuplicateMast"),
216                            new Object[]{m.getDisplayName()}), Bundle.getMessage("DuplicateMastTitle"), JOptionPane.INFORMATION_MESSAGE);
217                    return false;
218                }
219                try {
220                    // now create it
221                    currentMast = (SignalHeadSignalMast)InstanceManager.getDefault(SignalMastManager.class).provideSignalMast(name);
222                } catch (IllegalArgumentException ex) {
223                    // user input no good
224                    handleCreateException(name);
225                    return false; // without creating
226                }
227        }
228
229        // load a new or existing mast
230        for (Map.Entry<String, JCheckBox> entry : disabledAspects.entrySet()) {
231            if (entry.getValue().isSelected()) {
232                currentMast.setAspectDisabled(entry.getKey());
233            } else {
234                currentMast.setAspectEnabled(entry.getKey());
235            }
236        }
237
238        // heads are attached via the system name
239        if (!username.isEmpty()) {
240            currentMast.setUserName(username);
241        }
242
243        currentMast.setAllowUnLit(allowUnLit.isSelected());
244
245        return true;
246    }
247
248    protected void refreshHeadComboBox() {
249        log.trace("refreshHeadComboBox");
250        if (includeUsed.isSelected()) {
251            alreadyUsed = new HashSet<>();
252        } else {
253            List<SignalHead> alreadyUsedHeads = SignalHeadSignalMast.getSignalHeadsUsed();
254            alreadyUsed = new HashSet<>();
255            log.trace("   found {}", alreadyUsedHeads.size());
256            for (SignalHead head : alreadyUsedHeads) {
257                alreadyUsed.add(head);
258            }
259        }
260
261        for (NamedBeanComboBox<SignalHead> head : headList) {
262            head.setExcludedItems(alreadyUsed);
263        }
264    }
265
266    void handleCreateException(String sysName) {
267        JOptionPane.showMessageDialog(null,
268                Bundle.getMessage("ErrorSignalMastAddFailed", sysName) + "\n" + Bundle.getMessage("ErrorAddFailedCheck"),
269                Bundle.getMessage("ErrorTitle"),
270                JOptionPane.ERROR_MESSAGE);
271    }
272
273    @ServiceProvider(service = SignalMastAddPane.SignalMastAddPaneProvider.class)
274    static public class SignalMastAddPaneProvider extends SignalMastAddPane.SignalMastAddPaneProvider {
275        /** {@inheritDoc} */
276        @Override
277        @Nonnull public String getPaneName() {
278            return Bundle.getMessage("HeadCtlMast");
279        }
280        /** {@inheritDoc} */
281        @Override
282        @Nonnull public SignalMastAddPane getNewPane() {
283            return new SignalHeadSignalMastAddPane();
284        }
285    }
286
287    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SignalHeadSignalMastAddPane.class);
288
289}