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