001package jmri.jmrit.display.controlPanelEditor;
002
003import java.awt.Dimension;
004import java.awt.FlowLayout;
005import java.awt.datatransfer.DataFlavor;
006import java.awt.datatransfer.UnsupportedFlavorException;
007import java.awt.dnd.DragSourceDropEvent;
008import java.awt.event.ActionEvent;
009import java.io.IOException;
010import java.util.ArrayList;
011import java.util.Enumeration;
012import java.util.HashMap;
013import java.util.List;
014import java.util.Map.Entry;
015
016import javax.annotation.Nonnull;
017import javax.swing.BorderFactory;
018import javax.swing.Box;
019import javax.swing.BoxLayout;
020import javax.swing.JButton;
021import javax.swing.JComponent;
022import javax.swing.JLabel;
023import javax.swing.JPanel;
024import javax.swing.JScrollPane;
025import javax.swing.JTextField;
026import javax.swing.event.ListSelectionEvent;
027import javax.swing.event.ListSelectionListener;
028
029import jmri.InstanceManager;
030import jmri.NamedBean;
031import jmri.SignalAppearanceMap;
032import jmri.SignalHead;
033import jmri.SignalMast;
034import jmri.SignalHeadManager;
035import jmri.SignalMastManager;
036import jmri.NamedBean.DisplayOptions;
037import jmri.jmrit.beantable.AbstractTableAction;
038import jmri.jmrit.beantable.BeanTableFrame;
039import jmri.jmrit.beantable.SignalHeadTableAction;
040import jmri.jmrit.beantable.SignalMastTableAction;
041import jmri.jmrit.catalog.DragJLabel;
042import jmri.jmrit.catalog.NamedIcon;
043import jmri.jmrit.display.Editor;
044import jmri.jmrit.display.PositionableIcon;
045import jmri.jmrit.display.SignalHeadIcon;
046import jmri.jmrit.display.SignalMastIcon;
047import jmri.jmrit.display.palette.ItemPalette;
048import jmri.jmrit.logix.OBlock;
049import jmri.jmrit.logix.Portal;
050import jmri.jmrit.picker.PickListModel;
051import jmri.util.swing.JmriJOptionPane;
052
053/**
054 *
055 * @author Pete Cressman Copyright: Copyright (c) 2019
056 *
057 */
058public class EditSignalFrame extends EditFrame {
059
060    private PortalIcon _portalIcon;
061
062    private JTextField _mastName;
063    private PortalList _portalList;
064    private SignalList _signalList;
065    private LengthPanel _lengthPanel;
066    private Portal _currentPortal;
067    OpenPickListButton<SignalMast> _pickMast;
068    OpenPickListButton<SignalHead> _pickHead;
069    AbstractTableAction<SignalMast> _mastTableAction;
070    AbstractTableAction<SignalHead> _headTableAction;
071    JPanel _dndPanel;
072    private IconDragJLabel _dragLabel;
073
074    public EditSignalFrame(String title, CircuitBuilder parent, OBlock block) {
075        super(title, parent, block);
076        checkCircuitIcons("BlockSignal");
077        pack();
078    }
079
080    @Override
081    protected JPanel makeContentPanel() {
082        JPanel signalPanel = new JPanel();
083        signalPanel.setLayout(new BoxLayout(signalPanel, BoxLayout.Y_AXIS));
084
085        JPanel panel = new JPanel();
086        panel.add(new JLabel(Bundle.getMessage("PortalTitle", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME))));
087        signalPanel.add(panel);
088
089        _portalList = new PortalList(_homeBlock, this);
090        _portalList.addListSelectionListener(new PortalListListener(this));
091        signalPanel.add(new JScrollPane(_portalList));
092        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
093
094        panel = new JPanel();
095        panel.add(new JLabel(Bundle.getMessage("SignalTitle", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME))));
096        signalPanel.add(panel);
097
098        _signalList = new SignalList(_homeBlock, this);
099        _signalList.addListSelectionListener(new SignalListListener(this));
100        signalPanel.add(new JScrollPane(_signalList));
101        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
102
103        JButton clearButton = new JButton(Bundle.getMessage("buttonClearSelection"));
104        clearButton.addActionListener(a -> {
105            _portalList.clearSelection();
106            _signalList.clearSelection();
107            _parent._editor.highlight(null);
108            _mastName.setText(null);
109        });
110
111        panel = new JPanel();
112        panel.add(clearButton);
113        signalPanel.add(panel);
114        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
115
116        JPanel framingPanel = new JPanel();
117        JPanel mastConfigPanel = new JPanel();
118        // set border to group items in UI
119        mastConfigPanel.setBorder(BorderFactory.createEtchedBorder());
120        mastConfigPanel.setLayout(new BoxLayout(mastConfigPanel, BoxLayout.Y_AXIS));
121
122        panel = new JPanel();
123        _mastName = new JTextField();
124        panel.add(CircuitBuilder.makeTextBoxPanel(false, _mastName, "mastName", true, null));
125        _mastName.setPreferredSize(new Dimension(300, _mastName.getPreferredSize().height));
126        _mastName.setToolTipText(Bundle.getMessage("ToolTipMastName", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME)));
127        mastConfigPanel.add(panel);
128
129        _lengthPanel = new LengthPanel(_homeBlock, LengthPanel.ENTRANCE_SPACE, "OffsetToolTip");
130        _lengthPanel.changeUnits();
131        mastConfigPanel.add(_lengthPanel);
132
133        JPanel buttonPanel = new JPanel();
134        JButton addButton = new JButton(Bundle.getMessage("ButtonAddMast"));
135        addButton.addActionListener((ActionEvent a) -> addMast());
136        addButton.setToolTipText(Bundle.getMessage("ToolTipAddMast", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME)));
137        buttonPanel.add(addButton);
138/*
139        JButton button = new JButton(Bundle.getMessage("buttonChangeName"));
140        button.addActionListener((ActionEvent a) -> changeName(null));
141        button.setToolTipText(Bundle.getMessage("ToolTipChangeName", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME)));
142        panel.add(button);*/
143
144        JButton buttonRemove = new JButton(Bundle.getMessage("ButtonRemoveMast"));
145        buttonRemove.addActionListener((ActionEvent a) -> removeMast());
146        buttonRemove.setToolTipText(Bundle.getMessage("ToolTipRemoveMast", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME)));
147        buttonPanel.add(buttonRemove);
148
149        mastConfigPanel.add(buttonPanel);
150        // border up to here
151        framingPanel.add(mastConfigPanel);
152        signalPanel.add(framingPanel);
153
154        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE));
155
156        panel = new JPanel();
157        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
158        JLabel l = new JLabel(Bundle.getMessage("addSignalConfig"));
159        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
160        panel.add(l);
161        l = new JLabel(Bundle.getMessage("selectSignalMast"));
162        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
163        panel.add(l);
164        l = new JLabel(Bundle.getMessage("pressConfigure", Bundle.getMessage("ButtonAddMast")));
165        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
166        panel.add(l);
167        panel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
168        l = new JLabel(Bundle.getMessage("addSignalIcon"));
169        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
170        panel.add(l);
171        l = new JLabel(Bundle.getMessage("positionMast"));
172        l.setAlignmentX(JComponent.LEFT_ALIGNMENT);
173        panel.add(l);
174        JPanel p = new JPanel();
175        p.add(panel);
176        signalPanel.add(p);
177        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
178
179        panel = new JPanel();
180        l = new JLabel(Bundle.getMessage("recommendMasts"));
181        panel.add(l);
182        signalPanel.add(panel);
183        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE / 2));
184
185        String[] blurbLines = {Bundle.getMessage("DragMast", Bundle.getMessage("mastName"))};
186        
187        panel = new JPanel();
188        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
189        _pickMast = new OpenPickListButton<>(blurbLines, PickListModel.signalMastPickModelInstance(), 
190                this, Bundle.getMessage("OpenPicklist", Bundle.getMessage("BeanNameSignalMast")));
191        _mastTableAction = new SignalMastTableAction(Bundle.getMessage("ButtonCreateMast"));
192        p = new JPanel();
193        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
194        p.add(_pickMast.getButtonPanel());
195        JPanel pp = new JPanel();
196        pp.setLayout(new FlowLayout());
197        JButton buttonCreate = new JButton(Bundle.getMessage("ButtonCreateMast"));
198        buttonCreate.addActionListener(_mastTableAction);
199        buttonCreate.setToolTipText(Bundle.getMessage("ToolTipAddToTable"));
200        pp.add(buttonCreate);
201        p.add(pp);
202        panel.add(p);
203        
204        _pickHead = new OpenPickListButton<>(blurbLines, PickListModel.signalHeadPickModelInstance(),
205                this, Bundle.getMessage("OpenPicklist", Bundle.getMessage("BeanNameSignalHead")));
206        _headTableAction = new SignalHeadTableAction(Bundle.getMessage("ButtonCreateHead"));
207        p = new JPanel();
208        p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
209        p.add(_pickHead.getButtonPanel());
210        pp = new JPanel();
211        pp.setLayout(new FlowLayout());
212        buttonCreate = new JButton(Bundle.getMessage("ButtonCreateHead"));
213        buttonCreate.addActionListener(_headTableAction);
214        buttonCreate.setToolTipText(Bundle.getMessage("ToolTipAddToTable"));
215        pp.add(buttonCreate);
216        p.add(pp);
217        panel.add(p);
218        signalPanel.add(panel);
219        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE));
220
221        panel = new JPanel();
222        l = new JLabel(Bundle.getMessage("modifySignal"));
223        panel.add(l);
224        signalPanel.add(panel);
225        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE));
226
227        _dndPanel = makeDndIconPanel();
228        signalPanel.add(_dndPanel);
229        signalPanel.add(Box.createVerticalStrut(STRUT_SIZE));
230        signalPanel.add(makeDoneButtonPanel());
231        return signalPanel;
232    }
233
234    protected void setSelected(PositionableIcon icon) {
235        if (!canEdit()) {
236            return;
237        }
238        NamedBean mast = null;
239        Portal portal = null;
240        if (icon instanceof PortalIcon) {
241            portal = ((PortalIcon)icon).getPortal();
242        } else if (icon instanceof SignalMastIcon) {
243            mast = ((SignalMastIcon)icon).getSignalMast();
244        } else if (icon instanceof SignalHeadIcon) {
245            mast = ((SignalHeadIcon)icon).getSignalHead();
246        } else {
247            return;
248        }
249        if (log.isDebugEnabled()) {
250            log.debug("setSelected portal= \"{}\" mast ={}", (portal!=null?portal.getName():"null"),(mast!=null?mast.getDisplayName():"null"));
251        }
252        _portalIcon = null;
253        if (portal != null) {
254            mast = portal.getSignalProtectingBlock(_homeBlock);
255            if (mast !=null) {
256                setMastNameAndIcon(mast, portal);
257            }
258        } else if (mast !=null) {
259            portal =_parent.getSignalPortal(mast);
260            if (portal != null) {
261                OBlock protectedBlock = portal.getProtectedBlock(mast);
262                if (_homeBlock.equals(protectedBlock)) {
263                    setMastNameAndIcon(mast, portal);
264                }
265            }
266            _portalList.setSelected(portal);
267            _signalList.setSelected(portal);
268            _parent._editor.highlight(icon);
269        }
270    }
271    
272    private void setMastNameAndIcon(NamedBean mast, Portal portal) {
273        _mastName.setText(mast.getDisplayName(DisplayOptions.DISPLAYNAME));
274        _parent._editor.highlight(null);
275        List<PositionableIcon> siArray = _parent.getSignalIconMap(mast);
276        for (PositionableIcon si : siArray) {
277            _parent._editor.highlight(si);
278        }
279        List<PortalIcon> piArray = _parent.getPortalIcons(portal);
280        for (PortalIcon pi : piArray) {
281            _parent._editor.highlight(pi);
282        }
283    }
284
285    /**
286     * *********************** end setup *************************
287     */
288    
289    class PortalListListener implements ListSelectionListener {
290        EditFrame _frame;
291        PortalListListener(EditFrame parent) {
292            _frame = parent;
293        }
294        @Override
295        public void valueChanged(ListSelectionEvent e) {
296            Portal portal = _portalList.getSelectedValue();
297            if (log.isDebugEnabled()) {
298                log.debug("PortalList: valueChanged: portal = {}, _currentPortal = {}", (portal==null?"null":portal.getName()), 
299                        (_currentPortal==null?"null":_currentPortal.getName()));
300            }
301            NamedBean mast = null;
302            if (portal != null) {
303                mast = portal.getSignalProtectingBlock(_homeBlock);
304                if (!portal.equals(_currentPortal)) {
305                    String msg = checkMastForSave();
306                    if (msg.length() > 0) {
307                        StringBuffer sb = new StringBuffer(msg);
308                        NamedBean bean = getSignal();
309//                        if (bean != null) {
310                            sb.append("\n");
311                            sb.append(Bundle.getMessage("saveChanges"));
312//                        }
313                        int answer = JmriJOptionPane.showConfirmDialog(_frame, sb.toString(), Bundle.getMessage("configureSignal"),
314                                JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
315                        if (answer == JmriJOptionPane.YES_OPTION) {
316                            if (bean != null) {
317                                addMast(_currentPortal, bean);                            
318//                            } else {
319//                                changeName(_currentPortal);
320                            }
321                            return;
322                        }
323                    }
324                }
325            }
326            if (_portalIcon == null) {
327                _parent._editor.highlight(null);
328            }
329            _currentPortal = portal;
330            if (portal != null) {
331                _lengthPanel.setLength(portal.getEntranceSpaceForBlock(_homeBlock));
332               List<PortalIcon> piArray = _parent.getPortalIcons(portal);
333               if (!piArray.isEmpty()) {
334                   _portalIcon = piArray.get(0);
335               }
336            }
337            
338            if (mast != null) {
339                setMastNameAndIcon(mast, portal);
340            } else {
341                _parent._editor.highlight(null);
342                _mastName.setText(null);
343                if (portal != null) {
344                    _parent._editor.highlight(_portalIcon);
345                }
346                _lengthPanel.setLength(0);
347            }
348            _signalList.setSelected(portal);
349            setDragIcon(mast);
350        }
351        
352    }
353
354    class SignalListListener  implements ListSelectionListener {
355        EditFrame _frame;
356        SignalListListener(EditFrame parent) {
357            _frame = parent;
358        }
359        @Override
360        public void valueChanged(ListSelectionEvent e) {
361            SignalPair sp = _signalList.getSelectedValue();
362            if (log.isDebugEnabled()) {
363                if (sp != null) {
364                    log.debug("SignalList: valueChanged: portal = {}, signal = {}", 
365                            sp._portal.getName(), sp._signal.getDisplayName());
366                } else {
367                    log.debug("SignalList: valueChanged: signalPair null"); 
368                }
369            }
370            NamedBean signal;
371            if (sp != null) {
372                _portalList.setSelected(sp._portal);
373                signal = sp._signal;
374            } else {
375                signal = null;
376            }
377            setDragIcon(signal);
378        }
379    }
380
381    // Called from ButtonAddMast
382    private boolean replaceQuestion(@Nonnull NamedBean mast, @Nonnull Portal homePortal) {
383        StringBuffer sb = new StringBuffer();
384        Portal portal = _parent.getSignalPortal(mast);
385        OBlock blk = null;
386        if (portal != null) {
387            blk = portal.getProtectedBlock(mast);
388            if (blk != null && !blk.equals(_homeBlock)) {
389                sb.append(Bundle.getMessage("mastProtectsPortal", 
390                        mast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME), 
391                        blk.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME),
392                        portal.getName()));
393                sb.append("\n");
394            }
395        }
396        NamedBean homeMast = homePortal.getSignalProtectingBlock(_homeBlock);
397        String mastName = mast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
398        String homeName = _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
399        if (homeMast != null) {
400            if (homeMast.equals(mast)) {
401                // no changes needed except for length.  So do it now and skip the rest of AddMast()
402                homePortal.setEntranceSpaceForBlock(_homeBlock, _lengthPanel.getLength());
403                return false;
404            } else {
405                String homeMastName = homeMast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
406                sb.append(Bundle.getMessage("mastProtectsPortal", homeMastName, homeName, homePortal.getName()));
407                sb.append("\n");
408                sb.append(Bundle.getMessage("replaceSignalMast", homeMast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME), 
409                                mastName, homePortal.getName()));
410            }
411        } else if (sb.length() > 0) {
412            sb.append(Bundle.getMessage("noMast", homePortal.getName(), homeName));
413            sb.append("\n");                    
414            sb.append(Bundle.getMessage("setSignal", mastName));
415            sb.append("\n");                    
416            sb.append(Bundle.getMessage("attachMast", mastName, homeName, homePortal.getName()));
417        }
418        if (sb.length() > 0) {
419            int answer = JmriJOptionPane.showConfirmDialog(this,  sb.toString(),
420                    Bundle.getMessage("configureSignal"), JmriJOptionPane.YES_NO_OPTION, JmriJOptionPane.QUESTION_MESSAGE);
421            if (answer != JmriJOptionPane.YES_OPTION) {
422                return false;   // Skip the rest
423            }
424        }
425        if (homeMast != null) {
426            homePortal.setProtectSignal(null, 0, blk);
427        }
428        _parent.putSignalPortal(mast, null);
429        return true;
430    }
431
432    private NamedBean getSignal() {
433        String name = _mastName.getText();
434        if (name.trim().length() == 0) {
435            return null;
436        }
437        NamedBean signal = InstanceManager.getDefault(SignalMastManager.class).getSignalMast(name);
438        if (signal == null) {
439            signal = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(name);
440        }
441        return signal;
442    }
443
444    // Called from: 
445    // ConfigureButton -    addMast(portal, mast); portal from portal list, mast from name field
446    private void addMast(@Nonnull Portal portal, @Nonnull NamedBean newMast) {
447        if (log.isDebugEnabled()) {
448            log.debug("addMast \"{}\"", newMast.getDisplayName());
449        }
450        if (newMast instanceof SignalMast) {
451            SignalMast mast = (SignalMast)newMast;
452            if (mast.getAspect() == null) {
453                mast.setAspect(mast.getValidAspects().get(0));
454            }
455        }
456        portal.setProtectSignal(newMast, _lengthPanel.getLength(), _homeBlock);
457        _parent.putSignalPortal(newMast, portal);
458        setDragIcon(newMast);
459    }
460
461    private boolean addMast() {
462        Portal portal = _portalList.getSelectedValue();
463        String msg = null;
464        if (portal != null) {
465            NamedBean signal = getSignal();
466            if (signal != null) {
467                if (replaceQuestion(signal, portal)) {
468                    addMast(portal, signal);
469                }
470            } else {
471                String name = _mastName.getText().trim();
472                if ( name.length()==0) {
473                    msg = Bundle.getMessage("selectSignalMast");
474                } else {
475                    msg = Bundle.getMessage("NotFound", name);
476                }
477            }
478        } else {
479            msg = Bundle.getMessage("selectPortalProtection", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME));
480        }
481        if (msg != null) {
482            JmriJOptionPane.showMessageDialog(this, msg,
483                    Bundle.getMessage("configureSignal"), JmriJOptionPane.INFORMATION_MESSAGE);
484            return false;
485        }
486        return true;
487    }
488/*
489    private void changeName(Portal portal) {
490        if (portal == null) {
491            portal = _portalList.getSelectedValue();
492        }
493        String msg = null;
494        if (portal != null) {
495            NamedBean signal = portal.getSignalProtectingBlock(_homeBlock);
496            if (signal != null) {
497                String name = _mastName.getText().trim();
498                if ( name.length()==0) {
499                    msg = Bundle.getMessage("selectSignalMast", Bundle.getMessage("mastName"));
500                } else {
501                    NamedBean nb;
502                    if (signal instanceof SignalMast) {
503                        nb = InstanceManager.getDefault(SignalMastManager.class).getByUserName(name);
504                    } else {
505                        nb = InstanceManager.getDefault(SignalHeadManager.class).getByUserName(name);
506                    }
507                    if (nb != null) {
508                        msg = Bundle.getMessage("signalExists", name, signal.getDisplayName());
509                    } else {
510                        // TODO!!!! patch up references to name change!
511                        signal.setUserName(name);
512                    }
513                }
514            } else {
515                msg = Bundle.getMessage("noMast", portal.getName(), 
516                        _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME));
517            }
518        } else {
519            msg = Bundle.getMessage("selectPortalProtection", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME));
520        }
521        if (msg != null) {
522            JmriJOptionPane.showMessageDialog(this, msg,
523                    Bundle.getMessage("configureSignal"), JmriJOptionPane.INFORMATION_MESSAGE);
524        }
525    }*/
526
527    private void removeMast() {
528        Portal portal = _portalList.getSelectedValue();
529        NamedBean oldMast = null;
530        if (portal != null) {
531            oldMast = portal.getSignalProtectingBlock(_homeBlock);
532        } else {
533            _mastName.setText(null);
534            return;
535        }
536        if (oldMast != null) {
537            _mastName.setText(null);    // do before portal triggers propertyChange
538            portal.setProtectSignal(null, 0, _homeBlock);
539            _parent.putSignalPortal(oldMast, null);
540        } else {
541            JmriJOptionPane.showMessageDialog(this, 
542                    Bundle.getMessage("noPortalProtection", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME),
543                            portal.getName()),
544                    Bundle.getMessage("configureSignal"), JmriJOptionPane.INFORMATION_MESSAGE);
545        }
546        _mastName.setText(null);
547    }
548
549    /**
550     * Check for questions about configuring this signal 
551     * @return message of any concerns. But ALWAYS non-null.
552     */
553    private String checkMastForSave() {
554        if (_currentPortal == null) {
555            return "";
556        }
557        StringBuffer sb = new StringBuffer();
558        NamedBean selectedMast = getSignal();
559        NamedBean currentMast = _currentPortal.getSignalProtectingBlock(_homeBlock);
560
561        if (selectedMast == null) {
562            if (currentMast != null) {
563                String curMastName = currentMast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
564                String curPortalName = _currentPortal.getName();
565                sb.append(Bundle.getMessage("mastProtectsPortal", curMastName,
566                        _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME), curPortalName));
567                sb.append("\n");                    
568                String name = _mastName.getText();
569                if (name.trim().length() > 0) {
570                    sb.append(Bundle.getMessage("NotFound", name));
571                    sb.append("\n");
572                    String type;
573                    if (currentMast instanceof SignalMast) {
574                        type = Bundle.getMessage("BeanNameSignalMast");
575                    } else {
576                        type = Bundle.getMessage("BeanNameSignalHead");
577                    }
578                    sb.append(Bundle.getMessage("changeOrCancel", curMastName, name, type));
579                } else {
580                    sb.append(Bundle.getMessage("removeSignalMast", curMastName, curPortalName));
581                }
582            }
583        } else {
584            String selMastName = selectedMast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
585            String curPortalName = _currentPortal.getName();
586            String homeName = _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
587            if (!selectedMast.equals(currentMast)) {
588                if (currentMast != null) {
589                    Portal selectedPortal = _parent.getSignalPortal(selectedMast);
590                    if (selectedPortal != null) {
591                        OBlock blk = selectedPortal.getProtectedBlock(selectedMast);
592                        if (blk != null) {
593                            sb.append(Bundle.getMessage("mastProtectsPortal", selMastName,
594                                    blk.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME), selectedPortal.getName()));
595                            sb.append("\n");
596                        }
597                    }
598                    String curMastName = currentMast.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME);
599                    sb.append(Bundle.getMessage("mastProtectsPortal", curMastName, homeName, curPortalName));
600                    sb.append("\n");                    
601                    sb.append(Bundle.getMessage("replaceSignalMast", curMastName, selMastName, curPortalName));
602                    sb.append("\n");
603                    if (_lengthPanel.isChanged(_currentPortal.getEntranceSpaceForBlock(_homeBlock))) {
604                        sb.append(Bundle.getMessage("spaceChanged", selMastName, _currentPortal.getName()));
605                    }
606                } else {
607                    sb.append(Bundle.getMessage("noMast", curPortalName, homeName));
608                    sb.append("\n");                    
609                    sb.append(Bundle.getMessage("setSignal", selMastName));
610                    sb.append("\n");                    
611                    sb.append(Bundle.getMessage("attachMast", selMastName,
612                            homeName, _currentPortal.getName()));
613                }
614            }
615        }
616        return sb.toString();
617    }
618
619    @Override
620    protected void closingEvent(boolean close) {
621        StringBuffer sb = new StringBuffer();
622        String msg = _parent.checkForPortals(_homeBlock, "ItemTypeSignalMast");
623        if (msg.length() > 0) {
624            sb.append(msg);
625            sb.append("\n");
626        }
627        msg = checkMastForSave();
628        if  (msg.length() > 0) {
629            sb.append(msg);
630            sb.append("\n");
631        }
632        closingEvent(close, sb.toString());
633        if (_pickMast != null) {
634            _pickMast.closePickList();
635        }
636        if (_pickHead != null) {
637            _pickHead.closePickList();
638        }
639        if (_mastTableAction != null) {
640            _mastTableAction.dispose();
641            BeanTableFrame<SignalMast> frame = _mastTableAction.getFrame();
642            if (frame != null) {
643                frame.dispose();
644            }
645        }
646        if (_headTableAction != null) {
647            _headTableAction.dispose();
648            BeanTableFrame<SignalHead> frame = _headTableAction.getFrame();
649            if (frame != null) {
650                frame.dispose();
651            }
652        }
653    }
654
655    private void setDragIcon(NamedBean signal) {
656        NamedIcon icon = null;
657        if (signal != null) {
658            if (signal instanceof SignalMast) {
659                icon = setDragMastIcon((SignalMast)signal);
660            } else if (signal instanceof SignalHead) {
661                icon = setDragHeadIcon((SignalHead)signal);
662            }
663        }
664        if (icon == null) {
665            _dragLabel.setText(Bundle.getMessage("noIcon"));
666        } else {
667            _dragLabel.setText(null);
668        }
669        _dragLabel.setIcon(icon);
670        _dndPanel.invalidate();
671        invalidate();
672        pack();
673    }
674
675    private NamedIcon setDragMastIcon(SignalMast mast) {
676        String family = mast.getSignalSystem().getSystemName();
677        SignalAppearanceMap appMap = mast.getAppearanceMap();
678        Enumeration<String> e = mast.getAppearanceMap().getAspects();
679        String s = appMap.getImageLink("Clear", family);
680        if ( s == null || s.equals("")) {
681            s = appMap.getImageLink("Stop", family);
682        }
683        if ( s == null || s.equals("")) {
684            s = appMap.getImageLink(e.nextElement(), family);
685        }
686        if (s !=null && !s.equals("")) {
687            if (!s.contains("preference:")) {
688                s = s.substring(s.indexOf("resources"));
689            }
690            return new NamedIcon(s, s);
691        }
692        log.error("SignalMast icon cannot be found for {}", mast.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME));
693        return null;
694    }
695
696    private NamedIcon setDragHeadIcon(SignalHead mast) {
697        _dragHeadIcon = null;
698        // find icon from other icons displaying this head, if any
699        List<PositionableIcon> iArray = _parent.getSignalIconMap(mast);
700        if (!iArray.isEmpty()) {
701            PositionableIcon pos = iArray.get(0);
702            if (pos instanceof SignalHeadIcon) {
703                _dragHeadIcon = (SignalHeadIcon)pos;
704            }
705        }
706        if (_dragHeadIcon == null) { // find icon from icons of other heads on this panel
707            HashMap<NamedBean, ArrayList<PositionableIcon>> icons = _parent.getSignalIconMap();
708            if (icons != null && !icons.isEmpty()) {
709                for (List<PositionableIcon> ia : icons.values()) {
710                    if (!ia.isEmpty()) {
711                        PositionableIcon pos = ia.get(0);
712                        if (pos instanceof SignalHeadIcon) {
713                            _dragHeadIcon = (SignalHeadIcon)pos;
714                            break;
715                        }
716                    }
717                }
718            }
719        }
720        if (_dragHeadIcon == null) { // find icon from any set in ItemPalette
721            _dragHeadIcon = new SignalHeadIcon(_parent._editor);
722            _dragHeadIcon.setSignalHead(mast.getDisplayName());
723            HashMap<String, HashMap<String, NamedIcon>> maps = ItemPalette.getFamilyMaps("SignalHead");
724            if (maps.isEmpty()) {
725                log.error("SignalHead icon cannot be found for {}", mast.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME));
726            } else {
727                for (Entry<String, HashMap<String, NamedIcon>> entry : maps.entrySet()) {
728                    HashMap<String, NamedIcon> map = entry.getValue();
729                    for (Entry<String, NamedIcon> ent : map.entrySet()) {
730                        _dragHeadIcon.setIcon(ent.getKey(), new NamedIcon(ent.getValue()));
731                    }
732                    _dragHeadIcon.setFamily(entry.getKey());
733                    break;
734                }
735            }
736        } else {
737            _dragHeadIcon = (SignalHeadIcon)_dragHeadIcon.deepClone();
738        }
739        _dragHeadIcon.setDisplayLevel(SignalHead.RED);
740        return (NamedIcon)_dragHeadIcon.getIcon();
741    }
742
743    SignalHeadIcon _dragHeadIcon;
744    
745    //////////////////////////// DnD ////////////////////////////
746    protected JPanel makeDndIconPanel() { 
747        JPanel dndPanel = new JPanel();
748        dndPanel.setLayout(new BoxLayout(dndPanel, BoxLayout.Y_AXIS));
749
750        JPanel p = new JPanel();
751        JLabel l = new JLabel(Bundle.getMessage("dragIcon"));
752        p.add(l);
753        dndPanel.add(p);
754
755        JPanel panel = new JPanel();
756        panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
757                Bundle.getMessage("signal")));
758        try {
759            _dragLabel = new IconDragJLabel(new DataFlavor(Editor.POSITIONABLE_FLAVOR));
760            _dragLabel.setIcon(null);
761            _dragLabel.setText(Bundle.getMessage("noIcon"));
762            _dragLabel.setName(Bundle.getMessage("signal"));
763            panel.add(_dragLabel);
764        } catch (java.lang.ClassNotFoundException cnfe) {
765            log.error("Unable to find class supporting {}", Editor.POSITIONABLE_FLAVOR, cnfe);
766        }
767        dndPanel.add(panel);
768        dndPanel.setVisible(true);
769        return dndPanel;
770    }
771
772    public class IconDragJLabel extends DragJLabel {
773
774        NamedBean signal;
775
776        public IconDragJLabel(DataFlavor flavor) {
777            super(flavor);
778        }
779
780        @Override
781        protected boolean okToDrag() {
782            String msg;
783            if (_currentPortal == null) {
784                msg = Bundle.getMessage("selectPortalProtection", _homeBlock.getDisplayName(DisplayOptions.QUOTED_DISPLAYNAME));
785            } else {
786                signal = getSignal();
787                if (signal != null) {
788                    msg = checkMastForSave();
789                } else {
790                    String name = _mastName.getText().trim();
791                    if ( name.length()==0) {
792                        msg = Bundle.getMessage("selectSignalMast");
793                    } else {
794                        msg = Bundle.getMessage("NotFound", name);
795                    }
796                }
797            }
798            if (msg.length() > 0) {
799                JmriJOptionPane.showMessageDialog(this, msg,
800                        Bundle.getMessage("configureSignal"), JmriJOptionPane.INFORMATION_MESSAGE);
801                return false;
802            }
803            return true;
804        }
805
806        @Override
807        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
808            if (!isDataFlavorSupported(flavor)) {
809                return null;
810            }
811            if (DataFlavor.stringFlavor.equals(flavor)) {
812                return null;
813            }
814
815            if (signal == null || _currentPortal == null) {
816                return null;
817            }
818            PositionableIcon icon;
819            if (signal instanceof SignalMast) {
820                icon = new SignalMastIcon(_parent._editor);
821                ((SignalMastIcon)icon).setSignalMast(signal.getDisplayName());
822            } else if (signal instanceof SignalHead) {
823                icon = _dragHeadIcon;
824           } else {
825                log.error("Signal icon cannot be created for {}", signal.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME));
826                return null;
827            }
828            _parent.getCircuitIcons(_homeBlock).add(icon);
829            List<PositionableIcon> siArray = _parent.getSignalIconMap(signal);
830            siArray.add(icon);
831            _parent._editor.highlight(icon);
832            icon.setLevel(Editor.SIGNALS);
833            log.debug("getTransferData for {}", signal.getDisplayName(DisplayOptions.USERNAME_SYSTEMNAME));
834            return icon;
835        }
836
837        @Override
838        public void dragDropEnd(DragSourceDropEvent e) {
839            setMastNameAndIcon(signal, _currentPortal);
840            log.debug("DragJLabel.dragDropEnd ");
841        }
842    }
843
844    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EditSignalFrame.class);
845
846}