001package jmri.jmrit.display.palette;
002
003import java.awt.Color;
004import java.awt.FlowLayout;
005import java.awt.Frame;
006import java.awt.event.ActionEvent;
007import java.awt.event.ActionListener;
008import java.awt.event.KeyEvent;
009import java.awt.event.KeyListener;
010import javax.swing.Box;
011import javax.swing.BoxLayout;
012import javax.swing.JButton;
013import javax.swing.JColorChooser;
014import javax.swing.JComponent;
015import javax.swing.JDialog;
016import javax.swing.JPanel;
017import javax.swing.JTextField;
018import javax.swing.SpinnerNumberModel;
019import javax.swing.event.ChangeEvent;
020import javax.swing.event.ChangeListener;
021import jmri.InstanceManager;
022import jmri.jmrit.display.Positionable;
023import jmri.jmrit.display.PositionableLabel;
024import jmri.jmrit.display.PositionablePopupUtil;
025import jmri.jmrit.display.palette.TextItemPanel.AJSpinner;
026import jmri.util.swing.JmriColorChooser;
027
028/**
029 * A dialog to color a Component in a Window.  The ColorChooser
030 * interactively modifies the color of the component on the window
031 * until the user either cancels or decides to keep the changes.
032 *
033 * @author Pete Cressman Copyright (C) 2018
034 * @since 4.13.1
035 */
036public class ColorDialog extends JDialog implements ChangeListener {
037
038    public static final int STRUT = 6;
039
040    public static final int ONLY = 0;
041    public static final int BORDER = 1; // (= 1)
042    public static final int MARGIN = 2; // (= 2)
043    public static final int FWIDTH = 3; // (= 3)
044    public static final int FHEIGHT = 4;   // (= 4)
045    public static final int FONT = 5;
046    public static final int TEXT = 6;
047
048        JColorChooser _chooser;
049        JComponent _target;
050        int _type;
051        Color _saveColor;
052        boolean _saveOpaque;
053        String _saveText;
054        PositionablePopupUtil _util;
055        PositionablePopupUtil _saveUtil;
056        ActionListener _colorAction;
057        JPanel _preview;
058
059        /**
060         * 
061         * @param client Window holding the component
062         * @param t target whose color may be changed
063         * @param type which attribute is being changed
064         * @param ca callback to tell client the component's color was changed. 
065         * May be null if client doesen't care.
066         */
067        public ColorDialog(Frame client, JComponent t, int type, ActionListener ca) {
068            super(client, true);
069            _target = t;
070            _type = type;
071            if (t instanceof Positionable) {
072                Positionable pos = (Positionable)t;
073                _util = pos.getPopupUtility();
074                if (_util != null) {
075                    _util.setSuppressRecentColor(true);
076                    Positionable p = pos.deepClone();
077                    _saveUtil = p.getPopupUtility();
078                    p.remove();
079                }
080            } else {
081                _util = null;
082            }
083            _saveOpaque = t.isOpaque();
084            _colorAction = ca;
085
086            JPanel panel = new JPanel();
087            panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
088            panel.add(Box.createVerticalGlue());
089
090            String title;
091            switch (type) {
092                case ONLY:
093                    title = "PanelColor";
094                    _saveColor = t.getBackground();
095                    break;
096                case BORDER:
097                    title = "SetBorderSizeColor";
098                    _saveColor = _util.getBorderColor();
099                    SpinnerNumberModel model = new SpinnerNumberModel(_util.getBorderSize(), 0, 100, 1);
100                    JPanel p = makePanel(TextItemPanel.makeSpinPanel("borderSize", new AJSpinner(model, BORDER), this));
101                    panel.add(p);
102                    break;
103                case MARGIN:
104                    title = "SetMarginSizeColor";
105                    _saveColor = _util.getBackground();
106                    model = new SpinnerNumberModel(_util.getMargin(), 0, 100, 1);
107                    p = makePanel(TextItemPanel.makeSpinPanel("marginSize", new AJSpinner(model, MARGIN), this));
108                    panel.add(p);
109                    break;
110                case FONT:
111                    title = "SetFontSizeColor";
112                    _saveColor = _util.getForeground();
113                    ActionListener fontAction = ((ActionEvent event) -> {
114                        update(); // callback
115                    });
116                    FontPanel fontPanel = new FontPanel(_util, fontAction);
117                    panel.add(fontPanel);
118                    fontPanel.setFontSelections();
119                    break;
120                case TEXT:
121                    title = "SetTextSizeColor";
122                    _saveColor = _util.getBackground();
123                    _saveText = ((PositionableLabel)t).getUnRotatedText();
124                    panel.add(makePanel(makeTextSpinnerPanel()));
125                    panel.add(Box.createVerticalGlue());
126                    panel.add(makePanel(makeTextPanel()));
127                    break;
128                default:
129                    title = "ColorChooser";
130                    _saveColor = t.getBackground();
131            }
132            panel.add(Box.createVerticalStrut(STRUT));
133            setTitle(Bundle.getMessage(title));
134
135            _chooser = JmriColorChooser.extendColorChooser(new JColorChooser(_saveColor));
136            _chooser.getSelectionModel().addChangeListener(this);
137            _chooser.setPreviewPanel(new JPanel());
138            JmriColorChooser.suppressAddRecentColor(true);
139            panel.add(_chooser);
140            panel.add(Box.createVerticalStrut(STRUT));
141
142            panel.add(makeDoneButtonPanel());
143            panel.add(Box.createVerticalGlue());
144
145            super.addWindowListener(new java.awt.event.WindowAdapter() {
146                @Override
147                public void windowClosing(java.awt.event.WindowEvent e) {
148                    cancel();
149                }
150            });
151            setContentPane(panel);
152
153            pack();
154            InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(client, t, this);
155            setVisible(true);
156        }
157
158        JPanel makePanel(JPanel p) {
159            JPanel panel = new JPanel();
160            panel.add(p);
161            return panel;
162        }
163
164        JPanel makeTextSpinnerPanel() {
165            JPanel panel = new JPanel();
166            panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
167            SpinnerNumberModel model = new SpinnerNumberModel(_util.getFixedWidth(), 0, 1000, 1);
168            panel.add(TextItemPanel.makeSpinPanel("fixedWidth", new AJSpinner(model, FWIDTH), this));
169            panel.add(Box.createHorizontalStrut(STRUT));
170            model = new SpinnerNumberModel(_util.getFixedHeight(), 0, 1000, 1);
171            panel.add(TextItemPanel.makeSpinPanel("fixedHeight", new AJSpinner(model, FHEIGHT), this));
172            return panel;
173        }
174
175        JPanel makeTextPanel() {
176            JPanel panel = new JPanel();
177            JTextField textField = new JTextField(_saveText, 25);
178            textField.addKeyListener(new KeyListener() {
179                @Override
180                public void keyTyped(KeyEvent evt) {
181                }
182                @Override
183                public void keyPressed(KeyEvent evt) {
184                }
185                @Override
186                public void keyReleased(KeyEvent evt) {
187                    JTextField tmp = (JTextField) evt.getSource();
188                    ((PositionableLabel)_target).setText(tmp.getText());
189                    update();
190                }
191            });
192            panel.add(textField);
193            return panel;
194        }
195
196        protected JPanel makeDoneButtonPanel() {
197            JPanel panel = new JPanel();
198            panel.setLayout(new FlowLayout());
199            JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
200            doneButton.addActionListener((ActionEvent event) -> {
201                done();
202            });
203            panel.add(doneButton);
204
205            JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel"));
206            cancelButton.addActionListener((ActionEvent event) -> cancel());
207
208            panel.add(cancelButton);
209            return panel;
210        }
211
212        void done() {
213            log.debug("Done button: color= {}", _chooser.getColor());
214            if (_colorAction != null) {
215                _colorAction.actionPerformed(null);
216            }
217            if (_util != null) {
218                _util.setSuppressRecentColor(false);
219            }
220            JmriColorChooser.suppressAddRecentColor(false);
221            JmriColorChooser.addRecentColor(_chooser.getColor());
222            dispose();
223        }
224
225        void cancel() {
226            if (_util != null) {
227                Positionable pl = (Positionable)_target;
228                pl.getEditor().setAttributes(_saveUtil, pl);
229                _util.setSuppressRecentColor(false);
230                pl.updateSize();
231                if (_type == TEXT) {
232                    ((PositionableLabel)_target).setText(_saveText);
233                }
234            } else {
235                _target.setBackground(_saveColor);
236            }
237            _target.setOpaque(_saveOpaque);
238            log.debug("Cancel: color= {}", _saveColor);
239            JmriColorChooser.suppressAddRecentColor(false);
240            dispose();
241        }
242
243        private void update() {
244            if (_util != null) {
245                Positionable pl = (Positionable)_target;
246                pl.getEditor().setAttributes(_util, pl);
247            }
248        }
249
250        @Override
251        public void stateChanged(ChangeEvent e) {
252            Object obj = e.getSource();
253            if (obj instanceof AJSpinner) {
254                int num = ((Number) ((AJSpinner) obj).getValue()).intValue();
255                switch (((AJSpinner) obj)._which) {
256                    case BORDER:
257                        _util.setBorderSize(num);
258                        break;
259                    case MARGIN:
260                        _util.setMargin(num);
261                        break;
262                    case FWIDTH:
263                        _util.setFixedWidth(num);
264                        break;
265                    case FHEIGHT:
266                        _util.setFixedHeight(num);
267                        break;
268                    default:
269                        log.warn("Unexpected _which {}  in stateChanged", ((AJSpinner) obj)._which);
270                        break;
271                }
272            } else {
273                log.debug("stateChanged: color= {}", _chooser.getColor());
274                if (_util != null) {
275                    switch (_type) {
276                        case BORDER:
277                            _util.setBorderColor(_chooser.getColor());
278                            break;
279                        case MARGIN:
280                            _util.setBackgroundColor(_chooser.getColor());
281                            break;
282                        case FONT:
283                        case TEXT:
284                            _util.setForeground(_chooser.getColor());
285                            break;
286                        default:
287                    }
288                } else {
289                    _target.setOpaque(true);
290                    _target.setBackground(_chooser.getColor());
291                }
292            }
293            update();
294        }
295
296        private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ColorDialog.class);
297}
298