001package jmri.jmrit.display.palette;
002
003import java.awt.Color;
004import java.awt.Dimension;
005import java.awt.Font;
006import java.awt.datatransfer.DataFlavor;
007import java.awt.datatransfer.UnsupportedFlavorException;
008import java.awt.event.ActionEvent;
009import java.awt.event.ActionListener;
010import java.awt.event.KeyEvent;
011import java.awt.event.KeyListener;
012import java.io.IOException;
013import java.util.HashMap;
014
015import javax.swing.BorderFactory;
016import javax.swing.Box;
017import javax.swing.BoxLayout;
018import javax.swing.ButtonGroup;
019import javax.swing.JButton;
020import javax.swing.JColorChooser;
021import javax.swing.JComboBox;
022import javax.swing.JComponent;
023import javax.swing.JLabel;
024import javax.swing.JPanel;
025import javax.swing.JRadioButton;
026import javax.swing.JSpinner;
027import javax.swing.JTabbedPane;
028import javax.swing.JTextField;
029import javax.swing.SpinnerModel;
030import javax.swing.SpinnerNumberModel;
031import javax.swing.border.Border;
032import javax.swing.border.CompoundBorder;
033import javax.swing.border.LineBorder;
034import javax.swing.event.ChangeEvent;
035import javax.swing.event.ChangeListener;
036
037import jmri.jmrit.catalog.NamedIcon;
038import jmri.jmrit.display.DisplayFrame;
039import jmri.jmrit.display.Editor;
040import jmri.jmrit.display.LinkingLabel;
041import jmri.jmrit.display.Positionable;
042import jmri.jmrit.display.PositionableLabel;
043import jmri.jmrit.display.PositionablePopupUtil;
044import jmri.jmrit.display.PreviewPanel;
045import jmri.jmrit.display.SensorIcon;
046import jmri.util.swing.ImagePanel;
047import jmri.util.swing.JmriColorChooser;
048
049import org.slf4j.Logger;
050import org.slf4j.LoggerFactory;
051
052/**
053 * ItemPanel for text labels.
054 * @see ItemPanel palette class diagram
055 * @author Pete Cressman Copyright (c) 2010, 2011, 2020
056 */
057public class TextItemPanel extends ItemPanel {
058
059    private final ImagePanel _samplePanel;
060
061    private PositionablePopupUtil _util;
062    private final HashMap<String, PositionableLabel> _samples;
063
064    private String _selectedState;
065    private boolean _isPositionableLabel;
066    private JComponent _textEditComponent;
067
068    private JTabbedPane _tabPane;
069
070    private final ButtonGroup _buttonGroup = new ButtonGroup();
071    private AJRadioButton _fontButton;
072    private AJRadioButton _borderButton;
073    private AJRadioButton _backgroundButton;
074    protected int _selectedButton;  // allow access for testing
075    protected JColorChooser _chooser;   // allow access for testing
076
077    protected AJSpinner _borderSpin;    // allow testing access
078    protected AJSpinner _marginSpin;
079    private AJSpinner _widthSpin;
080    private AJSpinner _heightSpin;
081
082    public static final int BORDER = 1;
083    public static final int MARGIN = 2;
084    public static final int FWIDTH = 3;
085    public static final int FHEIGHT = 4;
086
087    static final int FOREGROUND_BUTTON = 1;
088    static final int BACKGROUND_BUTTON = 2;
089    static final int TRANSPARENT_BUTTON = 3;
090    static final int BORDERCOLOR_BUTTON = 4;
091
092
093    /**
094     * Constructor for Text Labels.
095     *
096     * @param parentFrame ItemPalette instance
097     * @param type        identifier of the ItemPanel type, should be "Text"
098     */
099    public TextItemPanel(DisplayFrame parentFrame, String type) {
100        super(parentFrame, type);
101        setToolTipText(Bundle.getMessage("ToolTipDragText"));
102        _samples = new HashMap<>();
103        _samplePanel = new ImagePanel();
104//        _samplePanel.add(Box.createVerticalStrut(50));
105        _samplePanel.setBorder(BorderFactory.createLineBorder(Color.black));
106        _samplePanel.setName("SamplePanel");    // to find component for testing
107        _samplePanel.setImage(_frame.getPreviewBackground());
108    }
109    JPanel dragger;
110
111    @Override
112    public void init() {
113        if (!_initialized) {
114            add(instructions());
115            PositionableLabel label = new PositionableLabel(Bundle.getMessage("sample"), _frame.getEditor());
116            label.setLevel(Editor.LABELS);
117            JPanel dragger;
118            try {
119               dragger = new LabelDragJComponent(new DataFlavor(Editor.POSITIONABLE_FLAVOR), label);
120            } catch (java.lang.ClassNotFoundException cnfe) {
121                log.error("Unable to find class supporting {}", Editor.POSITIONABLE_FLAVOR, cnfe);
122                dragger = new JPanel();
123            }
124            _util = label.getPopupUtility();
125            _samples.put("Text", label);
126            _selectedState = "Text";
127            _isPositionableLabel = true;
128            _textEditComponent = makeTextPanel("Text", label, true);
129            _samplePanel.add(dragger);
130            _previewPanel = new PreviewPanel(_frame, _samplePanel, null, true);
131            sampleBgColorChange();
132            log.debug("LabelDragJComponent size {} | panel size {}", label.getPreferredSize(), _samplePanel.getPreferredSize());
133            finishInit(false);
134              dragger.setOpaque(false);
135              dragger.setToolTipText(Bundle.getMessage("ToolTipDragIcon"));
136            if (log.isDebugEnabled()) {
137                log.debug("end init: TextItemPanel size {}", getPreferredSize());
138            }
139            super.init();
140            initLinkPanel();
141        }
142    }
143
144    public void init(ActionListener doneAction, Positionable pos) {
145        _update = true;
146        _doneAction = doneAction;
147        Positionable item = pos.deepClone(); // need copy of PositionableJPanel for PopupUtility
148        _util = item.getPopupUtility();
149        item.remove();      // don't need copy any more. Removes ghost image of PositionableJPanels
150        _isPositionableLabel = (pos instanceof PositionableLabel);
151
152        _previewPanel = new PreviewPanel(_frame, _samplePanel, null, false);
153        sampleBgColorChange();
154
155        boolean hasTextStates;
156        if (pos instanceof SensorIcon && !((SensorIcon)pos).isIcon()) {
157            SensorIcon si = (SensorIcon) pos;
158            hasTextStates = true;
159            if (!si.isIcon() && si.isText()) {
160                _tabPane = new JTabbedPane();
161                _tabPane.addChangeListener(c -> {
162                    TextPanel panel = (TextPanel)_tabPane.getSelectedComponent();
163                    _selectedState = panel.getState();
164                    log.debug("StateChange");
165                    setChooserColor();
166                });
167                PositionableLabel sample = new PositionableLabel(si.getActiveText(), _frame.getEditor());
168                sample.setForeground(si.getTextActive());
169                sample.setBackground(si.getBackgroundActive());
170                JPanel panel  = doPopupUtility("SensorStateActive", sample, true); // NOI18N
171                _tabPane.add(panel, Bundle.getMessage("SensorStateActive"));
172
173                sample = new PositionableLabel(si.getInactiveText(), _frame.getEditor());
174                sample.setForeground(si.getTextInActive());
175                sample.setBackground(si.getBackgroundInActive());
176                panel  = doPopupUtility("SensorStateInactive", sample, true); // NOI18N
177                _tabPane.add(panel, Bundle.getMessage("SensorStateInactive"));
178
179                sample = new PositionableLabel(si.getUnknownText(), _frame.getEditor());
180                sample.setForeground(si.getTextUnknown());
181                sample.setBackground(si.getBackgroundUnknown());
182                panel  = doPopupUtility("BeanStateUnknown", sample, true); // NOI18N
183                _tabPane.add(panel, Bundle.getMessage("BeanStateUnknown"));
184
185                sample = new PositionableLabel(si.getInconsistentText(), _frame.getEditor());
186                sample.setForeground(si.getTextInconsistent());
187                sample.setBackground(si.getBackgroundInconsistent());
188                panel  = doPopupUtility("BeanStateInconsistent", sample, true); // NOI18N
189                _tabPane.add(panel, Bundle.getMessage("BeanStateInconsistent"));
190                /*      to be used later!!!
191                for (String state : si.getStateNameCollection()) {
192                    PositionableLabel sample = new PositionableLabel(si.getActiveText(), _editor);
193                    sample.setForeground(si.getTextActive());
194                    sample.setBackground(si.getBackgroundActive());
195                    JPanel panel  = doPopupUtility(state, sample, true); // NOI18N
196                    _tabPane.add(panel, Bundle.getMessage(state));
197                }
198                */
199                _textEditComponent = _tabPane;
200            }   // else a non-text SensorIcon cannot be decorated.
201        } else { // not a SensorIcon
202            hasTextStates = false;
203            PositionableLabel sample = new PositionableLabel("", _frame.getEditor());
204            sample.setForeground(_util.getForeground());
205            sample.setBackground(_util.getBackground());
206            PositionablePopupUtil util = sample.getPopupUtility();
207            util.setHasBackground(_util.hasBackground());
208            boolean addtextField;
209            if (pos instanceof PositionableLabel) {
210                sample.setText(((PositionableLabel)pos).getUnRotatedText());
211                addtextField = !(pos instanceof jmri.jmrit.display.MemoryOrGVIcon);
212            } else {
213                // To display PositionableJPanel types as PositionableLabels, set fixed sizes.
214                util.setFixedWidth(pos.getWidth() - 2*_util.getBorderSize());
215                util.setFixedHeight(pos.getHeight() - 2*_util.getBorderSize());
216                if (pos instanceof jmri.jmrit.display.MemoryOrGVComboIcon) {
217                    JComboBox<String> box = ((jmri.jmrit.display.MemoryOrGVComboIcon)pos).getTextComponent();
218                    sample.setText(box.getSelectedItem() != null ? box.getSelectedItem().toString() : "");
219                    addtextField = false;
220                } else if (pos instanceof jmri.jmrit.display.PositionableJPanel) {
221                    JTextField field = (JTextField) pos.getTextComponent();
222                    sample.setText(field.getText());
223                    addtextField = false;
224                } else {
225                    addtextField = true;
226                    log.error("Unknown Postionable Type {}", pos.getClass().getName());
227                }
228            }
229            _textEditComponent = doPopupUtility("Text", sample, addtextField);
230            if (log.isDebugEnabled()) {
231                log.debug("util width= {} height= {} POS width= {} height= {}",
232                        util.getFixedWidth(), util.getFixedHeight(), pos.getWidth(), pos.getHeight());
233            }
234        }
235        finishInit(hasTextStates);
236        _bottomPanel = new JPanel();
237        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
238        cancelButton.addActionListener(a -> cancel());
239        _bottomPanel.add(cancelButton);
240        _bottomPanel.add(makeUpdateButton(_doneAction));
241        add(_bottomPanel);
242    }
243
244    protected void cancel() {
245        _frame.dispose();
246    }
247
248    @Override
249    protected JPanel instructions() {
250        JPanel blurb = new JPanel();
251        blurb.setLayout(new BoxLayout(blurb, BoxLayout.Y_AXIS));
252        blurb.add(new JLabel(Bundle.getMessage("addTextAndAttrs")));
253        blurb.add(new JLabel(Bundle.getMessage("ToolTipDragText")));
254        blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE));
255        JPanel panel = new JPanel();
256        panel.add(blurb);
257        return panel;
258    }
259
260    @Override
261    protected void previewColorChange() {
262        _previewPanel.setBackgroundSelection(_frame.getPreviewBg());
263    }
264
265    protected JPanel makeDoneButtonPanel(ActionListener doneAction) {
266        JPanel panel = new JPanel();
267        JButton updateButton = new JButton(Bundle.getMessage("updateButton")); // custom update label
268        updateButton.addActionListener(doneAction);
269        updateButton.setToolTipText(Bundle.getMessage("ToolTipPickFromTable"));
270        panel.add(updateButton);
271
272        JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
273        cancelButton.addActionListener(a -> closeDialogs());
274        panel.add(cancelButton);
275        return panel;
276    }
277
278    public void updateAttributes(PositionableLabel l) {
279        setAttributes(l);
280        PositionablePopupUtil util = getPositionablePopupUtil();
281        l.setPopupUtility(util.clone(l, l.getTextComponent()));
282        l.setFont(util.getFont().deriveFont(util.getFontStyle()));
283        if (util.hasBackground()) { // unrotated
284            l.setOpaque(true);
285        }
286    }
287
288    @Override
289    protected JPanel makeSpecialBottomPanel(boolean update) {return null;}
290    @Override
291    protected JPanel makeItemButtonPanel() {return null;}
292    @Override
293    protected JPanel makeIconDisplayPanel(String k, HashMap<String, NamedIcon> m, boolean d) {return null;}
294    @Override
295    protected void initIconFamiliesPanel() {}
296    @Override
297    protected void hideIcons() {}
298    @Override
299    protected void makeFamiliesPanel() {}
300
301
302    protected class LabelDragJComponent extends DragJComponent {
303
304        public LabelDragJComponent(DataFlavor flavor, JComponent comp) {
305            super(flavor, comp);
306        }
307
308        @Override
309        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
310            if (!isDataFlavorSupported(flavor)) {
311                return null;
312            }
313
314            if (flavor.isMimeTypeEqual(Editor.POSITIONABLE_FLAVOR)) {
315                String link = _linkName.getText().trim();
316                PositionableLabel sample = (PositionableLabel)getThing();
317                PositionableLabel label;
318                if (link.length() == 0) {
319                    label = new PositionableLabel(sample.getText(), sample.getEditor());
320                } else {
321                    label = new LinkingLabel(sample.getText(), sample.getEditor(), link);
322                }
323                updateAttributes(label);
324                label.setLevel(sample.getDisplayLevel());
325                return label;
326            }
327            return null;
328        }
329    }
330
331    /*************************************************************************/
332    static class AJSpinner extends JSpinner {
333        int _which;
334
335        AJSpinner(SpinnerModel model, int which) {
336            super(model);
337            _which = which;
338        }
339    }
340
341    static class AJRadioButton extends JRadioButton {
342        int _which;
343
344        AJRadioButton(String text, int which) {
345            super(text);
346            _which = which;
347        }
348    }
349
350    static class TextPanel extends JPanel {
351        String _state;
352        TextPanel(String state) {
353            _state = state;
354        }
355        String getState() {
356            return _state;
357        }
358    }
359
360
361    private void finishInit(boolean addCaption) {
362        _selectedButton = FOREGROUND_BUTTON;
363        if (addCaption) {
364            _selectedState = "SensorStateActive";
365        } else {
366            _selectedState = "Text";
367        }
368        JPanel colorButtons = makeColorPanel(addCaption);
369        makeColorChooser();
370        JPanel panel = makeFontPanel();
371        if (addCaption) {
372            JPanel p = new JPanel();
373            p.add(new JLabel(Bundle.getMessage("StateTextBlurb1")));
374            panel.add(p);
375        }
376        this.add(panel);
377        if (addCaption) {
378            JPanel p = new JPanel();
379            p.add(new JLabel(Bundle.getMessage("StateTextBlurb3")));
380            this.add(p);
381        }
382        JPanel textEditPanel = new JPanel();
383        textEditPanel.setLayout(new BoxLayout(textEditPanel, BoxLayout.Y_AXIS));
384        textEditPanel.add(_textEditComponent);
385        this.add(textEditPanel);
386        this.add(_previewPanel);
387        this.add(Box.createVerticalGlue());
388        this.add(colorButtons);
389        this.add(_chooser);
390    }
391
392    private JPanel doPopupUtility(String type, PositionableLabel sample, boolean editText) {
393        PositionablePopupUtil util = sample.getPopupUtility();
394        util.setJustification(_util.getJustification());
395        util.setHorizontalAlignment(_util.getJustification());
396        if (_isPositionableLabel) {
397            int size = _util.getFixedWidth();
398            util.setFixedWidth(size);
399            size = _util.getFixedHeight();
400            util.setFixedHeight(size);
401        }
402        util.setMargin(_util.getMargin());
403        util.setBorderSize(_util.getBorderSize());
404        util.setBorderColor(_util.getBorderColor());
405        util.setFont(util.getFont().deriveFont(_util.getFontStyle()));
406        util.setFontSize(_util.getFontSize());
407        util.setFontStyle(_util.getFontStyle());
408        util.setOrientation(_util.getOrientation());
409        boolean back = (sample.getBackground() != null);
410        util.setHasBackground(back);
411        sample.setOpaque(back);
412        sample.updateSize();
413
414        _samples.put(type, sample);
415        _selectedState = type;
416        JPanel panel = makeTextPanel(type, sample, editText);
417        _samplePanel.add(sample);
418        return panel;
419    }
420
421    protected void initLinkPanel() {
422        JPanel blurb = new JPanel();
423        blurb.setLayout(new BoxLayout(blurb, BoxLayout.Y_AXIS));
424        blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE));
425        blurb.add(new JLabel(Bundle.getMessage("ToLinkToURL", "Text")));
426        blurb.add(new JLabel(Bundle.getMessage("enterPanel")));
427        blurb.add(new JLabel(Bundle.getMessage("enterURL")));
428        blurb.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE));
429        blurb.add(new JLabel(Bundle.getMessage("LinkName")));
430        blurb.add(_linkName);
431        _linkName.setToolTipText(Bundle.getMessage("ToolTipLink"));
432        blurb.setToolTipText(Bundle.getMessage("ToolTipLink"));
433        JPanel panel = new JPanel();
434        panel.add(blurb);
435        JPanel linkPanel = new JPanel();
436        linkPanel.add(panel);
437        add(linkPanel);
438    }
439
440    private void makeColorChooser() {
441        Color panelBackground = _frame.getEditor().getTargetPanel().getBackground(); // start using Panel background color
442        _chooser = JmriColorChooser.extendColorChooser(new JColorChooser(panelBackground));
443        _chooser.getSelectionModel().addChangeListener(c -> chooserColorChange());
444        _chooser.setPreviewPanel(new JPanel());
445        JmriColorChooser.suppressAddRecentColor(true);
446    }
447
448    private void fontChange() {
449        log.debug("fontChange");
450        if (_selectedButton != FOREGROUND_BUTTON) {
451            _selectedButton = FOREGROUND_BUTTON;
452            _fontButton.setSelected(true);  // will change chooser color, if needed
453        }
454        updateSamples();
455     }
456
457    private class SpinnerChangeListener implements ChangeListener {
458        @Override
459        public void stateChanged(ChangeEvent e) {
460            Object obj = e.getSource();
461            int num = ((Number) ((AJSpinner) obj).getValue()).intValue();
462            int which = ((AJSpinner) obj)._which;
463            switch (which) {
464                case BORDER:
465                    _util.setBorderSize(num);
466                    _borderButton.setSelected(true);
467                    _selectedButton = BORDERCOLOR_BUTTON;
468                    break;
469                case MARGIN:
470                    _util.setMargin(num);
471                    _selectedButton = BACKGROUND_BUTTON;
472                    _backgroundButton.setSelected(true);
473                    break;
474                case FWIDTH:
475                    _util.setFixedWidth(num);
476                    _selectedButton = BACKGROUND_BUTTON;
477                    _backgroundButton.setSelected(true);
478                    break;
479                case FHEIGHT:
480                    _util.setFixedHeight(num);
481                    _selectedButton = BACKGROUND_BUTTON;
482                    _backgroundButton.setSelected(true);
483                    break;
484                default:
485                    log.warn("Unexpected _which {}  in stateChanged", ((AJSpinner) obj)._which);
486                    break;
487            }
488            updateSamples();
489            log.debug("SpinnerChange which= {}, num= {}", which, num);
490        }
491    }
492
493    private JPanel makeFontPanel() {
494        JPanel panel = new JPanel();
495        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
496        panel.setBorder(BorderFactory.createTitledBorder(
497                BorderFactory.createLineBorder(Color.black, 1),
498                Bundle.getMessage("FontDisplaySettings")));
499
500        ActionListener fontAction = ((ActionEvent event) -> {
501            fontChange(); // callback
502        });
503        FontPanel fontPanel = new FontPanel(_util, fontAction);
504        panel.add(fontPanel);
505        fontPanel.setFontSelections();
506
507        ChangeListener listener = new SpinnerChangeListener();
508        JPanel sizePanel = new JPanel();
509        SpinnerNumberModel model = new SpinnerNumberModel(_util.getBorderSize(), 0, 100, 1);
510        _borderSpin = new AJSpinner(model, BORDER);
511        sizePanel.add(makeSpinPanel("borderSize", _borderSpin, listener));
512        model = new SpinnerNumberModel(_util.getMargin(), 0, 100, 1);
513        _marginSpin = new AJSpinner(model, MARGIN);
514        sizePanel.add(makeSpinPanel("marginSize", _marginSpin, listener));
515        if (_isPositionableLabel) {
516            model = new SpinnerNumberModel(_util.getFixedWidth(), 0, 1000, 1);
517            _widthSpin = new AJSpinner(model, FWIDTH);
518            sizePanel.add(makeSpinPanel("fixedWidth", _widthSpin, listener));
519            model = new SpinnerNumberModel(_util.getFixedHeight(), 0, 1000, 1);
520            _heightSpin = new AJSpinner(model, FHEIGHT);
521            sizePanel.add(makeSpinPanel("fixedHeight", _heightSpin, listener));
522        }
523        panel.add(sizePanel);
524        return panel;
525    }
526
527    public static JPanel makeSpinPanel(String caption, JSpinner spin, ChangeListener listener) {
528        JPanel panel = new JPanel();
529        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
530        panel.add(new JLabel(Bundle.getMessage(caption)));
531        spin.addChangeListener(listener);
532        panel.add(spin);
533        return panel;
534    }
535
536    private JPanel makeTextPanel(String beanState, JLabel sample, boolean addTextField) {
537        JPanel panel = new TextPanel(beanState);
538        // use NamedBeanBundle property for captions
539        // sample is the preview JLable
540        panel.setBorder(BorderFactory.createTitledBorder(
541                BorderFactory.createLineBorder(Color.black, 1),
542                Bundle.getMessage(beanState)));
543        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
544        JPanel p = new JPanel();
545        if (addTextField) {
546            JTextField textField = new JTextField(sample.getText(), 25);
547            textField.addKeyListener(new KeyListener() {
548                JLabel sample;
549
550                KeyListener init(JLabel s) {
551                    sample = s;
552                    return this;
553                }
554
555                @Override
556                public void keyTyped(KeyEvent evt) {
557                }
558
559                @Override
560                public void keyPressed(KeyEvent evt) {
561                }
562
563                @Override
564                public void keyReleased(KeyEvent evt) {
565                    JTextField tmp = (JTextField) evt.getSource();
566                    sample.setText(tmp.getText());
567                    updateSamples();
568                }
569            }.init(sample));
570            p.add(textField);
571            panel.add(p);
572        }
573        return panel;
574    }
575
576    private JPanel makeColorPanel(boolean addCaption) {
577        JPanel panel = new JPanel();
578        panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
579        panel.setBorder(BorderFactory.createTitledBorder(
580                BorderFactory.createLineBorder(Color.black, 1),
581                Bundle.getMessage("ColorDisplaySettings")));
582
583        JPanel buttonPanel = new JPanel();
584        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
585        _fontButton = makeColorRadioButton("FontColor", FOREGROUND_BUTTON);
586        buttonPanel.add(_fontButton);
587
588        _backgroundButton = makeColorRadioButton("FontBackgroundColor", BACKGROUND_BUTTON);
589        buttonPanel.add(_backgroundButton);
590
591        AJRadioButton button = makeColorRadioButton("transparentBack", TRANSPARENT_BUTTON);
592        buttonPanel.add(button);
593
594        _borderButton = makeColorRadioButton("borderColor", BORDERCOLOR_BUTTON);
595        buttonPanel.add(_borderButton);
596
597        panel.add(buttonPanel);
598        if (addCaption) {
599            JPanel p = new JPanel();
600            p.add(new JLabel(Bundle.getMessage("StateTextBlurb2")));
601            panel.add(p);
602        }
603        _fontButton.setSelected(true);
604        _selectedButton = FOREGROUND_BUTTON;
605        return panel;
606    }
607
608    private AJRadioButton makeColorRadioButton(String caption, int which) {
609        AJRadioButton button = new AJRadioButton(Bundle.getMessage(caption), which);
610        button.addActionListener(a -> {
611            if (button.isSelected()) {
612                _selectedButton = button._which;
613                log.debug("Button #{} selected.", which);
614                setChooserColor();
615            }
616        });
617        _buttonGroup.add(button);
618        return button;
619    }
620
621    private void chooserColorChange() {
622        PositionableLabel pos =_samples.get(_selectedState);
623        PositionablePopupUtil util = pos.getPopupUtility();
624        switch (_selectedButton) {
625            case FOREGROUND_BUTTON:
626                util.setForeground(_chooser.getColor());
627                break;
628            case BACKGROUND_BUTTON:
629                pos.setOpaque(true);
630                util.setBackgroundColor(_chooser.getColor());
631                break;
632            case TRANSPARENT_BUTTON:
633//                util.setBackgroundColor(null);
634                pos.setOpaque(false);
635                break;
636            case BORDERCOLOR_BUTTON:
637                _util.setBorderColor(_chooser.getColor());      // will remove later!!!
638                util.setBorderColor(_chooser.getColor());
639                break;
640            default:
641                log.warn("Unexpected color change for state {}, button# {}", _selectedState, _selectedButton);
642                break;
643        }
644        log.debug("chooserColorChange opaque= {} _selectedState= {} _selectedButton= {} color= {}",
645                pos.isOpaque(), _selectedState, _selectedButton, _chooser.getColor().toString());
646        updateSamples();
647    }
648
649    private void setChooserColor() {
650        PositionableLabel pos =_samples.get(_selectedState);
651        PositionablePopupUtil util = pos.getPopupUtility();
652        Color c = null;
653        switch (_selectedButton) {
654            case FOREGROUND_BUTTON:
655                c = util.getForeground();
656                break;
657            case BACKGROUND_BUTTON:
658                c = util.getBackground();
659                break;
660            case BORDERCOLOR_BUTTON:
661                c = util.getBorderColor();
662                break;
663            case TRANSPARENT_BUTTON:
664                util.setBackgroundColor(null);
665                updateSamples();
666                break;
667            default:
668                return;
669        }
670        _chooser.setColor(c);
671    }
672
673    protected void updateSamples() {
674        boolean isPalette = (_frame instanceof ItemPalette);
675        Dimension totalDim = _frame.getSize();
676        Dimension oldDim = getSize();
677
678        int mar = _util.getMargin();
679        int bor = _util.getBorderSize();
680        Border outlineBorder;
681        Font font = _util.getFont();
682        int just = _util.getJustification();
683
684        for (PositionableLabel sam : _samples.values()) {
685            PositionablePopupUtil util = sam.getPopupUtility();
686            sam.setFont(font);
687            if (_isPositionableLabel) {
688                util.setFixedWidth(_util.getFixedWidth());
689                util.setFixedHeight(_util.getFixedHeight());
690            } else {
691                util.setFixedWidth(util.getFixedWidth() + 2 * (mar - util.getMargin()) /*+ bor - util.getBorderSize()*/);
692                util.setFixedHeight(util.getFixedHeight() + 2 * (mar - util.getMargin()) /*+ bor - util.getBorderSize()*/);
693            }
694            util.setMargin(mar);
695            util.setBorderSize(bor);
696            Border borderMargin;
697            if (sam.isOpaque()) {
698                borderMargin = new LineBorder(sam.getBackground(), mar);
699            } else {
700                borderMargin = BorderFactory.createEmptyBorder(mar, mar, mar, mar);
701            }
702            if (bor == 0) {
703                outlineBorder = BorderFactory.createEmptyBorder(0, 0, 0, 0);
704            } else {
705                outlineBorder = new LineBorder(_util.getBorderColor(), bor);      // will remove later!!!
706//                outlineBorder = new LineBorder(util.getBorderColor(), bor);   // will use this
707            }
708            sam.setBorder(new CompoundBorder(outlineBorder, borderMargin));
709
710            switch (just) {
711                case PositionablePopupUtil.LEFT:
712                    sam.setHorizontalAlignment(JLabel.LEFT);
713                    break;
714                case PositionablePopupUtil.RIGHT:
715                    sam.setHorizontalAlignment(JLabel.RIGHT);
716                    break;
717                default:
718                    sam.setHorizontalAlignment(JLabel.CENTER);
719            }
720            sam.updateSize();
721            sam.setPreferredSize(sam.getSize());
722            sam.invalidate();
723            if (log.isTraceEnabled()) {
724                log.debug("util width= {} height= {} SAM width= {} height= {}", util.getFixedWidth(), util.getFixedHeight(), sam.getWidth(), sam.getHeight());
725                log.debug("margin = {}, border = {}, opaque= {}", util.getMargin(), util.getBorderSize(), sam.isOpaque());
726            }
727        }
728        _previewPanel.invalidate();
729        _textEditComponent.invalidate();
730        if (dragger != null) {
731            dragger.invalidate();
732        }
733        _samplePanel.invalidate();
734        reSizeDisplay(isPalette, oldDim, totalDim);
735    }
736
737    // called when editor changed
738    protected void sampleBgColorChange() {
739        _previewPanel.setBackgroundSelection(_frame.getPreviewBg());
740    }
741
742    public PositionablePopupUtil getPositionablePopupUtil() {
743        PositionableLabel pos =_samples.get(_selectedState);
744        PositionablePopupUtil util = pos.getPopupUtility();
745        _util.setForeground(util.getForeground());
746        _util.setBackgroundColor(util.getBackground());
747        _util.setBorderColor(util.getBorderColor());
748        return _util;
749    }
750
751    public void setAttributes(Positionable pos) {
752        if (pos instanceof SensorIcon  && !((SensorIcon)pos).isIcon()) {
753            SensorIcon icon = (SensorIcon) pos;
754            PositionableLabel sample = _samples.get("SensorStateActive");
755            if (sample.isOpaque()) {
756                icon.setBackgroundActive(sample.getBackground());
757            } else {
758                icon.setBackgroundActive(null);
759            }
760            icon.setTextActive(sample.getForeground());
761            icon.setActiveText(sample.getText());
762
763            sample = _samples.get("SensorStateInactive");
764            icon.setInactiveText(sample.getText());
765            if (sample.isOpaque()) {
766                icon.setBackgroundInActive(sample.getBackground());
767            } else {
768                icon.setBackgroundInActive(null);
769            }
770            icon.setTextInActive(sample.getForeground());
771
772            sample = _samples.get("BeanStateUnknown");
773            icon.setUnknownText(sample.getText());
774            if (sample.isOpaque()) {
775                icon.setBackgroundUnknown(sample.getBackground());
776            } else {
777                icon.setBackgroundUnknown(null);
778            }
779            icon.setTextUnknown(sample.getForeground());
780
781            sample = _samples.get("BeanStateInconsistent");
782            icon.setInconsistentText(sample.getText());
783            if (sample.isOpaque()) {
784                icon.setBackgroundInconsistent(sample.getBackground());
785            } else {
786                icon.setBackgroundInconsistent(null);
787            }
788            icon.setTextInconsistent(sample.getForeground());
789        } else {
790            PositionableLabel sample = _samples.get("Text");
791            if ( pos instanceof PositionableLabel &&
792                !(pos instanceof jmri.jmrit.display.MemoryOrGVIcon)) {
793                ((PositionableLabel) pos).setText(sample.getText());
794            }
795            PositionablePopupUtil posUtil = pos.getPopupUtility();
796            PositionablePopupUtil samUtil = sample.getPopupUtility();
797            if (sample.isOpaque()) {
798                posUtil.setBackgroundColor(samUtil.getBackground());
799            } else {
800                posUtil.setBackgroundColor(null);
801            }
802            posUtil.setHasBackground(samUtil.hasBackground());
803            posUtil.setForeground(samUtil.getForeground());
804            posUtil.setBorderColor(samUtil.getBorderColor());
805            posUtil.setFont(samUtil.getFont());
806            posUtil.setFixedWidth(samUtil.getFixedWidth());
807            posUtil.setFixedHeight(samUtil.getFixedHeight());
808            posUtil.setMargin(samUtil.getMargin());
809            posUtil.setBorderSize(samUtil.getBorderSize());
810        }
811        pos.invalidate();
812    }
813
814    public void close() {
815        JmriColorChooser.suppressAddRecentColor(false);
816    }
817
818    private final static Logger log = LoggerFactory.getLogger(TextItemPanel.class);
819
820}