001package jmri.jmrit.display;
002
003import java.awt.Component;
004import java.awt.Dimension;
005import java.awt.GridBagConstraints;
006import java.awt.GridBagLayout;
007import java.awt.event.ActionEvent;
008import java.awt.event.WindowEvent;
009import java.text.MessageFormat;
010
011import javax.swing.AbstractAction;
012import javax.swing.JButton;
013import javax.swing.JComponent;
014import javax.swing.JLabel;
015import javax.swing.JSpinner;
016import javax.swing.JTextField;
017import javax.swing.SpinnerNumberModel;
018import javax.swing.event.ChangeListener;
019
020import jmri.util.JmriJFrame;
021import jmri.util.swing.JmriJOptionPane;
022
023/**
024 * Displays and allows user to modify {@literal x & y} coordinates of
025 * positionable labels This class has been generalized to provide popup edit
026 * dialogs for positionable item properties when TextFields are needed to input
027 * data.
028 * <p>
029 * The class name no longer identifies the full purpose of the class, However
030 * the name is retained because coordinate editing was the genesis. The current
031 * list of properties served for editing is:
032 * <ul>
033 * <li>modify {@literal x & y} coordinates modify level modify tooltip modify
034 * border size</li>
035 * <li>modify margin size modify fixed size modify rotation degrees modify
036 * scaling</li>
037 * <li>modify text labels modify zoom scaling modify panel name</li>
038 * </ul>
039 * To use, write a static method that provides the dialog frame. Then write an
040 * initX method that customizes the dialog for the property.
041 *
042 * @author Dan Boudreau Copyright (C) 2007
043 * @author Pete Cressman Copyright (C) 2010
044 */
045public class CoordinateEdit extends JmriJFrame {
046
047    Positionable pl;    // positional label tracked by this frame
048    int oldX;
049    int oldY;
050    double oldD;
051    String oldStr;
052
053    // member declarations
054    JLabel nameText = new JLabel();
055    JLabel textX;
056    JLabel textY;
057
058    // buttons
059    JButton okButton = new JButton();
060    JButton cancelButton = new JButton();
061
062    // text field
063    JTextField xTextField;
064    JTextField yTextField;
065
066    //SpinnerNumberModel _spinModel;
067    JSpinner spinX;
068    JSpinner spinY;
069
070    public static AbstractAction getCoordinateEditAction(final Positionable pos) {
071        return new AbstractAction() {
072            @Override
073            public void actionPerformed(ActionEvent e) {
074                CoordinateEdit f = new CoordinateEdit();
075                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
076                f.init(Bundle.getMessage("SetXY", ""), pos, true); // use property without ellipsis in variable
077                f.initSetXY();
078                f.setVisible(true);
079                f.setLocationRelativeTo((Component) pos);
080            }
081        };
082    }
083    //////////////////////////////////////////////////////////////
084
085    public static AbstractAction getLevelEditAction(final Positionable pos) {
086        return new AbstractAction(Bundle.getMessage("SetLevel", "...")) {
087            @Override
088            public void actionPerformed(ActionEvent e) {
089                CoordinateEdit f = new CoordinateEdit();
090                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
091                f.init(Bundle.getMessage("SetLevel", ""), pos, true); // use property without ellipsis in variable
092                f.initSetLevel();
093                f.setVisible(true);
094                f.setLocationRelativeTo((Component) pos);
095            }
096        };
097    }
098    //////////////////////////////////////////////////////////////
099
100    public static AbstractAction getToolTipEditAction(final Positionable pos) {
101        return new AbstractAction(Bundle.getMessage("SetCustomTooltip", "...")) {
102            @Override
103            public void actionPerformed(ActionEvent e) {
104                CoordinateEdit f = new CoordinateEdit();
105                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
106                f.init(Bundle.getMessage("SetTooltip", ""), pos, true); // use property without ellipsis in variable
107                f.initSetTip();
108                f.setVisible(true);
109                f.setLocationRelativeTo((Component) pos);
110            }
111        };
112    }
113    //////////////////////////////////////////////////////////////
114
115    public static AbstractAction getBorderEditAction(final Positionable pos) {
116        return new AbstractAction(Bundle.getMessage("SetBorderSize", "...")) {
117
118            @Override
119            public void actionPerformed(ActionEvent e) {
120                CoordinateEdit f = new CoordinateEdit();
121                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
122                f.init(Bundle.getMessage("SetBorderSize", ""), pos, true);
123                f.initBorder();
124                f.setVisible(true);
125                f.setLocationRelativeTo((Component) pos);
126            }
127        };
128    }
129    //////////////////////////////////////////////////////////////
130
131    public static AbstractAction getMarginEditAction(final Positionable pos) {
132        return new AbstractAction(Bundle.getMessage("SetMarginSize", "...")) {
133            @Override
134            public void actionPerformed(ActionEvent e) {
135                CoordinateEdit f = new CoordinateEdit();
136                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
137                f.init(Bundle.getMessage("SetMarginSize", ""), pos, true);
138                f.initMargin();
139                f.setVisible(true);
140                f.setLocationRelativeTo((Component) pos);
141            }
142        };
143    }
144    //////////////////////////////////////////////////////////////
145
146    public static AbstractAction getFixedSizeEditAction(final Positionable pos) {
147        return new AbstractAction(Bundle.getMessage("SetFixedSize", "...")) {
148
149            @Override
150            public void actionPerformed(ActionEvent e) {
151                CoordinateEdit f = new CoordinateEdit();
152                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
153                f.init(Bundle.getMessage("SetFixedSize", ""), pos, true);
154                f.initFixedSize();
155                f.setVisible(true);
156                f.setLocationRelativeTo((Component) pos);
157            }
158        };
159    }
160    //////////////////////////////////////////////////////////////
161
162    public static AbstractAction getRotateEditAction(final Positionable pos) {
163        return new AbstractAction(Bundle.getMessage("RotateSign", pos.getDegrees())) { // Bundle property includes degree symbol
164
165            @Override
166            public void actionPerformed(ActionEvent e) {
167                CoordinateEdit f = new CoordinateEdit();
168                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
169                f.init(Bundle.getMessage("Rotate", ""), pos, true);
170                f.initRotate();
171                f.setVisible(true);
172                f.setLocationRelativeTo((Component) pos);
173            }
174        };
175    }
176    //////////////////////////////////////////////////////////////
177
178    public static AbstractAction getScaleEditAction(final Positionable pos) {
179        return new AbstractAction(Bundle.getMessage("Rescale", "...")) {
180
181            @Override
182            public void actionPerformed(ActionEvent e) {
183                CoordinateEdit f = new CoordinateEdit();
184                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
185                f.init(Bundle.getMessage("Rescale", ""), pos, true);
186                f.initScale();
187                f.setVisible(true);
188                f.setLocationRelativeTo((Component) pos);
189            }
190        };
191    }
192    //////////////////////////////////////////////////////////////
193
194    public static AbstractAction getIdEditAction(
195            final Positionable pos, final String title, final Editor editor) {
196
197        return new AbstractAction(Bundle.getMessage(title) + "...") {
198
199            @Override
200            public void actionPerformed(ActionEvent e) {
201                CoordinateEdit f = new CoordinateEdit();
202                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
203                f.init(Bundle.getMessage(title), pos, false);
204                f.initId(editor);
205                f.setVisible(true);
206                f.setLocationRelativeTo((Component) pos);
207            }
208        };
209    }
210    //////////////////////////////////////////////////////////////
211
212    public static AbstractAction getClassesEditAction(
213            final Positionable pos, final String title, final Editor editor) {
214
215        return new AbstractAction(Bundle.getMessage(title) + "...") {
216
217            @Override
218            public void actionPerformed(ActionEvent e) {
219                CoordinateEdit f = new CoordinateEdit();
220                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
221                f.init(Bundle.getMessage(title), pos, false);
222                f.initClasses(editor);
223                f.setVisible(true);
224                f.setLocationRelativeTo((Component) pos);
225            }
226        };
227    }
228    //////////////////////////////////////////////////////////////
229
230    public static AbstractAction getTextEditAction(final Positionable pos, final String title) {
231        return new AbstractAction(Bundle.getMessage(title) + "...") {
232
233            @Override
234            public void actionPerformed(ActionEvent e) {
235                CoordinateEdit f = new CoordinateEdit();
236                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
237                f.init(Bundle.getMessage(title), pos, false);
238                f.initText();
239                f.setVisible(true);
240                f.setLocationRelativeTo((Component) pos);
241            }
242        };
243    }
244    //////////////////////////////////////////////////////////////
245
246    public static AbstractAction getLinkEditAction(final Positionable pos, final String title) {
247        return new AbstractAction(Bundle.getMessage(title) + "...") {
248
249            @Override
250            public void actionPerformed(ActionEvent e) {
251                CoordinateEdit f = new CoordinateEdit();
252                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
253                f.init(Bundle.getMessage(title), pos, false);
254                f.initLink();
255                f.setVisible(true);
256                f.setLocationRelativeTo((Component) pos);
257            }
258        };
259    }
260    //////////////////////////////////////////////////////////////
261
262    public static AbstractAction getZoomEditAction(final Positionable pos) {
263        return new AbstractAction(Bundle.getMessage("Zoom", "...")) {
264
265            @Override
266            public void actionPerformed(ActionEvent e) {
267                CoordinateEdit f = new CoordinateEdit();
268                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
269                f.init(Bundle.getMessage("Zoom", ""), pos, false);
270                f.initZoom();
271                f.setVisible(true);
272                //f.setLocation(100,100);
273                f.setLocationRelativeTo(pos.getEditor().getTargetPanel());
274            }
275        };
276    }
277    //////////////////////////////////////////////////////////////
278
279    public static AbstractAction getNameEditAction(final Positionable pos) {
280        return new AbstractAction(Bundle.getMessage("renamePanelMenu", "...")) {
281
282            @Override
283            public void actionPerformed(ActionEvent e) {
284                CoordinateEdit f = new CoordinateEdit();
285                f.addHelpMenu("package.jmri.jmrit.display.CoordinateEdit", true);
286                f.init(Bundle.getMessage("renamePanelMenu", ""), pos, false);
287                f.initSetName();
288                f.setVisible(true);
289                //f.setLocation(100,100);
290                f.setLocationRelativeTo(pos.getEditor().getTargetPanel());
291            }
292        };
293    }
294    //////////////////////////////////////////////////////////////
295
296    public CoordinateEdit() {
297        super(false, false);
298    }
299
300    @Override
301    public void windowClosed(WindowEvent e) {
302        super.windowClosed(e);
303    }
304
305    public void init(String title, Positionable pos, boolean showName) {
306        pl = pos;
307        if (showName) {
308            nameText.setText(MessageFormat.format(Bundle.getMessage("namelabel"), pos.getNameString()));
309            nameText.setVisible(true);
310        }
311        okButton.setText(Bundle.getMessage("ButtonOK"));
312        okButton.setVisible(true);
313
314        cancelButton.setText(Bundle.getMessage("ButtonCancel"));
315        cancelButton.setVisible(true);
316
317        Dimension dim = (new JButton("XXXXXXXX")).getPreferredSize();
318        okButton.setMinimumSize(dim);
319        cancelButton.setMinimumSize(dim);
320        setTitle(title);
321        //setLocation(pl.getLocation());
322    }
323
324    public void initSetXY() {
325        oldX = pl.getX();
326        oldY = pl.getY();
327
328        textX = new JLabel();
329        textX.setText("X: " + pl.getX());
330        textX.setVisible(true);
331        textY = new JLabel();
332        textY.setText("Y: " + pl.getY());
333        textY.setVisible(true);
334
335        SpinnerNumberModel model = new SpinnerNumberModel(0, 0, 10000, 1);
336        ChangeListener listener = e -> {
337            int x = ((Number) spinX.getValue()).intValue();
338            int y = ((Number) spinY.getValue()).intValue();
339            pl.setLocation(x, y);
340            textX.setText("X: " + pl.getX());
341            textY.setText("Y: " + pl.getY());
342        };
343        spinX = new JSpinner(model);
344        spinX.setValue(pl.getX());
345        spinX.setToolTipText(Bundle.getMessage("EnterXcoord"));
346        spinX.setMaximumSize(new Dimension(
347                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
348        spinX.addChangeListener(listener);
349        model = new SpinnerNumberModel(0, 0, 10000, 1);
350        spinY = new JSpinner(model);
351        spinY.setValue(pl.getY());
352        spinY.setToolTipText(Bundle.getMessage("EnterYcoord"));
353        spinY.setMaximumSize(new Dimension(
354                spinY.getMaximumSize().width, spinY.getPreferredSize().height));
355        spinY.addChangeListener(listener);
356
357        getContentPane().setLayout(new GridBagLayout());
358
359        addSpinItems(true);
360
361        okButton.addActionListener(e -> {
362            int x = ((Number) spinX.getValue()).intValue();
363            int y = ((Number) spinY.getValue()).intValue();
364            pl.setLocation(x, y);
365            textX.setText("X: " + pl.getX());
366            textY.setText("Y: " + pl.getY());
367            dispose();
368        });
369        okButton.getRootPane().setDefaultButton(okButton);
370
371        cancelButton.addActionListener(e -> {
372            pl.setLocation(oldX, oldY);
373            dispose();
374        });
375        // make large enough to easily move
376        setMinimumSize(new Dimension(250, 175));
377        pack();
378    }
379
380    public void initSetLevel() {
381        oldX = pl.getDisplayLevel();
382        textX = new JLabel();
383        textX.setText(Bundle.getMessage("Level") + ": " + pl.getDisplayLevel());
384        textX.setVisible(true);
385
386        SpinnerNumberModel model = new SpinnerNumberModel(0, 0, 10, 1);
387        spinX = new JSpinner(model);
388        spinX.setValue(pl.getDisplayLevel());
389        spinX.setToolTipText(Bundle.getMessage("EnterLevel"));
390        spinX.setMaximumSize(new Dimension(
391                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
392
393        getContentPane().setLayout(new GridBagLayout());
394
395        addSpinItems(false);
396
397        okButton.addActionListener(e -> {
398            int l = ((Number) spinX.getValue()).intValue();
399            pl.getEditor().setSelectionsDisplayLevel(l, pl);
400            textX.setText(Bundle.getMessage("Level") + ": " + l);
401            dispose();
402        });
403        okButton.getRootPane().setDefaultButton(okButton);
404        cancelButton.addActionListener(e -> {
405            pl.getEditor().setSelectionsDisplayLevel(oldX, pl);
406            dispose();
407        });
408        setMinimumSize(new Dimension(250, 175));
409        pack();
410    }
411
412    public void initSetTip() {
413        oldStr = pl.getToolTip().getText();
414        textX = new JLabel();
415        textX.setText(Bundle.getMessage("TooltipLabel") + ": ");
416        textX.setVisible(true);
417
418        xTextField = new JTextField(15);
419        xTextField.setText(pl.getToolTip().getText());
420        xTextField.setToolTipText(Bundle.getMessage("EnterTooltip"));
421//  xTextField.setMaximumSize(new Dimension(
422//    xTextField.getMaximumSize().width+100, xTextField.getPreferredSize().height));
423
424        getContentPane().setLayout(new GridBagLayout());
425
426        addTextItems();
427
428        okButton.addActionListener(e -> {
429            pl.getToolTip().setText(xTextField.getText()); // is fetched from pane OK but not stored in icon pl
430            dispose();
431        });
432        okButton.getRootPane().setDefaultButton(okButton);
433        cancelButton.addActionListener(e -> {
434            pl.getToolTip().setText(oldStr);
435            dispose();
436        });
437        pack();
438    }
439
440    public void initBorder() {
441        PositionablePopupUtil util = pl.getPopupUtility();
442        oldX = util.getBorderSize();
443
444        textX = new JLabel();
445        textX.setText(Bundle.getMessage("Border") + ": " + util.getBorderSize());
446        textX.setVisible(true);
447
448        SpinnerNumberModel model = new SpinnerNumberModel(0, 0, 1000, 1);
449        spinX = new JSpinner(model);
450        spinX.setValue(util.getBorderSize());
451        spinX.setToolTipText("Enter border size");
452        spinX.setMaximumSize(new Dimension(
453                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
454
455        getContentPane().setLayout(new GridBagLayout());
456
457        addSpinItems(false);
458
459        okButton.addActionListener(e -> {
460            int l = ((Number) spinX.getValue()).intValue();
461            PositionablePopupUtil util1 = pl.getPopupUtility();
462            util1.setBorderSize(l);
463            pl.getEditor().setAttributes(util1, pl);
464            textX.setText(Bundle.getMessage("Border") + ": " + l);
465            dispose();
466        });
467        okButton.getRootPane().setDefaultButton(okButton);
468        cancelButton.addActionListener(e -> {
469            pl.getPopupUtility().setBorderSize(oldX);
470            dispose();
471        });
472        pack();
473    }
474
475    public void initMargin() {
476        PositionablePopupUtil util = pl.getPopupUtility();
477        oldX = util.getMargin();
478
479        textX = new JLabel();
480        textX.setText(Bundle.getMessage("Margin") + ": " + util.getMargin());
481        textX.setVisible(true);
482
483        SpinnerNumberModel model = new SpinnerNumberModel(0, 0, 1000, 1);
484        spinX = new JSpinner(model);
485        spinX.setValue(util.getMargin());
486        spinX.setToolTipText("Enter margin size");
487        spinX.setMaximumSize(new Dimension(
488                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
489
490        getContentPane().setLayout(new GridBagLayout());
491
492        addSpinItems(false);
493
494        okButton.addActionListener(e -> {
495            int l = ((Number) spinX.getValue()).intValue();
496            PositionablePopupUtil util1 = pl.getPopupUtility();
497            pl.getPopupUtility().setMargin(l);
498            pl.getEditor().setAttributes(util1, pl);
499            textX.setText(Bundle.getMessage("Margin") + ": " + l);
500            dispose();
501        });
502        okButton.getRootPane().setDefaultButton(okButton);
503        cancelButton.addActionListener(e -> {
504            pl.getPopupUtility().setMargin(oldX);
505            dispose();
506        });
507        pack();
508    }
509
510    public void initFixedSize() {
511        PositionablePopupUtil util = pl.getPopupUtility();
512        oldX = util.getFixedHeight();
513        oldY = util.getFixedWidth();
514
515        textX = new JLabel();
516        textX.setText("Height = " + util.getFixedHeight());
517        textX.setVisible(true);
518        textY = new JLabel();
519        textY.setText("Width = " + util.getFixedWidth());
520        textY.setVisible(true);
521
522        SpinnerNumberModel model = new SpinnerNumberModel(0, 0, 1000, 1);
523        spinX = new JSpinner(model);
524        spinX.setValue(util.getFixedHeight());
525        spinX.setToolTipText(Bundle.getMessage("FixedSizeHeight"));
526        spinX.setMaximumSize(new Dimension(
527                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
528        model = new SpinnerNumberModel(0, 0, 1000, 1);
529        spinY = new JSpinner(model);
530        spinY.setValue(util.getFixedWidth());
531        spinY.setToolTipText(Bundle.getMessage("FixedSizeWidth"));
532        spinY.setMaximumSize(new Dimension(
533                spinY.getMaximumSize().width, spinY.getPreferredSize().height));
534
535        getContentPane().setLayout(new GridBagLayout());
536
537        addSpinItems(true);
538
539        okButton.addActionListener(e -> {
540            int height = ((Number) spinX.getValue()).intValue();
541            int width = ((Number) spinY.getValue()).intValue();
542            PositionablePopupUtil util1 = pl.getPopupUtility();
543            util1.setFixedSize(width, height);
544            pl.getEditor().setAttributes(util1, pl);
545            textX.setText("Height: " + util1.getFixedHeight());
546            textY.setText("Width: " + util1.getFixedWidth());
547            dispose();
548        });
549        okButton.getRootPane().setDefaultButton(okButton);
550        cancelButton.addActionListener(e -> {
551            pl.getPopupUtility().setFixedSize(oldY, oldX);
552            dispose();
553        });
554        pack();
555    }
556
557    public void initRotate() {
558        oldX = pl.getDegrees();
559
560        textX = new JLabel();
561        int deg = oldX;
562        textX.setText(MessageFormat.format(Bundle.getMessage("Angle"), deg));
563        textX.setVisible(true);
564
565        SpinnerNumberModel model = new SpinnerNumberModel(0, -360, 360, 1);
566        spinX = new JSpinner(model);
567//        spinX.setValue(Integer.valueOf(((NamedIcon)pLabel.getIcon()).getDegrees()));
568        spinX.setValue(deg);
569        spinX.setToolTipText(Bundle.getMessage("enterDegrees"));
570        spinX.setMaximumSize(new Dimension(
571                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
572
573        getContentPane().setLayout(new GridBagLayout());
574
575        addSpinItems(false);
576
577        okButton.addActionListener(e -> {
578            int k = ((Number) spinX.getValue()).intValue();
579            pl.getEditor().setSelectionsRotation(k, pl);
580            textX.setText(MessageFormat.format(Bundle.getMessage("Angle"), k));
581            dispose();
582        });
583        okButton.getRootPane().setDefaultButton(okButton);
584        cancelButton.addActionListener(e -> dispose());
585        pack();
586    }
587
588    public void initScale() {
589        //int scale = (int)Math.round(pl.getScale()*100);
590        oldD = pl.getScale();
591        log.debug("initScale oldD = {}", oldD);
592        log.trace("  pl = {}", pl);
593        textX = new JLabel();
594        textX.setText(MessageFormat.format(Bundle.getMessage("Scale"), oldD * 100));
595        textX.setVisible(true);
596
597        SpinnerNumberModel model = new SpinnerNumberModel(100.0, 10.0, 5000.0, 1.0);
598        spinX = new JSpinner(model);
599        log.debug("scale%= {}", (int) Math.round(oldD * 100));
600        spinX.setValue((int) Math.round(oldD * 100));
601        spinX.setToolTipText(Bundle.getMessage("enterScale"));
602        spinX.setMaximumSize(new Dimension(
603                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
604
605        getContentPane().setLayout(new GridBagLayout());
606
607        addSpinItems(false);
608
609        okButton.addActionListener(e -> {
610            double s = ((Number) spinX.getValue()).doubleValue() / 100 * pl.getScale();
611            pl.getEditor().setSelectionsScale(s, pl);
612            textX.setText(MessageFormat.format(Bundle.getMessage("Scale"), pl.getScale() * 100));
613            dispose();
614        });
615        okButton.getRootPane().setDefaultButton(okButton);
616        cancelButton.addActionListener(e -> dispose());
617        pack();
618    }
619
620    public void initId(final Editor editor) {
621        PositionableLabel pLabel = (PositionableLabel) pl;
622        oldStr = pLabel.getId();
623        textX = new JLabel();
624        textX.setText(Bundle.getMessage("EnterId") + ":");
625        textX.setVisible(true);
626
627        xTextField = new JTextField(15);
628        xTextField.setText(pLabel.getId());
629        xTextField.setToolTipText(Bundle.getMessage("TooltipEnterId"));
630
631        getContentPane().setLayout(new GridBagLayout());
632        addTextItems();
633
634        okButton.addActionListener(e -> {
635            PositionableLabel pp = (PositionableLabel) pl;
636            String t = xTextField.getText();
637            boolean hasText = (t != null && t.length() > 0);
638            if (hasText) {
639                try {
640                    pp.setId(t);
641                    pp.updateSize();
642                    dispose();
643                } catch (Positionable.DuplicateIdException ignore) {
644                    JmriJOptionPane.showMessageDialog(editor,
645                            Bundle.getMessage("ErrorIdNotUnique"),
646                            Bundle.getMessage("ErrorDialog"),
647                            JmriJOptionPane.ERROR_MESSAGE);
648                }
649            } else {
650                try {
651                    pp.setId(null);
652                    pp.updateSize();
653                    dispose();
654                } catch (Positionable.DuplicateIdException ex) {
655                    // This should never happen
656                    log.error("Positionable.setId(null) has thrown DuplicateIdException", ex);
657                }
658            }
659        });
660        okButton.getRootPane().setDefaultButton(okButton);
661        cancelButton.addActionListener(e -> {
662            PositionableLabel pp = (PositionableLabel) pl;
663//            pp.setId(oldStr);
664            pp.updateSize();
665            dispose();
666        });
667        pack();
668    }
669
670    public void initClasses(final Editor editor) {
671        PositionableLabel pLabel = (PositionableLabel) pl;
672        oldStr = String.join(",", pLabel.getClasses());
673        textX = new JLabel();
674        textX.setText(Bundle.getMessage("EnterClasses") + ":");
675        textX.setVisible(true);
676
677        xTextField = new JTextField(15);
678        xTextField.setText(oldStr);
679        xTextField.setToolTipText(Bundle.getMessage("TooltipEnterClasses"));
680
681        getContentPane().setLayout(new GridBagLayout());
682        addTextItems();
683
684        okButton.addActionListener(e -> {
685            PositionableLabel pp = (PositionableLabel) pl;
686            pp.removeAllClasses();
687            String t = xTextField.getText();
688            if (t != null && t.length() > 0) {
689                for (String className : t.split(",")) {
690                    if (!className.isBlank()) {
691                        pp.addClass(className.trim());
692                    }
693                }
694            }
695            pp.updateSize();
696            dispose();
697        });
698        okButton.getRootPane().setDefaultButton(okButton);
699        cancelButton.addActionListener(e -> {
700            PositionableLabel pp = (PositionableLabel) pl;
701            pp.updateSize();
702            dispose();
703        });
704        pack();
705    }
706
707    public void initText() {
708        PositionableLabel pLabel = (PositionableLabel) pl;
709        oldStr = pLabel.getUnRotatedText();
710        textX = new JLabel();
711        textX.setText(Bundle.getMessage("TextLabel") + ":");
712        textX.setVisible(true);
713
714        xTextField = new JTextField(15);
715        xTextField.setText(pLabel.getUnRotatedText());
716        xTextField.setToolTipText(Bundle.getMessage("TooltipEnterText"));
717
718        getContentPane().setLayout(new GridBagLayout());
719        addTextItems();
720
721        okButton.addActionListener(e -> {
722            PositionableLabel pp = (PositionableLabel) pl;
723            String t = xTextField.getText();
724            boolean hasText = (t != null && t.length() > 0);
725            if (pp.isIcon() || hasText) {
726                pp._text = hasText;
727                if (pp instanceof SensorIcon) {
728                    ((SensorIcon) pp).setOriginalText(t);
729                }
730                pp.setText(t);
731                pp.updateSize();
732                dispose();
733            } else {
734                xTextField.setText(Bundle.getMessage("warningNullText"));
735            }
736        });
737        okButton.getRootPane().setDefaultButton(okButton);
738        cancelButton.addActionListener(e -> {
739            PositionableLabel pp = (PositionableLabel) pl;
740            pp.setText(oldStr);
741            pp.updateSize();
742            dispose();
743        });
744        pack();
745    }
746
747    public void initLink() {
748        LinkingObject pLabel = (LinkingObject) pl;
749        oldStr = pLabel.getURL();
750        textX = new JLabel();
751        textX.setText(Bundle.getMessage("LinkEqual"));
752        textX.setVisible(true);
753
754        xTextField = new JTextField(15);
755        xTextField.setText(pLabel.getURL());
756        xTextField.setToolTipText(Bundle.getMessage("EnterLink"));
757
758        getContentPane().setLayout(new GridBagLayout());
759        addTextItems();
760        oldX = 0;  // counter for warning
761
762        okButton.addActionListener(e -> {
763            LinkingObject pp = (LinkingObject) pl;
764            String t = xTextField.getText();
765            boolean hasText = (t != null && t.length() > 0);
766            if (hasText || oldX > 0) {
767                pp.setULRL(t);
768                pp.updateSize();
769                dispose();
770            } else {
771                xTextField.setText("Link disappears with null text!");
772                oldX++;
773            }
774        });
775        okButton.getRootPane().setDefaultButton(okButton);
776        cancelButton.addActionListener(e -> {
777            LinkingObject pp = (LinkingObject) pl;
778            pp.setULRL(oldStr);
779            pp.updateSize();
780            dispose();
781        });
782        pack();
783    }
784
785    public void initZoom() {
786        oldD = pl.getScale();
787        log.debug("initZoom oldD = {}", oldD);
788        log.trace("  pl = {}", pl);
789
790        textX = new JLabel();
791        textX.setText(MessageFormat.format(Bundle.getMessage("Scale"), oldD * 100));
792        textX.setVisible(true);
793
794        SpinnerNumberModel model = new SpinnerNumberModel(100.0, 1.0, 5000.0, 1.0);
795        spinX = new JSpinner(model);
796        if (log.isDebugEnabled()) {
797            log.debug("scale%= {}", (int) Math.round(oldD * 100));
798        }
799        spinX.setToolTipText(Bundle.getMessage("enterZoom"));
800        spinX.setMaximumSize(new Dimension(
801                spinX.getMaximumSize().width, spinX.getPreferredSize().height));
802
803        getContentPane().setLayout(new GridBagLayout());
804
805        addSpinItems(false);
806
807        okButton.addActionListener(e -> {
808            double s = ((Number) spinX.getValue()).doubleValue() / 100;
809            pl.setScale(s);
810            pl.getEditor().setPaintScale(s);
811            textX.setText(MessageFormat.format(Bundle.getMessage("Scale"), pl.getScale() * 100));
812            dispose();
813        });
814        okButton.getRootPane().setDefaultButton(okButton);
815        cancelButton.addActionListener(e -> dispose());
816        pack();
817    }
818
819    public void initSetName() {
820        oldStr = pl.getEditor().getName();
821
822        textX = new JLabel();
823        textX.setText(MessageFormat.format(Bundle.getMessage("namelabel"), oldStr));
824        textX.setVisible(true);
825
826        xTextField = new JTextField(15);
827        xTextField.setText(oldStr);
828        xTextField.setToolTipText(Bundle.getMessage("PromptNewName"));
829//  xTextField.setMaximumSize(new Dimension(1000, xTextField.getPreferredSize().height));
830//    xTextField.getMaximumSize().width+100, xTextField.getPreferredSize().height));
831
832        getContentPane().setLayout(new GridBagLayout());
833
834        addTextItems();
835
836        okButton.addActionListener(e -> {
837            String t = xTextField.getText();
838            Editor ed = pl.getEditor();
839            ed.setName(t);
840            ed.setTitle();
841            textX.setText(MessageFormat.format(Bundle.getMessage("namelabel"), t));
842            dispose();
843        });
844        okButton.getRootPane().setDefaultButton(okButton);
845        cancelButton.addActionListener(e -> {
846            pl.getEditor().setName(oldStr);
847            dispose();
848        });
849        pack();
850    }
851
852    protected void addSpinItems(boolean addY) {
853        addItem(nameText, 0, 0, 2, true);
854        addItem(textX, 0, 1, 1, true);
855        addItem(spinX, 1, 1, 1, false);
856        if (addY) {
857            addItem(textY, 0, 2, 1, true);
858            addItem(spinY, 1, 2, 1, false);
859            addItem(cancelButton, 0, 3, 1, false);
860            addItem(okButton, 1, 3, 1, false);
861        } else {
862            addItem(cancelButton, 0, 2, 1, false);
863            addItem(okButton, 1, 2, 1, false);
864        }
865        validate();
866    }
867
868    private void addTextItems() {
869        addItem(nameText, 0, 0, 2, true);
870        addItem(textX, 0, 1, 1, true);
871        addItem(xTextField, 1, 1, 1, true);
872        addItem(cancelButton, 0, 2, 1, false);
873        addItem(okButton, 1, 2, 1, false);
874        validate();
875    }
876
877    private void addItem(JComponent c, int x, int y, int w, boolean horzExpand) {
878        GridBagConstraints gc = new GridBagConstraints();
879        gc.gridx = x;
880        gc.gridy = y;
881        gc.gridwidth = w;
882        gc.weightx = 100.0;
883        gc.weighty = 100.0;
884        gc.anchor = GridBagConstraints.CENTER;
885        if (horzExpand) {
886            gc.fill = GridBagConstraints.HORIZONTAL;  // text field will expand
887        }
888        getContentPane().add(c, gc);
889    }
890
891    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CoordinateEdit.class);
892
893}