001package jmri.jmrix.can.cbus.swing.nodeconfig; 002 003import java.awt.BorderLayout; 004import java.awt.Dimension; 005import java.awt.event.ActionEvent; 006import java.awt.event.ActionListener; 007import java.beans.PropertyChangeListener; 008import java.beans.PropertyChangeEvent; 009import java.util.Arrays; 010 011import javax.swing.*; 012import javax.swing.event.ChangeEvent; 013import javax.swing.event.TableModelEvent; 014import javax.swing.event.TableModelListener; 015import javax.swing.text.DefaultFormatter; 016 017import jmri.jmrix.can.CanSystemConnectionMemo; 018import jmri.jmrix.can.cbus.node.CbusNode; 019import jmri.jmrix.can.cbus.node.CbusNodeEvent; 020import jmri.jmrix.can.cbus.node.CbusNodeTableDataModel; 021import jmri.jmrix.can.cbus.node.CbusNodeSingleEventTableDataModel; 022import jmri.jmrix.can.cbus.CbusNameService; 023import jmri.util.JmriJFrame; 024import jmri.util.swing.JmriJOptionPane; 025 026/** 027 * Frame to control an instance of CBUS highlighter to highlight events. 028 * 029 * @author Steve Young Copyright (C) 2019 030 */ 031public class CbusNodeEditEventFrame extends JmriJFrame 032 implements TableModelListener, PropertyChangeListener { 033 034 private CbusNodeSingleEventTableDataModel singleEVModel; 035 private final CbusNodeTableDataModel nodeModel; 036 037 private JSpinner numberSpinnerEv; 038 private JSpinner numberSpinnernd; 039 040 private JButton framedeletebutton; 041 private JButton frameeditevbutton; 042 private JButton frameCopyButton; 043 private JTabbedPane tabbedPane; 044 private CbusNodeEvent _ndEv; 045 private CbusNode _node; 046 private JLabel ndEvNameLabel; 047 private final NodeConfigToolPane mainpane; 048 private jmri.util.swing.BusyDialog busy_dialog; 049 050 private CanSystemConnectionMemo _memo; 051 052 private JPanel infoPane; 053 private JButton framenewevbutton; 054 private JButton frameResetButton; 055 private boolean isNewEvent; 056 057 /** 058 * Create a new instance of CbusNodeEditEventFrame. 059 * @param tp The main NodeConfigToolPane this is a part of 060 */ 061 public CbusNodeEditEventFrame(NodeConfigToolPane tp) { 062 super(); 063 mainpane = tp; 064 nodeModel = mainpane.getNodeModel(); 065 } 066 067 public void initComponents(CanSystemConnectionMemo memo, CbusNodeEvent ndEv) { 068 _ndEv = ndEv; 069 _memo = memo; 070 singleEVModel = new CbusNodeSingleEventTableDataModel(memo, 5, 071 CbusNodeSingleEventTableDataModel.MAX_COLUMN, _ndEv); // controller, row, column 072 singleEVModel.addTableModelListener(this); 073 074 screenInit(); 075 } 076 077 private void screenInit() { 078 079 if (infoPane != null ){ 080 infoPane.setVisible(false); 081 infoPane = null; 082 } 083 084 if ( _ndEv == null ){ 085 return; 086 } 087 088 _node = nodeModel.getNodeByNodeNum( _ndEv.getParentNn() ); 089 if (_node!=null) { 090 _node.addPropertyChangeListener(this); 091 } 092 isNewEvent = _node!=null && 093 _node.getNodeEventManager().getNodeEvent(_ndEv.getNn(), _ndEv.getEn()) == null; 094 log.debug("isNewEvent {}", isNewEvent); 095 096 frameeditevbutton = new JButton(Bundle.getMessage("EditEvent")); 097 framenewevbutton = new JButton(Bundle.getMessage("NewEvent")); 098 framedeletebutton = new JButton(Bundle.getMessage("ButtonDelete")); 099 frameResetButton = new JButton(("Reset New Values")); 100 frameCopyButton = new JButton(("Copy Event")); 101 102 numberSpinnernd = new JSpinner(new SpinnerNumberModel(Math.max(0,_ndEv.getNn()), 0, 65535, 1)); 103 JSpinner.NumberEditor editor = new JSpinner.NumberEditor(numberSpinnernd, "#"); 104 numberSpinnernd.setEditor(editor); 105 JFormattedTextField fieldNd = (JFormattedTextField) editor.getComponent(0); 106 DefaultFormatter formatterNd = (DefaultFormatter) fieldNd.getFormatter(); 107 formatterNd.setCommitsOnValidEdit(true); 108 109 numberSpinnerEv = new JSpinner(new SpinnerNumberModel(Math.max(0,_ndEv.getEn()), 0, 65535, 1)); 110 JSpinner.NumberEditor neditor = new JSpinner.NumberEditor(numberSpinnernd, "#"); 111 numberSpinnernd.setEditor(neditor); 112 JFormattedTextField fieldEv = (JFormattedTextField) neditor.getComponent(0); 113 DefaultFormatter formatterEv = (DefaultFormatter) fieldEv.getFormatter(); 114 formatterEv.setCommitsOnValidEdit(true); 115 116 infoPane = new JPanel(); 117 infoPane.setLayout(new BorderLayout() ); 118 119 JPanel pageStartPanel = new JPanel(); 120 pageStartPanel.setLayout(new BoxLayout(pageStartPanel, BoxLayout.Y_AXIS)); 121 122 JPanel buttonPanel = new JPanel(); 123 124 buttonPanel.add(frameCopyButton); 125 buttonPanel.add(frameResetButton); 126 buttonPanel.add(framedeletebutton); 127 128 JPanel spinnerPanel= new JPanel(); // row container 129 130 spinnerPanel.add(new JLabel(Bundle.getMessage("CbusNode"), JLabel.RIGHT)); 131 spinnerPanel.add(numberSpinnernd); 132 spinnerPanel.add(new JLabel(Bundle.getMessage("CbusEvent"), JLabel.RIGHT)); 133 spinnerPanel.add(numberSpinnerEv); 134 135 spinnerPanel.add(framenewevbutton); 136 spinnerPanel.add(frameeditevbutton); 137 138 JPanel evNamePanel= new JPanel(); // row container 139 ndEvNameLabel = new JLabel(""); 140 evNamePanel.add(ndEvNameLabel); 141 142 pageStartPanel.add(spinnerPanel); 143 pageStartPanel.add(evNamePanel); 144 pageStartPanel.add( buttonPanel); 145 pageStartPanel.add(new JSeparator(SwingConstants.HORIZONTAL)); 146 147 pageStartPanel.validate(); 148 149 JPanel generic = new JPanel(); 150 151 generic.setLayout( new BorderLayout() ); 152 153 CbusNodeSingleEventEditTablePane genericEVTable = new CbusNodeSingleEventEditTablePane(singleEVModel); 154 genericEVTable.initComponents(_memo, mainpane); 155 generic.add( genericEVTable ); 156 157 tabbedPane = new JTabbedPane(); 158 159 tabbedPane.addTab(("Template"), null); 160 tabbedPane.addTab(("Generic"), generic); 161 162 tabbedPane.setEnabledAt(0,false); 163 tabbedPane.setSelectedIndex(1); 164 165 infoPane.add(pageStartPanel, BorderLayout.PAGE_START); 166 infoPane.add(tabbedPane, BorderLayout.CENTER); 167 168 this.add(infoPane); 169 170 this.setPreferredSize(new Dimension(500, 300)); 171 172 this.pack(); 173 this.setResizable(true); 174 175 this.validate(); 176 this.repaint(); 177 178 this.setVisible(true); 179 this.toFront(); 180 181 updateButtons(); 182 183 frameResetButton.addActionListener((ActionEvent e) -> { 184 singleEVModel.resetnewEVs(); 185 numberSpinnernd.setValue(Math.max(0,_ndEv.getNn() ) ); 186 numberSpinnerEv.setValue(Math.max(1,_ndEv.getEn() ) ); 187 }); 188 189 190 ActionListener copyEvClicked = ae -> { 191 log.debug("copy button"); 192 isNewEvent=true; 193 updateButtons(); 194 }; 195 frameCopyButton.addActionListener(copyEvClicked); 196 197 // node will check for other nodes in learn mode 198 ActionListener newEvClicked = ae -> { 199 busy_dialog = new jmri.util.swing.BusyDialog(this, "Teaching Node", false); 200 busy_dialog.start(); 201 // setEnabled(false); 202 jmri.util.ThreadingUtil.runOnLayout( ()->{ 203 singleEVModel.passNewEvToNode(this); 204 }); 205 }; 206 framenewevbutton.addActionListener(newEvClicked); 207 208 ActionListener deleteClicked = ae -> { 209 if (_node == null ){ 210 return; 211 } 212 int response = JmriJOptionPane.showConfirmDialog( 213 this, 214 Bundle.getMessage("NdDelEvConfrm", 215 new CbusNameService(_memo).getEventNodeString(_ndEv.getNn(), _ndEv.getEn() ), _node ), 216 Bundle.getMessage("DelEvPopTitle"), 217 JmriJOptionPane.YES_NO_OPTION, 218 JmriJOptionPane.ERROR_MESSAGE); 219 if (response == JmriJOptionPane.YES_OPTION) { 220 221 busy_dialog = new jmri.util.swing.BusyDialog(this, "Deleting Event", false); 222 busy_dialog.start(); 223 // setEnabled(false); 224 jmri.util.ThreadingUtil.runOnLayout( ()->{ 225 _node.getNodeEventManager().deleteEvOnNode(_ndEv.getNn(), _ndEv.getEn() ); 226 }); 227 } 228 }; 229 framedeletebutton.addActionListener(deleteClicked); 230 231 ActionListener editEvClicked = ae -> { 232 busy_dialog = new jmri.util.swing.BusyDialog(this, "Teaching Node", false); 233 busy_dialog.start(); 234 // setEnabled(false); 235 jmri.util.ThreadingUtil.runOnLayout( ()->{ 236 singleEVModel.passEditEvToNode(this); 237 }); 238 }; 239 frameeditevbutton.addActionListener(editEvClicked); 240 241 numberSpinnerEv.addChangeListener((ChangeEvent e) -> { 242 updateButtons(); 243 }); 244 245 numberSpinnernd.addChangeListener((ChangeEvent e) -> { 246 updateButtons(); 247 }); 248 249 } 250 251 public boolean spinnersDirty(){ 252 return ( _ndEv.getNn() != getNodeVal() ) || ( _ndEv.getEn() != getEventVal() ); 253 } 254 255 private void notifyLearnEvoutcome( String message) { 256 257 _ndEv.setNn( getNodeVal() ); 258 _ndEv.setEn( getEventVal() ); 259 _ndEv.setEvArr( Arrays.copyOf( 260 singleEVModel.newEVs, 261 singleEVModel.newEVs.length) ); 262 singleEVModel.fireTableDataChanged(); 263 264 updateButtons(); 265 busy_dialog.finish(); 266 busy_dialog = null; 267 268 if (!message.isEmpty() ) { 269 JmriJOptionPane.showMessageDialog( this, 270 Bundle.getMessage("NdEvVarWriteError"), Bundle.getMessage("WarningTitle"), 271 JmriJOptionPane.ERROR_MESSAGE); 272 } 273 } 274 275 private void notifyDeleteEvoutcome(String message) { 276 busy_dialog.finish(); 277 busy_dialog = null; 278 updateButtons(); 279 if (!message.isEmpty()) { 280 JmriJOptionPane.showMessageDialog( this, 281 message, Bundle.getMessage("WarningTitle"), 282 JmriJOptionPane.ERROR_MESSAGE); 283 } 284 this.dispose(); 285 } 286 287 /** {@inheritDoc} */ 288 @Override 289 public void propertyChange(PropertyChangeEvent ev){ 290 291 if (ev.getPropertyName().equals("DELETEEVCOMPLETE")) { 292 jmri.util.ThreadingUtil.runOnGUIEventually( ()->{ 293 notifyDeleteEvoutcome(ev.getNewValue().toString()); 294 }); 295 } 296 if (ev.getPropertyName().equals("ADDEVCOMPLETE")) { 297 jmri.util.ThreadingUtil.runOnGUIEventually( ()->{ 298 notifyLearnEvoutcome(ev.getNewValue().toString()); 299 }); 300 } 301 } 302 303 // called on startup + when number spinners changed 304 private void updateButtons(){ 305 306 setTitle( getTitle() ); 307 308 ndEvNameLabel.setText("<html><div style='text-align: center;'>" + 309 new CbusNameService(_memo).getEventNodeString(getNodeVal(), getEventVal() ) 310 + "</div></html>"); 311 312 frameResetButton.setEnabled(singleEVModel.isTableDirty() || spinnersDirty()); 313 314 if (_node == null){ 315 return; 316 } 317 framenewevbutton.setVisible(isNewEvent); 318 frameeditevbutton.setVisible(!isNewEvent); 319 framedeletebutton.setVisible(!isNewEvent); 320 frameCopyButton.setVisible(!isNewEvent); 321 322 if (isNewEvent) { 323 324 if ( _node.getNodeEventManager().getNodeEvent(getNodeVal(),getEventVal() ) == null ) { 325 326 framenewevbutton.setEnabled(true); 327 framenewevbutton.setToolTipText(null); 328 329 } else { 330 framenewevbutton.setEnabled(false); 331 framenewevbutton.setToolTipText("Event Already on Node"); 332 } 333 334 } 335 else { // not new event 336 if ( spinnersDirty() || singleEVModel.isTableDirty() ){ 337 if ( _node.getNodeEventManager().getNodeEvent(getNodeVal(),getEventVal() ) == null ) { 338 frameeditevbutton.setEnabled(true); 339 frameeditevbutton.setToolTipText(null); 340 } else { 341 if ( spinnersDirty() ) { 342 frameeditevbutton.setEnabled(false); 343 frameeditevbutton.setToolTipText("Event Already on Node"); 344 } 345 else { 346 frameeditevbutton.setEnabled(true); 347 frameeditevbutton.setToolTipText(null); 348 } 349 } 350 } else { 351 frameeditevbutton.setEnabled(false); 352 frameeditevbutton.setToolTipText(null); 353 } 354 if ( spinnersDirty() ) { 355 framedeletebutton.setEnabled(false); 356 framedeletebutton.setToolTipText("Cannot Delete an edited event or node number"); 357 } 358 else { 359 framedeletebutton.setEnabled(true); 360 framedeletebutton.setToolTipText(null); 361 } 362 } 363 } 364 365 public int getEventVal(){ 366 return (Integer) numberSpinnerEv.getValue(); 367 } 368 369 public int getNodeVal(){ 370 return (Integer) numberSpinnernd.getValue(); 371 } 372 373 /** 374 * {@inheritDoc} 375 */ 376 @Override 377 public String getTitle() { 378 379 if (nodeModel==null){ 380 return Bundle.getMessage("NewEvent"); 381 } 382 383 if (_node!=null) { 384 StringBuilder title = new StringBuilder(); 385 if ( isNewEvent ) { 386 title.append( Bundle.getMessage("NewEvent")) 387 .append(" "); 388 } else { 389 title.append( Bundle.getMessage("EditEvent")) 390 .append( 391 new CbusNameService(_memo).getEventNodeString(_ndEv.getNn(), _ndEv.getEn() )); 392 } 393 title.append("on ") 394 .append( Bundle.getMessage("CbusNode")) 395 .append( _node); 396 return title.toString(); 397 } else { 398 return Bundle.getMessage("NewEvent"); 399 } 400 } 401 402 /** 403 * {@inheritDoc} 404 */ 405 @Override 406 public void tableChanged(TableModelEvent e) { 407 updateButtons(); 408 } 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override 414 public void dispose() { 415 if (_node!=null) { 416 _node.removePropertyChangeListener(this); 417 } 418 if ( mainpane != null ){ 419 mainpane.clearEditEventFrame(); 420 } 421 super.dispose(); 422 } 423 424 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeEditEventFrame.class); 425 426}