001package jmri.jmrit.display.controlPanelEditor.shape; 002 003import java.awt.Color; 004import java.awt.FlowLayout; 005import java.awt.GraphicsDevice; 006import java.awt.Point; 007import java.awt.Rectangle; 008import java.awt.event.ActionEvent; 009 010import javax.swing.Box; 011import javax.swing.BoxLayout;// 012import javax.swing.ButtonGroup; 013import javax.swing.JButton; 014import javax.swing.JColorChooser; 015import javax.swing.JComboBox; 016import javax.swing.JComponent; 017import javax.swing.JLabel; 018import javax.swing.JPanel; 019import javax.swing.JRadioButton; 020import javax.swing.JScrollPane; 021import javax.swing.JSlider; 022import javax.swing.JTextField; 023import javax.swing.SwingConstants; 024import javax.swing.event.ChangeEvent; 025 026import jmri.InstanceManager; 027import jmri.Sensor; 028import jmri.SensorManager; 029import jmri.NamedBean.DisplayOptions; 030import jmri.jmrit.display.Editor; 031import jmri.jmrit.display.Positionable; 032import jmri.jmrit.display.controlPanelEditor.ControlPanelEditor; 033import jmri.swing.NamedBeanComboBox; 034import jmri.util.ThreadingUtil; 035import jmri.util.swing.JmriColorChooser; 036import jmri.util.swing.JmriJOptionPane; 037 038/** 039 * Frame to create/edit a Control Panel shape PositionableShape object. 040 * 041 * @author Pete Cressman Copyright (c) 2012 042 */ 043abstract class DrawFrame extends jmri.util.JmriJFrame { 044 045 private final Editor _editor; 046 protected PositionableShape _shape; // for use while editing 047 private PositionableShape _originalShape; // saved for use if cancelled 048 protected boolean _create; 049 050 int _lineWidth; 051 private Color _lineColor; 052 private Color _fillColor; 053 private JColorChooser _chooser; 054 private JRadioButton _lineColorButon; 055 private JRadioButton _fillColorButon; 056 private JSlider _lineSlider; 057 private JSlider _alphaSlider; 058 private final transient NamedBeanComboBox<Sensor> _sensorBox = new NamedBeanComboBox<>( 059 InstanceManager.getDefault(SensorManager.class), null, DisplayOptions.DISPLAYNAME); 060 private JRadioButton _hideShape; 061 private JRadioButton _changeLevel; 062 private JComboBox<String> _levelComboBox; 063 private final JPanel _contentPanel; 064 065 protected DrawFrame(String which, String title, PositionableShape ps, Editor ed, boolean create) { 066 super(false, false); 067 _shape = ps; 068 _editor = ed; 069 _create = create; 070 setTitle(Bundle.getMessage(which, Bundle.getMessage(title))); 071 072 _lineWidth = 1; 073 _lineColor = Color.black; 074 075 _contentPanel = new JPanel(); 076 _contentPanel.setLayout(new java.awt.BorderLayout(10, 10)); 077 _contentPanel.setLayout(new BoxLayout(_contentPanel, BoxLayout.Y_AXIS)); 078 079 if (_shape == null) { 080 _contentPanel.add(makeCreatePanel(title)); 081 } else { 082 // closingEvent will re-establish listener 083 _shape.removeListener(); 084 } 085 086 setContentPane(new JScrollPane(_contentPanel)); 087 088 addWindowListener(new java.awt.event.WindowAdapter() { 089 @Override 090 public void windowClosing(java.awt.event.WindowEvent e) { 091 closingEvent(true); 092 } 093 }); 094 ThreadingUtil.runOnGUI( () -> { 095 super.pack(); 096 if (_shape == null) { 097 Point loc = _editor.getLocationOnScreen(); 098 loc.x = Math.max(loc.x + 200, 0); 099 loc.y = Math.max(loc.y, 0); 100 setLocation(loc); 101 setVisible(true); 102 setAlwaysOnTop(true); 103 } 104 }); 105 //_shape != null finishes construction at setDisplayParams() 106 } 107 108 private static void addLabel(JPanel panel, String text) { 109 JLabel label = new JLabel(text); 110 label.setAlignmentX(JComponent.LEFT_ALIGNMENT); 111 panel.add(label); 112 } 113 114 private JPanel makeCreatePanel(String type) { 115 JPanel panel = new JPanel(); 116 panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); 117 java.awt.Dimension dim = new java.awt.Dimension(250, 8); 118 panel.add(Box.createRigidArea(dim)); 119 JPanel p = new JPanel(); 120 p.setLayout(new BoxLayout(p, BoxLayout.LINE_AXIS)); 121 p.add(Box.createHorizontalStrut(10)); 122 JPanel pp = new JPanel(); 123 pp.setLayout(new BoxLayout(pp, BoxLayout.PAGE_AXIS)); 124 if (type != null && type.equals("Polygon")) { 125 addLabel(pp, Bundle.getMessage("drawInstructions2a")); 126 addLabel(pp, Bundle.getMessage("drawInstructions2b")); 127 } else { 128 addLabel(pp, Bundle.getMessage("drawInstructions2", type)); 129 } 130 p.add(pp); 131 p.add(Box.createHorizontalStrut(10)); 132 panel.add(p); 133 panel.add(Box.createRigidArea(dim)); 134 setVisible(false); 135 setUndecorated(true); 136 setBackground(new Color(0.8f, 0.8f, 0.8f, 1.0f)); 137 return panel; 138 } 139 140 private JPanel makeEditPanel() { 141 JPanel panel = new JPanel(); 142 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 143 JPanel p = new JPanel(); 144 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 145 p.add(new JLabel(Bundle.getMessage("lineWidth"))); 146 JPanel pp = new JPanel(); 147 pp.add(new JLabel(Bundle.getMessage("thin"))); 148 _lineSlider = new JSlider(SwingConstants.HORIZONTAL, 1, 30, _lineWidth); 149 _lineSlider.addChangeListener((ChangeEvent e) -> widthChange()); 150 pp.add(_lineSlider); 151 pp.add(new JLabel(Bundle.getMessage("thick"))); 152 p.add(pp); 153 panel.add(p); 154 p = new JPanel(); 155 ButtonGroup bg = new ButtonGroup(); 156 _lineColorButon = new JRadioButton(Bundle.getMessage("lineColor")); 157 p.add(_lineColorButon); 158 bg.add(_lineColorButon); 159 _fillColorButon = new JRadioButton(Bundle.getMessage("fillColor")); 160 p.add(_fillColorButon); 161 bg.add(_fillColorButon); 162 _lineColorButon.setSelected(true); 163 panel.add(p); 164 _chooser = new JColorChooser(_lineColor); 165 _chooser.getSelectionModel().addChangeListener((ChangeEvent e) -> colorChange()); 166 _chooser.setPreviewPanel(new JPanel()); 167 _chooser = JmriColorChooser.extendColorChooser(_chooser); 168 panel.add(_chooser); 169 p = new JPanel(); 170 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 171 p.add(new JLabel(Bundle.getMessage("transparency"))); 172 pp = new JPanel(); 173 pp.add(new JLabel(Bundle.getMessage("transparent"))); 174 _alphaSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 255, _lineColor.getAlpha()); 175 _alphaSlider.addChangeListener((ChangeEvent e) -> alphaChange()); 176 pp.add(_alphaSlider); 177 _lineColorButon.addChangeListener((ChangeEvent e) -> buttonChange()); 178 pp.add(new JLabel(Bundle.getMessage("opaque"))); 179 p.add(pp); 180 panel.add(p); 181 return panel; 182 } 183 184 protected final JPanel makeSensorPanel() { 185 JPanel panel = new JPanel(); 186 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 187 panel.add(new JLabel(Bundle.getMessage("SensorMsg"))); 188 panel.add(new JLabel(Bundle.getMessage("MakeLabel", Bundle.getMessage("VisibleSensor")))); 189 _sensorBox.setAllowNull(true); // already filled with names of all existing sensors 190 _sensorBox.addActionListener((ActionEvent e) -> { 191 String msg = _shape.setControlSensor(_sensorBox.getSelectedItemDisplayName()); 192 log.debug("Setting sensor to {} after action", _sensorBox.getSelectedItemDisplayName()); 193 if (msg != null) { 194 JmriJOptionPane.showMessageDialog(panel, msg, Bundle.getMessage("ErrorSensor"), 195 JmriJOptionPane.INFORMATION_MESSAGE); // NOI18N 196 _sensorBox.setSelectedItem(null); 197 } 198 updateShape(); 199 }); 200 JPanel p = new JPanel(); 201 p.add(_sensorBox); 202 p.add(Box.createVerticalGlue()); 203 panel.add(p); 204 panel.add(Box.createVerticalGlue()); 205 206 _hideShape = new JRadioButton(Bundle.getMessage("HideOnSensor")); 207 _changeLevel = new JRadioButton(Bundle.getMessage("ChangeLevel")); 208 ButtonGroup bg = new ButtonGroup(); 209 bg.add(_hideShape); 210 bg.add(_changeLevel); 211 _levelComboBox = new JComboBox<>(); 212 _levelComboBox.addItem(Bundle.getMessage("SameLevel")); 213 for (int i = 1; i < 11; i++) { 214 _levelComboBox.addItem(Bundle.getMessage("Level") + " " + i); 215 } 216 _levelComboBox.addActionListener((ActionEvent evt) -> { 217 int level = _levelComboBox.getSelectedIndex(); 218 _shape.setChangeLevel(level); 219 }); 220 _hideShape.addActionListener((ActionEvent a) -> { 221 _shape.setHide(true); 222 _levelComboBox.setEnabled(false); 223 }); 224 _changeLevel.addActionListener((ActionEvent a) -> { 225 _shape.setHide(false); 226 _levelComboBox.setEnabled(true); 227 }); 228 JPanel p1 = new JPanel(); 229 p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS)); 230 p1.add(_hideShape); 231 p1.add(_changeLevel); 232 233 JPanel p2 = new JPanel(); 234 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS)); 235 p = new JPanel(); 236 p.add(_levelComboBox); 237 p2.add(p); 238 JPanel p3 = new JPanel(); 239 p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS)); 240 p3.add(p1); 241 p3.add(Box.createHorizontalGlue()); 242 p3.add(p2); 243 244 panel.add(Box.createVerticalGlue()); 245 panel.add(p3); 246 panel.add(Box.createVerticalGlue()); 247 248 JPanel pp = new JPanel(); 249 pp.setLayout(new BoxLayout(pp, BoxLayout.X_AXIS)); 250 pp.add(Box.createHorizontalStrut(80)); 251 pp.add(Box.createHorizontalGlue()); 252 pp.add(panel, java.awt.BorderLayout.CENTER); 253 pp.add(Box.createHorizontalGlue()); 254 return pp; 255 } 256 257 /** 258 * Create a panel for setting parameters for the PositionableShape. 259 * 260 * @return a parameters panel 261 */ 262 protected JPanel makeParamsPanel() { 263 JPanel panel = new JPanel(); 264 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 265 return panel; 266 } 267 268 /** 269 * Set parameters on the popup that will edit the PositionableShape. 270 * Called both for creation and editing by the PositionableShape 271 * @param ps a PositionableShape 272 */ 273 protected void setDisplayParams(PositionableShape ps) { 274 if (!_create) { 275 makeCopy(ps); 276 } 277 ShapeDrawer sd = ((ControlPanelEditor)ps.getEditor()).getShapeDrawer(); 278 if (!sd.setDrawFrame(this)) { 279 closingEvent(true); 280 return; 281 } 282 _contentPanel.removeAll(); 283 JPanel panel = new JPanel(); 284 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 285 panel.add(makeEditPanel()); 286 panel.add(makeParamsPanel()); 287 javax.swing.JTabbedPane tPanel = new javax.swing.JTabbedPane(); 288 tPanel.addTab(Bundle.getMessage("attributeTab"), null, 289 panel, Bundle.getMessage("drawInstructions1")); 290 291 _lineWidth = _shape.getLineWidth(); 292 _lineSlider.setValue(_lineWidth); 293 _lineColor = _shape.getLineColor(); 294 _fillColor = _shape.getFillColor(); 295 int alpha = _lineColor.getAlpha(); 296 if (alpha > 5) { 297 _alphaSlider.setValue(alpha); 298 _lineColorButon.setSelected(true); 299 _chooser.setColor(_lineColor); 300 } else { 301 _alphaSlider.setValue(_fillColor.getAlpha()); 302 _lineColorButon.setSelected(false); 303 _chooser.setColor(_fillColor); 304 } 305 306 tPanel.addTab(Bundle.getMessage("advancedTab"), null, 307 makeSensorPanel(), Bundle.getMessage("drawInstructions3a")); 308 _sensorBox.setSelectedItem(_shape.getControlSensor()); 309 _levelComboBox.setSelectedIndex(_shape.getChangeLevel()); 310 if (_shape.isHideOnSensor()) { 311 _hideShape.setSelected(true); 312 _levelComboBox.setEnabled(false); 313 } else { 314 _changeLevel.setSelected(true); 315 } 316 _contentPanel.add(tPanel); 317 _contentPanel.add(makeDoneButtonPanel()); 318 pack(); 319 InstanceManager.getDefault(jmri.util.PlaceWindow.class).nextTo(_editor, _shape, this); 320 setVisible(true); 321 setAlwaysOnTop(true); 322 if (log.isDebugEnabled()) { 323 Point pt1 = getLocation(); 324 GraphicsDevice device = getGraphicsConfiguration().getDevice(); 325 log.debug("setDisplayParams Screen device= {}: getLocation()= [{}, {}]", 326 device.getIDstring(), pt1.x, pt1.y); 327 } 328 } 329 330 /** 331 * Editing an existing shape (only make copy for cancel of edits). 332 * 333 * @param ps shape 334 */ 335 private void makeCopy(PositionableShape ps) { 336 // make a copy, but keep it out of editor's content 337 _originalShape = (PositionableShape) ps.deepClone(); 338 // cloning adds to editor's targetPane - (maybe fix needed in editor) 339 _originalShape.remove(); 340 // closingEvent will re-establish listener 341 _originalShape.removeListener(); 342 log.debug("_originalShape made"); 343 } 344 345 private JPanel makeDoneButtonPanel() { 346 JPanel panel = new JPanel(); 347 panel.setLayout(new FlowLayout()); 348// panel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); 349 panel.add(Box.createHorizontalGlue()); 350 JButton doneButton = new JButton(Bundle.getMessage("ButtonDone")); 351 doneButton.addActionListener((ActionEvent a) -> { 352 closingEvent(false); 353 JmriColorChooser.addRecentColor(_lineColor); 354 if (_fillColor != null) { 355 JmriColorChooser.addRecentColor(_fillColor); 356 } 357 }); 358 JPanel p =new JPanel(); 359 p.add(doneButton); 360 panel.add(p); 361 panel.add(Box.createHorizontalGlue()); 362 363 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 364 cancelButton.addActionListener((ActionEvent a) -> closingEvent(true)); 365 p =new JPanel(); 366 p.add(cancelButton); 367 panel.add(p); 368 panel.add(Box.createHorizontalGlue()); 369 return panel; 370 } 371 372 private void buttonChange() { 373 if (_lineColorButon.isSelected()) { 374// _chooser.getSelectionModel().setSelectedColor(_lineColor); 375 _chooser.setColor(_lineColor); 376 _alphaSlider.setValue(_lineColor.getAlpha()); 377 } else if (_fillColor != null) { 378// _chooser.getSelectionModel().setSelectedColor(_fillColor); 379 _chooser.setColor(_fillColor); 380 _alphaSlider.setValue(_fillColor.getAlpha()); 381 } else { 382 _alphaSlider.setValue(255); 383 } 384 _alphaSlider.revalidate(); 385 _alphaSlider.repaint(); 386 } 387 388 private void widthChange() { 389 _lineWidth = _lineSlider.getValue(); 390 if (_shape == null) { 391 return; 392 } 393 _shape.setLineWidth(_lineWidth); 394 updateShape(); 395 } 396 397 private void colorChange() { 398 Color c = _chooser.getColor(); 399 int alpha = c.getAlpha(); 400 log.debug("colorChange: color= {}, alpha= {} ", c, alpha); 401 _alphaSlider.setValue(c.getAlpha()); 402 if (_lineColorButon.isSelected()) { 403 _lineColor = new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); 404 if (_shape != null) { 405 _shape.setLineColor(_lineColor); 406 } 407 } else { 408 _fillColor = new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha); 409 if (_shape != null) { 410 _shape.setFillColor(_fillColor); 411 } 412 } 413 updateShape(); 414 } 415 416 private void alphaChange() { 417 int alpha = _alphaSlider.getValue(); 418 if (_lineColorButon.isSelected()) { 419 _lineColor = new Color(_lineColor.getRed(), _lineColor.getGreen(), _lineColor.getBlue(), alpha); 420 if (_shape != null) { 421 _shape.setLineColor(_lineColor); 422 } 423 } else if (_fillColorButon.isSelected() && _fillColor != null) { 424 _fillColor = new Color(_fillColor.getRed(), _fillColor.getGreen(), _fillColor.getBlue(), alpha); 425 if (_shape != null) { 426 _shape.setFillColor(_fillColor); 427 } 428 } 429 updateShape(); 430 } 431 432 protected void closingEvent(boolean cancel) { 433 log.debug("closingEvent for {}", getTitle()); 434 if (_shape != null) { 435 if (cancel) { 436 _shape.remove(); 437 if (_originalShape != null) { 438 try { 439 _originalShape.getEditor().putItem(_originalShape); 440 } catch (Positionable.DuplicateIdException e) { 441 // This should never happen 442 log.error("Editor.putItem() with has thrown DuplicateIdException", e); 443 } 444 _originalShape.setListener(); 445 } 446 } else { 447 _shape.setListener(); 448 ((ControlPanelEditor)_editor).setShapeSelect(true); 449 } 450 _shape.removeHandles(); 451 if(_shape instanceof PositionablePolygon) { 452 ((PositionablePolygon)_shape).editing(false); 453 } 454 } 455 ((ControlPanelEditor)_editor).getShapeDrawer().setDrawFrame(null); 456 _create = false; 457 _shape = null; // tells ShapeDrawer creation and editing is finished. 458 dispose(); 459 } 460 461 protected int getInteger(JTextField field, int value) { 462 try { 463 int i = Integer.parseInt(field.getText()); 464 if (i > 0) { 465 if (i < PositionableShape.SIZE) { 466 i = PositionableShape.SIZE; 467 } 468 return i; 469 } 470 } catch (NumberFormatException nfe) { 471 JmriJOptionPane.showMessageDialog(this, nfe, 472 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 473 } 474 field.setText(Integer.toString(value)); 475 return value; 476 } 477 478 protected void updateShape() { 479 if (_shape == null) { 480 return; 481 } 482 _shape.removeHandles(); 483 _shape.drawHandles(); 484 _shape.updateSize(); 485 _shape.getEditor().getTargetPanel().repaint(); 486 } 487 488 // these 2 methods update the JTextfields when mouse moves handles 489 abstract void setDisplayWidth(int w); 490 abstract void setDisplayHeight(int h); 491 492 protected abstract PositionableShape makeFigure(Rectangle r, Editor ed); 493 494 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DrawFrame.class); 495 496}