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; 015 016import jmri.jmrix.can.CanSystemConnectionMemo; 017import jmri.jmrix.can.cbus.node.CbusNode; 018import jmri.jmrix.can.cbus.node.CbusNodeEvent; 019import jmri.jmrix.can.cbus.node.CbusNodeTableDataModel; 020import jmri.jmrix.can.cbus.node.CbusNodeSingleEventTableDataModel; 021import jmri.jmrix.can.cbus.CbusNameService; 022import jmri.util.JmriJFrame; 023import jmri.util.swing.JmriJOptionPane; 024import jmri.util.swing.JSpinnerUtil; 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 JSpinnerUtil.setCommitsOnValidEdit(numberSpinnernd, true); 106 107 numberSpinnerEv = new JSpinner(new SpinnerNumberModel(Math.max(0,_ndEv.getEn()), 0, 65535, 1)); 108 JSpinner.NumberEditor neditor = new JSpinner.NumberEditor(numberSpinnernd, "#"); 109 numberSpinnernd.setEditor(neditor); 110 JSpinnerUtil.setCommitsOnValidEdit(numberSpinnerEv, true); 111 112 infoPane = new JPanel(); 113 infoPane.setLayout(new BorderLayout() ); 114 115 JPanel pageStartPanel = new JPanel(); 116 pageStartPanel.setLayout(new BoxLayout(pageStartPanel, BoxLayout.Y_AXIS)); 117 118 JPanel buttonPanel = new JPanel(); 119 120 buttonPanel.add(frameCopyButton); 121 buttonPanel.add(frameResetButton); 122 buttonPanel.add(framedeletebutton); 123 124 JPanel spinnerPanel= new JPanel(); // row container 125 126 spinnerPanel.add(new JLabel(Bundle.getMessage("CbusNode"), JLabel.RIGHT)); 127 spinnerPanel.add(numberSpinnernd); 128 spinnerPanel.add(new JLabel(Bundle.getMessage("CbusEvent"), JLabel.RIGHT)); 129 spinnerPanel.add(numberSpinnerEv); 130 131 spinnerPanel.add(framenewevbutton); 132 spinnerPanel.add(frameeditevbutton); 133 134 JPanel evNamePanel= new JPanel(); // row container 135 ndEvNameLabel = new JLabel(""); 136 evNamePanel.add(ndEvNameLabel); 137 138 pageStartPanel.add(spinnerPanel); 139 pageStartPanel.add(evNamePanel); 140 pageStartPanel.add( buttonPanel); 141 pageStartPanel.add(new JSeparator(SwingConstants.HORIZONTAL)); 142 143 pageStartPanel.validate(); 144 145 JPanel generic = new JPanel(); 146 147 generic.setLayout( new BorderLayout() ); 148 149 CbusNodeSingleEventEditTablePane genericEVTable = new CbusNodeSingleEventEditTablePane(singleEVModel); 150 genericEVTable.initComponents(_memo, mainpane); 151 generic.add( genericEVTable ); 152 153 tabbedPane = new JTabbedPane(); 154 155 tabbedPane.addTab(("Template"), null); 156 tabbedPane.addTab(("Generic"), generic); 157 158 tabbedPane.setEnabledAt(0,false); 159 tabbedPane.setSelectedIndex(1); 160 161 infoPane.add(pageStartPanel, BorderLayout.PAGE_START); 162 infoPane.add(tabbedPane, BorderLayout.CENTER); 163 164 this.add(infoPane); 165 166 this.setPreferredSize(new Dimension(500, 300)); 167 168 this.pack(); 169 this.setResizable(true); 170 171 this.validate(); 172 this.repaint(); 173 174 this.setVisible(true); 175 this.toFront(); 176 177 updateButtons(); 178 179 frameResetButton.addActionListener((ActionEvent e) -> { 180 singleEVModel.resetnewEVs(); 181 numberSpinnernd.setValue(Math.max(0,_ndEv.getNn() ) ); 182 numberSpinnerEv.setValue(Math.max(1,_ndEv.getEn() ) ); 183 }); 184 185 186 ActionListener copyEvClicked = ae -> { 187 log.debug("copy button"); 188 isNewEvent=true; 189 updateButtons(); 190 }; 191 frameCopyButton.addActionListener(copyEvClicked); 192 193 // node will check for other nodes in learn mode 194 ActionListener newEvClicked = ae -> { 195 busy_dialog = new jmri.util.swing.BusyDialog(this, "Teaching Node", false); 196 busy_dialog.start(); 197 // setEnabled(false); 198 jmri.util.ThreadingUtil.runOnLayout( ()->{ 199 singleEVModel.passNewEvToNode(this); 200 }); 201 }; 202 framenewevbutton.addActionListener(newEvClicked); 203 204 ActionListener deleteClicked = ae -> { 205 if (_node == null ){ 206 return; 207 } 208 int response = JmriJOptionPane.showConfirmDialog( 209 this, 210 Bundle.getMessage("NdDelEvConfrm", 211 new CbusNameService(_memo).getEventNodeString(_ndEv.getNn(), _ndEv.getEn() ), _node ), 212 Bundle.getMessage("DelEvPopTitle"), 213 JmriJOptionPane.YES_NO_OPTION, 214 JmriJOptionPane.ERROR_MESSAGE); 215 if (response == JmriJOptionPane.YES_OPTION) { 216 217 busy_dialog = new jmri.util.swing.BusyDialog(this, "Deleting Event", false); 218 busy_dialog.start(); 219 // setEnabled(false); 220 jmri.util.ThreadingUtil.runOnLayout( ()->{ 221 _node.getNodeEventManager().deleteEvOnNode(_ndEv.getNn(), _ndEv.getEn() ); 222 }); 223 } 224 }; 225 framedeletebutton.addActionListener(deleteClicked); 226 227 ActionListener editEvClicked = ae -> { 228 busy_dialog = new jmri.util.swing.BusyDialog(this, "Teaching Node", false); 229 busy_dialog.start(); 230 // setEnabled(false); 231 jmri.util.ThreadingUtil.runOnLayout( ()->{ 232 singleEVModel.passEditEvToNode(this); 233 }); 234 }; 235 frameeditevbutton.addActionListener(editEvClicked); 236 237 numberSpinnerEv.addChangeListener((ChangeEvent e) -> { 238 updateButtons(); 239 }); 240 241 numberSpinnernd.addChangeListener((ChangeEvent e) -> { 242 updateButtons(); 243 }); 244 245 } 246 247 public boolean spinnersDirty(){ 248 return ( _ndEv.getNn() != getNodeVal() ) || ( _ndEv.getEn() != getEventVal() ); 249 } 250 251 private void notifyLearnEvoutcome( String message) { 252 253 _ndEv.setNn( getNodeVal() ); 254 _ndEv.setEn( getEventVal() ); 255 _ndEv.setEvArr( Arrays.copyOf( 256 singleEVModel.newEVs, 257 singleEVModel.newEVs.length) ); 258 singleEVModel.fireTableDataChanged(); 259 260 updateButtons(); 261 busy_dialog.finish(); 262 busy_dialog = null; 263 264 if (!message.isEmpty() ) { 265 JmriJOptionPane.showMessageDialog( this, 266 Bundle.getMessage("NdEvVarWriteError"), Bundle.getMessage("WarningTitle"), 267 JmriJOptionPane.ERROR_MESSAGE); 268 } 269 } 270 271 private void notifyDeleteEvoutcome(String message) { 272 busy_dialog.finish(); 273 busy_dialog = null; 274 updateButtons(); 275 if (!message.isEmpty()) { 276 JmriJOptionPane.showMessageDialog( this, 277 message, Bundle.getMessage("WarningTitle"), 278 JmriJOptionPane.ERROR_MESSAGE); 279 } 280 this.dispose(); 281 } 282 283 /** {@inheritDoc} */ 284 @Override 285 public void propertyChange(PropertyChangeEvent ev){ 286 287 if (ev.getPropertyName().equals("DELETEEVCOMPLETE")) { 288 jmri.util.ThreadingUtil.runOnGUIEventually( ()->{ 289 notifyDeleteEvoutcome(ev.getNewValue().toString()); 290 }); 291 } 292 if (ev.getPropertyName().equals("ADDEVCOMPLETE")) { 293 jmri.util.ThreadingUtil.runOnGUIEventually( ()->{ 294 notifyLearnEvoutcome(ev.getNewValue().toString()); 295 }); 296 } 297 } 298 299 // called on startup + when number spinners changed 300 private void updateButtons(){ 301 302 setTitle( getTitle() ); 303 304 ndEvNameLabel.setText("<html><div style='text-align: center;'>" + 305 new CbusNameService(_memo).getEventNodeString(getNodeVal(), getEventVal() ) 306 + "</div></html>"); 307 308 frameResetButton.setEnabled(singleEVModel.isTableDirty() || spinnersDirty()); 309 310 if (_node == null){ 311 return; 312 } 313 framenewevbutton.setVisible(isNewEvent); 314 frameeditevbutton.setVisible(!isNewEvent); 315 framedeletebutton.setVisible(!isNewEvent); 316 frameCopyButton.setVisible(!isNewEvent); 317 318 if (isNewEvent) { 319 320 if ( _node.getNodeEventManager().getNodeEvent(getNodeVal(),getEventVal() ) == null ) { 321 322 framenewevbutton.setEnabled(true); 323 framenewevbutton.setToolTipText(null); 324 325 } else { 326 framenewevbutton.setEnabled(false); 327 framenewevbutton.setToolTipText("Event Already on Node"); 328 } 329 330 } 331 else { // not new event 332 if ( spinnersDirty() || singleEVModel.isTableDirty() ){ 333 if ( _node.getNodeEventManager().getNodeEvent(getNodeVal(),getEventVal() ) == null ) { 334 frameeditevbutton.setEnabled(true); 335 frameeditevbutton.setToolTipText(null); 336 } else { 337 if ( spinnersDirty() ) { 338 frameeditevbutton.setEnabled(false); 339 frameeditevbutton.setToolTipText("Event Already on Node"); 340 } 341 else { 342 frameeditevbutton.setEnabled(true); 343 frameeditevbutton.setToolTipText(null); 344 } 345 } 346 } else { 347 frameeditevbutton.setEnabled(false); 348 frameeditevbutton.setToolTipText(null); 349 } 350 if ( spinnersDirty() ) { 351 framedeletebutton.setEnabled(false); 352 framedeletebutton.setToolTipText("Cannot Delete an edited event or node number"); 353 } 354 else { 355 framedeletebutton.setEnabled(true); 356 framedeletebutton.setToolTipText(null); 357 } 358 } 359 } 360 361 public int getEventVal(){ 362 return (Integer) numberSpinnerEv.getValue(); 363 } 364 365 public int getNodeVal(){ 366 return (Integer) numberSpinnernd.getValue(); 367 } 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public String getTitle() { 374 375 if (nodeModel==null){ 376 return Bundle.getMessage("NewEvent"); 377 } 378 379 if (_node!=null) { 380 StringBuilder title = new StringBuilder(); 381 if ( isNewEvent ) { 382 title.append( Bundle.getMessage("NewEvent")) 383 .append(" "); 384 } else { 385 title.append( Bundle.getMessage("EditEvent")) 386 .append( 387 new CbusNameService(_memo).getEventNodeString(_ndEv.getNn(), _ndEv.getEn() )); 388 } 389 title.append("on ") 390 .append( Bundle.getMessage("CbusNode")) 391 .append( _node); 392 return title.toString(); 393 } else { 394 return Bundle.getMessage("NewEvent"); 395 } 396 } 397 398 /** 399 * {@inheritDoc} 400 */ 401 @Override 402 public void tableChanged(TableModelEvent e) { 403 updateButtons(); 404 } 405 406 /** 407 * {@inheritDoc} 408 */ 409 @Override 410 public void dispose() { 411 if (_node!=null) { 412 _node.removePropertyChangeListener(this); 413 } 414 if ( mainpane != null ){ 415 mainpane.clearEditEventFrame(); 416 } 417 super.dispose(); 418 } 419 420 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeEditEventFrame.class); 421 422}