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