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