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