001package jmri.jmrit.logixng.tools.swing; 002 003import java.awt.*; 004import java.awt.event.ActionEvent; 005import java.awt.event.ItemEvent; 006import java.awt.event.KeyEvent; 007import java.beans.PropertyChangeListener; 008import java.beans.PropertyVetoException; 009import java.text.MessageFormat; 010import java.util.*; 011import java.util.List; 012import java.util.concurrent.atomic.AtomicBoolean; 013 014import javax.swing.*; 015import javax.swing.border.Border; 016import javax.swing.table.AbstractTableModel; 017import javax.swing.table.TableCellEditor; 018import javax.swing.table.TableColumn; 019import javax.swing.table.TableColumnModel; 020 021import jmri.*; 022import jmri.jmrit.beantable.BeanTableDataModel; 023import jmri.jmrit.logixng.*; 024import jmri.jmrit.logixng.util.LogixNG_Thread; 025import jmri.util.JmriJFrame; 026import jmri.util.swing.JmriJOptionPane; 027import jmri.util.table.ButtonEditor; 028import jmri.util.table.ButtonRenderer; 029 030/** 031 * Editor for LogixNG 032 * 033 * @author Dave Duchamp Copyright (C) 2007 (ConditionalListEdit) 034 * @author Pete Cressman Copyright (C) 2009, 2010, 2011 (ConditionalListEdit) 035 * @author Matthew Harris copyright (c) 2009 (ConditionalListEdit) 036 * @author Dave Sand copyright (c) 2017 (ConditionalListEdit) 037 * @author Daniel Bergqvist (c) 2019 038 * @author Dave Sand (c) 2021 039 */ 040public final class LogixNGEditor implements AbstractLogixNGEditor<LogixNG> { 041 042 BeanTableDataModel<LogixNG> beanTableDataModel; 043 044 LogixNG_Manager _logixNG_Manager = null; 045 LogixNG _curLogixNG = null; 046 047 ConditionalNG_Manager _conditionalNG_Manager = null; 048 ConditionalNGEditor _treeEdit = null; 049 ConditionalNGDebugger _debugger = null; 050 051 int _numConditionalNGs = 0; 052 boolean _inEditMode = false; 053 054 boolean _showReminder = false; 055 boolean _suppressReminder = false; 056 boolean _suppressIndirectRef = false; 057 private boolean _checkEnabled = jmri.InstanceManager.getDefault(jmri.configurexml.ShutdownPreferences.class).isStoreCheckEnabled(); 058 059 private final JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName")); // NOI18N 060 private final JLabel _sysNameLabel = new JLabel(Bundle.getMessage("SystemName") + ":"); // NOI18N 061 private final JLabel _userNameLabel = new JLabel(Bundle.getMessage("UserName") + ":"); // NOI18N 062 private final String systemNameAuto = this.getClass().getName() + ".AutoSystemName"; // NOI18N 063 private final JTextField _systemName = new JTextField(20); 064 private final JTextField _addUserName = new JTextField(20); 065 066 067 /** 068 * Create a new ConditionalNG List View editor. 069 * 070 * @param m the bean table model 071 * @param sName name of the LogixNG being edited 072 */ 073 public LogixNGEditor(BeanTableDataModel<LogixNG> m, String sName) { 074 this.beanTableDataModel = m; 075 _logixNG_Manager = InstanceManager.getDefault(jmri.jmrit.logixng.LogixNG_Manager.class); 076 _curLogixNG = _logixNG_Manager.getBySystemName(sName); 077 _conditionalNG_Manager = InstanceManager.getDefault(jmri.jmrit.logixng.ConditionalNG_Manager.class); 078 makeEditLogixNGWindow(); 079 } 080 081 // ------------ LogixNG Variables ------------ 082 private JmriJFrame _editLogixNGFrame = null; 083 JTextField editUserName = new JTextField(20); 084 JLabel status = new JLabel(" "); 085 086 // ------------ ConditionalNG Variables ------------ 087 private ConditionalNGTableModel _conditionalNGTableModel = null; 088 private JCheckBox _showStartupThreadsCheckBox = null; 089 private ConditionalNG _curConditionalNG = null; 090 int _conditionalRowNumber = 0; 091 boolean _inReorderMode = false; 092 boolean _inActReorder = false; 093 boolean _inVarReorder = false; 094 int _nextInOrder = 0; 095 096 // ------------ Select LogixNG/ConditionalNG Variables ------------ 097 JPanel _selectLogixNGPanel = null; 098 JPanel _selectConditionalNGPanel = null; 099// private JComboBox<String> _selectLogixNGComboBox = new JComboBox<>(); 100// private JComboBox<String> _selectConditionalNGComboBox = new JComboBox<>(); 101 TreeMap<String, String> _selectLogixNGMap = new TreeMap<>(); 102 ArrayList<String> _selectConditionalNGList = new ArrayList<>(); 103 104 // ------------ Edit ConditionalNG Variables ------------ 105 private boolean _inEditConditionalNGMode = false; 106 private JmriJFrame _editConditionalNGFrame = null; 107 JRadioButton _triggerOnChangeButton; 108 109 // ------------ Methods for Edit LogixNG Pane ------------ 110 111 /** 112 * Create and/or initialize the Edit LogixNG pane. 113 */ 114 void makeEditLogixNGWindow() { 115 editUserName.setText(_curLogixNG.getUserName()); 116 // clear conditional table if needed 117 if (_conditionalNGTableModel != null) { 118 _conditionalNGTableModel.fireTableStructureChanged(); 119 } 120 _inEditMode = true; 121 if (_editLogixNGFrame == null) { 122 if (_curLogixNG.getUserName() != null) { 123 _editLogixNGFrame = new JmriJFrame( 124 Bundle.getMessage("TitleEditLogixNG2", 125 _curLogixNG.getSystemName(), // NOI18N 126 _curLogixNG.getUserName()), // NOI18N 127 false, 128 false); 129 } else { 130 _editLogixNGFrame = new JmriJFrame( 131 Bundle.getMessage("TitleEditLogixNG", _curLogixNG.getSystemName()), // NOI18N 132 false, 133 false); 134 } 135 _editLogixNGFrame.addHelpMenu( 136 "package.jmri.jmrit.logixng.LogixNGTableEditor", true); // NOI18N 137 _editLogixNGFrame.setLocation(100, 30); 138 Container contentPane = _editLogixNGFrame.getContentPane(); 139 contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 140 JPanel panel1 = new JPanel(); 141 panel1.setLayout(new FlowLayout()); 142 JLabel systemNameLabel = new JLabel(Bundle.getMessage("ColumnSystemName") + ":"); // NOI18N 143 panel1.add(systemNameLabel); 144 JLabel fixedSystemName = new JLabel(_curLogixNG.getSystemName()); 145 panel1.add(fixedSystemName); 146 contentPane.add(panel1); 147 JPanel panel2 = new JPanel(); 148 panel2.setLayout(new FlowLayout()); 149 JLabel userNameLabel = new JLabel(Bundle.getMessage("ColumnUserName") + ":"); // NOI18N 150 panel2.add(userNameLabel); 151 panel2.add(editUserName); 152 editUserName.setToolTipText(Bundle.getMessage("LogixNGUserNameHint2")); // NOI18N 153 contentPane.add(panel2); 154 // add table of ConditionalNGs 155 JPanel pctSpace = new JPanel(); 156 pctSpace.setLayout(new FlowLayout()); 157 pctSpace.add(new JLabel(" ")); 158 contentPane.add(pctSpace); 159 JPanel pTitle = new JPanel(); 160 pTitle.setLayout(new FlowLayout()); 161 pTitle.add(new JLabel(Bundle.getMessage("ConditionalNGTableTitle"))); // NOI18N 162 contentPane.add(pTitle); 163 // initialize table of conditionals 164 _conditionalNGTableModel = new ConditionalNGTableModel(); 165 JTable conditionalTable = new JTable(_conditionalNGTableModel); 166 conditionalTable.setRowSelectionAllowed(false); 167 TableColumnModel conditionalColumnModel = conditionalTable 168 .getColumnModel(); 169 TableColumn sNameColumn = conditionalColumnModel 170 .getColumn(ConditionalNGTableModel.SNAME_COLUMN); 171 sNameColumn.setResizable(true); 172 sNameColumn.setMinWidth(100); 173 sNameColumn.setPreferredWidth(130); 174 TableColumn uNameColumn = conditionalColumnModel 175 .getColumn(ConditionalNGTableModel.UNAME_COLUMN); 176 uNameColumn.setResizable(true); 177 uNameColumn.setMinWidth(210); 178 uNameColumn.setPreferredWidth(260); 179 TableColumn threadColumn = conditionalColumnModel 180 .getColumn(ConditionalNGTableModel.THREAD_COLUMN); 181 threadColumn.setResizable(true); 182 threadColumn.setMinWidth(210); 183 threadColumn.setPreferredWidth(260); 184 TableColumn buttonColumn = conditionalColumnModel 185 .getColumn(ConditionalNGTableModel.BUTTON_COLUMN); 186 TableColumn buttonDeleteColumn = conditionalColumnModel 187 .getColumn(ConditionalNGTableModel.BUTTON_DELETE_COLUMN); 188 TableColumn buttonEditThreadsColumn = conditionalColumnModel 189 .getColumn(ConditionalNGTableModel.BUTTON_EDIT_THREADS_COLUMN); 190 191 // install button renderer and editor 192 ButtonRenderer buttonRenderer = new ButtonRenderer(); 193 conditionalTable.setDefaultRenderer(JButton.class, buttonRenderer); 194 TableCellEditor buttonEditor = new ButtonEditor(new JButton()); 195 conditionalTable.setDefaultEditor(JButton.class, buttonEditor); 196 JButton testButton = new JButton("XXXXXX"); // NOI18N 197 JButton testButton2 = new JButton("XXXXXXXXXX"); // NOI18N 198 conditionalTable.setRowHeight(testButton.getPreferredSize().height); 199 buttonColumn.setMinWidth(testButton.getPreferredSize().width); 200 buttonColumn.setMaxWidth(testButton.getPreferredSize().width); 201 buttonColumn.setResizable(false); 202 buttonDeleteColumn.setMinWidth(testButton.getPreferredSize().width); 203 buttonDeleteColumn.setMaxWidth(testButton.getPreferredSize().width); 204 buttonDeleteColumn.setResizable(false); 205 buttonEditThreadsColumn.setMinWidth(testButton2.getPreferredSize().width); 206 buttonEditThreadsColumn.setMaxWidth(testButton2.getPreferredSize().width); 207 buttonEditThreadsColumn.setResizable(false); 208 209 JScrollPane conditionalTableScrollPane = new JScrollPane(conditionalTable); 210 Dimension dim = conditionalTable.getPreferredSize(); 211 dim.height = 450; 212 conditionalTableScrollPane.getViewport().setPreferredSize(dim); 213 contentPane.add(conditionalTableScrollPane); 214 215 _showStartupThreadsCheckBox = new JCheckBox(Bundle.getMessage("ShowStartupThreadCheckBox")); 216 contentPane.add(_showStartupThreadsCheckBox); 217 _showStartupThreadsCheckBox.addActionListener((evt) -> { 218 _conditionalNGTableModel.setShowStartupThreads( 219 _showStartupThreadsCheckBox.isSelected()); 220 }); 221 222 // add message area between table and buttons 223 JPanel panel4 = new JPanel(); 224 panel4.setLayout(new BoxLayout(panel4, BoxLayout.Y_AXIS)); 225 JPanel panel41 = new JPanel(); 226 panel41.setLayout(new FlowLayout()); 227 panel41.add(status); 228 panel4.add(panel41); 229 JPanel panel42 = new JPanel(); 230 panel42.setLayout(new FlowLayout()); 231 // ConditionalNG panel buttons - New ConditionalNG 232 JButton newConditionalNGButton = new JButton(Bundle.getMessage("NewConditionalNGButton")); // NOI18N 233 panel42.add(newConditionalNGButton); 234 newConditionalNGButton.addActionListener((e) -> { 235 newConditionalNGPressed(e); 236 }); 237 newConditionalNGButton.setToolTipText(Bundle.getMessage("NewConditionalNGButtonHint")); // NOI18N 238 // ConditionalNG panel buttons - Reorder 239 JButton reorderButton = new JButton(Bundle.getMessage("ReorderButton")); // NOI18N 240 panel42.add(reorderButton); 241 reorderButton.addActionListener((e) -> { 242 reorderPressed(e); 243 }); 244 reorderButton.setToolTipText(Bundle.getMessage("ReorderButtonHint")); // NOI18N 245 // ConditionalNG panel buttons - Calculate 246 JButton executeButton = new JButton(Bundle.getMessage("ExecuteButton")); // NOI18N 247 panel42.add(executeButton); 248 executeButton.addActionListener((e) -> { 249 executePressed(e); 250 }); 251 executeButton.setToolTipText(Bundle.getMessage("ExecuteButtonHint")); // NOI18N 252 panel4.add(panel42); 253 Border panel4Border = BorderFactory.createEtchedBorder(); 254 panel4.setBorder(panel4Border); 255 contentPane.add(panel4); 256 // add buttons at bottom of window 257 JPanel panel5 = new JPanel(); 258 panel5.setLayout(new FlowLayout()); 259 // Bottom Buttons - Done LogixNG 260 JButton done = new JButton(Bundle.getMessage("ButtonDone")); // NOI18N 261 panel5.add(done); 262 done.addActionListener((e) -> { 263 donePressed(e); 264 }); 265 done.setToolTipText(Bundle.getMessage("DoneButtonHint")); // NOI18N 266 // Delete LogixNG 267 JButton delete = new JButton(Bundle.getMessage("ButtonDelete")); // NOI18N 268 panel5.add(delete); 269 delete.addActionListener((e) -> { 270 deletePressed(); 271 }); 272 delete.setToolTipText(Bundle.getMessage("DeleteLogixNGButtonHint")); // NOI18N 273 contentPane.add(panel5); 274 } 275 276 _editLogixNGFrame.addWindowListener(new java.awt.event.WindowAdapter() { 277 @Override 278 public void windowClosing(java.awt.event.WindowEvent e) { 279 if (_inEditMode) { 280 donePressed(null); 281 } else { 282 finishDone(); 283 } 284 } 285 }); 286 _editLogixNGFrame.pack(); 287 _editLogixNGFrame.setVisible(true); 288 } 289 290 @Override 291 public void bringToFront() { 292 if (_editLogixNGFrame != null) { 293 _editLogixNGFrame.setVisible(true); 294 } 295 } 296 297 /** 298 * Display reminder to save. 299 */ 300 void showSaveReminder() { 301 if (_showReminder && !_checkEnabled) { 302 if (InstanceManager.getNullableDefault(jmri.UserPreferencesManager.class) != null) { 303 InstanceManager.getDefault(jmri.UserPreferencesManager.class). 304 showInfoMessage(Bundle.getMessage("ReminderTitle"), // NOI18N 305 Bundle.getMessage("ReminderSaveString", // NOI18N 306 Bundle.getMessage("MenuItemLogixNGTable")), // NOI18N 307 getClassName(), 308 "remindSaveLogixNG"); // NOI18N 309 } 310 } 311 } 312 313 /** 314 * Respond to the Reorder Button in the Edit LogixNG pane. 315 * 316 * @param e The event heard 317 */ 318 void reorderPressed(ActionEvent e) { 319 if (checkEditConditionalNG()) { 320 return; 321 } 322 // Check if reorder is reasonable 323 _showReminder = true; 324 _nextInOrder = 0; 325 _inReorderMode = true; 326 status.setText(Bundle.getMessage("ReorderMessage")); // NOI18N 327 _conditionalNGTableModel.fireTableDataChanged(); 328 } 329 330 /** 331 * Respond to the First/Next (Delete) Button in the Edit LogixNG window. 332 * 333 * @param row index of the row to put as next in line (instead of the one 334 * that was supposed to be next) 335 */ 336 void swapConditionalNG(int row) { 337 _curLogixNG.swapConditionalNG(_nextInOrder, row); 338 _nextInOrder++; 339 if (_nextInOrder >= _numConditionalNGs) { 340 _inReorderMode = false; 341 } 342 //status.setText(""); 343 _conditionalNGTableModel.fireTableDataChanged(); 344 } 345 346 /** 347 * Responds to the Execute Button in the Edit LogixNG window. 348 * 349 * @param e The event heard 350 */ 351 void executePressed(ActionEvent e) { 352 if (checkEditConditionalNG()) { 353 return; 354 } 355 // are there ConditionalNGs to execute? 356 if (_numConditionalNGs > 0) { 357 // There are conditionals to calculate 358 for (int i = 0; i < _numConditionalNGs; i++) { 359 ConditionalNG c = _curLogixNG.getConditionalNG(i); 360 if (c == null) { 361 log.error("Invalid conditional system name when executing"); // NOI18N 362 } else { 363 c.execute(); 364 } 365 } 366 // force the table to update 367// conditionalNGTableModel.fireTableDataChanged(); 368 } 369 } 370 371 /** 372 * Respond to the Done button in the Edit LogixNG window. 373 * <p> 374 * Note: We also get here if the Edit LogixNG window is dismissed, or if the 375 * Add button is pressed in the Logic Table with an active Edit LogixNG 376 * window. 377 * 378 * @param e The event heard 379 */ 380 void donePressed(ActionEvent e) { 381 if (checkEditConditionalNG()) { 382 return; 383 } 384 // Check if the User Name has been changed 385 String uName = editUserName.getText().trim(); 386 if (!(uName.equals(_curLogixNG.getUserName()))) { 387 // user name has changed - check if already in use 388 if (uName.length() > 0) { 389 LogixNG p = _logixNG_Manager.getByUserName(uName); 390 if (p != null) { 391 // LogixNG with this user name already exists 392 log.error("Failure to update LogixNG with Duplicate User Name: {}", uName); // NOI18N 393 JmriJOptionPane.showMessageDialog(_editLogixNGFrame, 394 Bundle.getMessage("Error_UserNameInUse"), 395 Bundle.getMessage("ErrorTitle"), // NOI18N 396 JmriJOptionPane.ERROR_MESSAGE); 397 return; 398 } 399 } 400 // user name is unique, change it 401 // user name is unique, change it 402 logixNG_Data.clear(); 403 logixNG_Data.put("chgUname", uName); // NOI18N 404 fireEditorEvent(); 405 } 406 // complete update and activate LogixNG 407 finishDone(); 408 } 409 410 void finishDone() { 411 showSaveReminder(); 412 _inEditMode = false; 413 if (_editLogixNGFrame != null) { 414 _editLogixNGFrame.setVisible(false); 415 _editLogixNGFrame.dispose(); 416 _editLogixNGFrame = null; 417 } 418 logixNG_Data.clear(); 419 logixNG_Data.put("Finish", _curLogixNG.getSystemName()); // NOI18N 420 fireEditorEvent(); 421 } 422 423 /** 424 * Respond to the Delete button in the Edit LogixNG window. 425 */ 426 void deletePressed() { 427 if (checkEditConditionalNG()) { 428 return; 429 } 430 431 _showReminder = true; 432 logixNG_Data.clear(); 433 logixNG_Data.put("Delete", _curLogixNG.getSystemName()); // NOI18N 434 fireEditorEvent(); 435 finishDone(); 436 } 437 438 /** 439 * Respond to the New ConditionalNG Button in Edit LogixNG Window. 440 * 441 * @param e The event heard 442 */ 443 void newConditionalNGPressed(ActionEvent e) { 444 if (checkEditConditionalNG()) { 445 return; 446 } 447 448 // make an Add Item Frame 449 if (showAddLogixNGFrame()) { 450 if (!checkConditionalNGSysName()) { 451 return; 452 } 453 if (_systemName.getText().isEmpty() && _autoSystemName.isSelected()) { 454 _systemName.setText(InstanceManager.getDefault(ConditionalNG_Manager.class).getAutoSystemName()); 455 } 456 457 // Create ConditionalNG 458 _curConditionalNG = 459 _conditionalNG_Manager.createConditionalNG(_curLogixNG, _systemName.getText(), _addUserName.getText()); 460 461 if (_curConditionalNG == null) { 462 // should never get here unless there is an assignment conflict 463 log.error("Failure to create ConditionalNG"); // NOI18N 464 return; 465 } 466 // add to LogixNG at the end of the calculate order 467 _conditionalNGTableModel.fireTableRowsInserted(_numConditionalNGs, _numConditionalNGs); 468 _conditionalRowNumber = _numConditionalNGs; 469 _numConditionalNGs++; 470 _showReminder = true; 471 makeEditConditionalNGWindow(); 472 } 473 } 474 475 /** 476 * Check validity of ConditionalNG system name. 477 * <p> 478 * Fixes name if it doesn't start with "IQC" or is missing the $ for alpha suffixes. 479 * 480 * @return false if the name fails the NameValidity check 481 */ 482 boolean checkConditionalNGSysName() { 483 if (_autoSystemName.isSelected()) { 484 return true; 485 } 486 487 var sName = _systemName.getText().trim(); 488 var prefix = _conditionalNG_Manager.getSubSystemNamePrefix(); 489 490 if (!sName.isEmpty() && !sName.startsWith(prefix)) { 491 var isNumber = sName.matches("^\\d+$"); 492 var hasDollar = sName.startsWith("$"); 493 494 var newName = new StringBuilder(prefix); 495 if (!isNumber && !hasDollar) { 496 newName.append("$"); 497 } 498 newName.append(sName); 499 sName = newName.toString(); 500 } 501 502 if (_conditionalNG_Manager.validSystemNameFormat(sName) != jmri.Manager.NameValidity.VALID) { 503 JmriJOptionPane.showMessageDialog(null, 504 Bundle.getMessage("Error_SystemName_Format", sName), Bundle.getMessage("ErrorTitle"), // NOI18N 505 JmriJOptionPane.ERROR_MESSAGE); 506 return false; 507 } 508 509 _systemName.setText(sName); 510 return true; 511 } 512 513 /** 514 * Create or edit action/expression dialog. 515 */ 516 private boolean showAddLogixNGFrame() { 517 518 AtomicBoolean result = new AtomicBoolean(false); 519 520 JDialog dialog = new JDialog( 521 _editLogixNGFrame, 522 Bundle.getMessage("AddConditionalNGDialogTitle"), 523 true); 524// frame.addHelpMenu( 525// "package.jmri.jmrit.logixng.tools.swing.ConditionalNGAddEdit", true); // NOI18N 526 Container contentPanel = dialog.getContentPane(); 527 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 528 529 JPanel p; 530 p = new JPanel(); 531// p.setLayout(new FlowLayout()); 532 p.setLayout(new java.awt.GridBagLayout()); 533 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 534 c.gridwidth = 1; 535 c.gridheight = 1; 536 c.gridx = 0; 537 c.gridy = 0; 538 c.anchor = java.awt.GridBagConstraints.EAST; 539 p.add(_sysNameLabel, c); 540 c.gridy = 1; 541 p.add(_userNameLabel, c); 542 c.gridx = 1; 543 c.gridy = 0; 544 c.anchor = java.awt.GridBagConstraints.WEST; 545 c.weightx = 1.0; 546 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 547 p.add(_systemName, c); 548 c.gridy = 1; 549 p.add(_addUserName, c); 550 c.gridx = 2; 551 c.gridy = 1; 552 c.anchor = java.awt.GridBagConstraints.WEST; 553 c.weightx = 1.0; 554 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 555 c.gridy = 0; 556 p.add(_autoSystemName, c); 557 558 _systemName.setText(""); 559 _systemName.setEnabled(true); 560 _addUserName.setText(""); 561 562 _addUserName.setToolTipText(Bundle.getMessage("UserNameHint")); // NOI18N 563// _addUserName.setToolTipText("LogixNGUserNameHint"); // NOI18N 564 _systemName.setToolTipText(Bundle.getMessage("LogixNGSystemNameHint")); // NOI18N 565// _systemName.setToolTipText("LogixNGSystemNameHint"); // NOI18N 566 contentPanel.add(p); 567 // set up message 568 JPanel panel3 = new JPanel(); 569 panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS)); 570 JPanel panel31 = new JPanel(); 571// panel31.setLayout(new FlowLayout()); 572 JPanel panel32 = new JPanel(); 573 JLabel message1 = new JLabel(Bundle.getMessage("AddMessage1")); // NOI18N 574 panel31.add(message1); 575 JLabel message2 = new JLabel(Bundle.getMessage("AddMessage2")); // NOI18N 576 panel32.add(message2); 577 578 // set up create and cancel buttons 579 JPanel panel5 = new JPanel(); 580 panel5.setLayout(new FlowLayout()); 581 582 // Get panel for the item 583 panel3.add(panel31); 584 panel3.add(panel32); 585 contentPanel.add(panel3); 586 587 // Cancel 588 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 589 panel5.add(cancel); 590 cancel.addActionListener((ActionEvent e) -> { 591 dialog.dispose(); 592 }); 593 cancel.setToolTipText(Bundle.getMessage("CancelEditLogixNGButtonHint")); // NOI18N 594 595 // Create 596 JButton create = new JButton(Bundle.getMessage("ButtonCreate")); // NOI18N 597 create.addActionListener((ActionEvent e2) -> { 598 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> { 599 prefMgr.setCheckboxPreferenceState(systemNameAuto, _autoSystemName.isSelected()); 600 }); 601 result.set(true); 602 dialog.dispose(); 603 }); 604 create.setToolTipText(Bundle.getMessage("CreateButtonHint")); // NOI18N 605 606 panel5.add(create); 607 608 dialog.addWindowListener(new java.awt.event.WindowAdapter() { 609 @Override 610 public void windowClosing(java.awt.event.WindowEvent e) { 611 dialog.dispose(); 612 } 613 }); 614 615 contentPanel.add(panel5); 616 617 _autoSystemName.addItemListener((ItemEvent e) -> { 618 autoSystemName(); 619 }); 620// addLogixNGFrame.setLocationRelativeTo(component); 621 dialog.pack(); 622 dialog.setLocationRelativeTo(null); 623 624 _autoSystemName.setSelected(true); 625 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> { 626 _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(systemNameAuto, true)); 627 }); 628 629 dialog.setVisible(true); 630 631 return result.get(); 632 } 633 634 /** 635 * Enable/disable fields for data entry when user selects to have system 636 * name automatically generated. 637 */ 638 void autoSystemName() { 639 if (_autoSystemName.isSelected()) { 640 _systemName.setEnabled(false); 641 _sysNameLabel.setEnabled(false); 642 } else { 643 _systemName.setEnabled(true); 644 _sysNameLabel.setEnabled(true); 645 } 646 } 647 648 // ============ Edit Conditional Window and Methods ============ 649 650 /** 651 * Create and/or initialize the Edit Conditional window. 652 * <p> 653 * Note: you can get here via the New Conditional button 654 * (newConditionalPressed) or via an Edit button in the Conditional table of 655 * the Edit Logix window. 656 */ 657 void makeEditConditionalNGWindow() { 658 // Create a new LogixNG edit view, add the listener. 659 _treeEdit = new ConditionalNGEditor(_curConditionalNG); 660 _treeEdit.initComponents(); 661 _treeEdit.setVisible(true); 662 _inEditConditionalNGMode = true; 663 _editConditionalNGFrame = _treeEdit; 664 _editConditionalNGFrame.addHelpMenu( 665 "package.jmri.jmrit.logixng.ConditionalNGEditor", true); // NOI18N 666 667 final LogixNGEditor logixNGEditor = this; 668 _treeEdit.addLogixNGEventListener(new LogixNGEventListenerImpl(logixNGEditor)); 669 } 670 671 /** 672 * Create and/or initialize the Edit Conditional window. 673 * <p> 674 * Note: you can get here via the New Conditional button 675 * (newConditionalPressed) or via an Edit button in the Conditional table of 676 * the Edit Logix window. 677 */ 678 void makeDebugConditionalNGWindow() { 679 // Create a new LogixNG edit view, add the listener. 680 _debugger = new ConditionalNGDebugger(_curConditionalNG); 681 _debugger.initComponents(); 682 _debugger.setVisible(true); 683 _inEditConditionalNGMode = true; 684 _editConditionalNGFrame = _debugger; 685 686 final LogixNGEditor logixNGEditor = this; 687 _debugger.addLogixNGEventListener(new LogixNG_DebuggerEventListenerImpl(logixNGEditor)); 688 } 689 690 // ------------ Methods for Edit ConditionalNG Pane ------------ 691 692 /** 693 * Respond to Edit Button in the ConditionalNG table of the Edit LogixNG Window. 694 * 695 * @param rx index (row number) of ConditionalNG to be edited 696 */ 697 void editConditionalNGPressed(int rx) { 698 if (checkEditConditionalNG()) { 699 return; 700 } 701 // get ConditionalNG to edit 702 _curConditionalNG = _curLogixNG.getConditionalNG(rx); 703 if (_curConditionalNG == null) { 704 log.error("Attempted edit of non-existant conditional."); // NOI18N 705 return; 706 } 707 _conditionalRowNumber = rx; 708 // get action variables 709 makeEditConditionalNGWindow(); 710 } 711 712 /** 713 * Respond to Edit Button in the ConditionalNG table of the Edit LogixNG Window. 714 * 715 * @param rx index (row number) of ConditionalNG to be edited 716 */ 717 void debugConditionalNGPressed(int rx) { 718 if (checkEditConditionalNG()) { 719 return; 720 } 721 // get ConditionalNG to edit 722 _curConditionalNG = _curLogixNG.getConditionalNG(rx); 723 if (_curConditionalNG == null) { 724 log.error("Attempted edit of non-existant conditional."); // NOI18N 725 return; 726 } 727 _conditionalRowNumber = rx; 728 // get action variables 729 makeDebugConditionalNGWindow(); 730 } 731 732 /** 733 * Check if edit of a conditional is in progress. 734 * 735 * @return true if this is the case, after showing dialog to user 736 */ 737 private boolean checkEditConditionalNG() { 738 if (_inEditConditionalNGMode) { 739 // set window visible first so that Dialog appears on top of window 740 _editConditionalNGFrame.setVisible(true); 741 // Already editing a ConditionalNG, ask for completion of that edit 742 JmriJOptionPane.showMessageDialog(_editConditionalNGFrame, 743 Bundle.getMessage("Error_ConditionalNGInEditMode", _curConditionalNG.getSystemName()), // NOI18N 744 Bundle.getMessage("ErrorTitle"), // NOI18N 745 JmriJOptionPane.ERROR_MESSAGE); 746 return true; 747 } 748 return false; 749 } 750 751 boolean checkConditionalNGUserName(String uName, LogixNG logixNG) { 752 if ((uName != null) && (!(uName.equals("")))) { 753 for (int i=0; i < logixNG.getNumConditionalNGs(); i++) { 754 ConditionalNG p = logixNG.getConditionalNG(i); 755 if (uName.equals(p.getUserName())) { 756 // ConditionalNG with this user name already exists 757 log.error("Failure to update ConditionalNG with Duplicate User Name: {}", uName); // NOI18N 758 JmriJOptionPane.showMessageDialog(_editConditionalNGFrame, 759 Bundle.getMessage("Error10"), // NOI18N 760 Bundle.getMessage("ErrorTitle"), // NOI18N 761 JmriJOptionPane.ERROR_MESSAGE); 762 return false; 763 } 764 } 765 } // else return false; 766 return true; 767 } 768 769 /** 770 * Check form of ConditionalNG systemName. 771 * 772 * @param sName system name of bean to be checked 773 * @return false if sName is empty string or null 774 */ 775 boolean checkConditionalNGSystemName(String sName) { 776 if ((sName != null) && (!(sName.equals("")))) { 777 ConditionalNG p = _curLogixNG.getConditionalNG(sName); 778 if (p != null) { 779 return false; 780 } 781 } else { 782 return false; 783 } 784 return true; 785 } 786 787 // ------------ Table Models ------------ 788 789 /** 790 * Table model for ConditionalNGs in the Edit LogixNG pane. 791 */ 792 public final class ConditionalNGTableModel extends AbstractTableModel 793 implements PropertyChangeListener { 794 795 public static final int SNAME_COLUMN = 0; 796 public static final int UNAME_COLUMN = SNAME_COLUMN + 1; 797 public static final int THREAD_COLUMN = UNAME_COLUMN + 1; 798 public static final int ENABLED_COLUMN = THREAD_COLUMN + 1; 799 public static final int STARTUP_COLUMN = ENABLED_COLUMN + 1; 800 public static final int BUTTON_COLUMN = STARTUP_COLUMN + 1; 801 public static final int BUTTON_DEBUG_COLUMN = BUTTON_COLUMN + 1; 802 public static final int BUTTON_DELETE_COLUMN = BUTTON_DEBUG_COLUMN + 1; 803 public static final int BUTTON_EDIT_THREADS_COLUMN = BUTTON_DELETE_COLUMN + 1; 804 public static final int NUM_COLUMNS = BUTTON_EDIT_THREADS_COLUMN + 1; 805 806 private boolean _showStartupThreads; 807 808 809 public ConditionalNGTableModel() { 810 super(); 811 updateConditionalNGListeners(); 812 } 813 814 synchronized void updateConditionalNGListeners() { 815 // first, remove listeners from the individual objects 816 ConditionalNG c; 817 _numConditionalNGs = _curLogixNG.getNumConditionalNGs(); 818 for (int i = 0; i < _numConditionalNGs; i++) { 819 // if object has been deleted, it's not here; ignore it 820 c = _curLogixNG.getConditionalNG(i); 821 if (c != null) { 822 c.removePropertyChangeListener(this); 823 } 824 } 825 // and add them back in 826 for (int i = 0; i < _numConditionalNGs; i++) { 827 c = _curLogixNG.getConditionalNG(i); 828 if (c != null) { 829 c.addPropertyChangeListener(this); 830 } 831 } 832 } 833 834 public void setShowStartupThreads(boolean showStartupThreads) { 835 _showStartupThreads = showStartupThreads; 836 fireTableRowsUpdated(0, _curLogixNG.getNumConditionalNGs()-1); 837 } 838 839 @Override 840 public void propertyChange(java.beans.PropertyChangeEvent e) { 841 if (e.getPropertyName().equals("length")) { // NOI18N 842 // a new NamedBean is available in the manager 843 updateConditionalNGListeners(); 844 fireTableDataChanged(); 845 } else if (matchPropertyName(e)) { 846 // a value changed. 847 fireTableDataChanged(); 848 } 849 } 850 851 /** 852 * Check if this property event is announcing a change this table should 853 * display. 854 * <p> 855 * Note that events will come both from the NamedBeans and from the 856 * manager. 857 * 858 * @param e the event heard 859 * @return true if a change in State or Appearance was heard 860 */ 861 boolean matchPropertyName(java.beans.PropertyChangeEvent e) { 862 return (e.getPropertyName().contains("UserName") || // NOI18N 863 e.getPropertyName().contains("Thread")); // NOI18N 864 } 865 866 @Override 867 public Class<?> getColumnClass(int c) { 868 if ((c == BUTTON_COLUMN) 869 || (c == BUTTON_DEBUG_COLUMN) 870 || (c == BUTTON_DELETE_COLUMN) 871 || (c == BUTTON_EDIT_THREADS_COLUMN)) { 872 return JButton.class; 873 } 874 if (c == STARTUP_COLUMN 875 || c == ENABLED_COLUMN) { 876 return Boolean.class; 877 } 878 return String.class; 879 } 880 881 @Override 882 public int getColumnCount() { 883 return NUM_COLUMNS; 884 } 885 886 @Override 887 public int getRowCount() { 888 return (_numConditionalNGs); 889 } 890 891 @Override 892 public boolean isCellEditable(int r, int c) { 893 if (!_inReorderMode) { 894 return ((c == UNAME_COLUMN) 895 || (c == ENABLED_COLUMN) 896 || (c == STARTUP_COLUMN) 897 || (c == BUTTON_COLUMN) 898 || ((c == BUTTON_DEBUG_COLUMN) && InstanceManager.getDefault(LogixNGPreferences.class).getInstallDebugger()) 899 || (c == BUTTON_DELETE_COLUMN) 900 || (c == BUTTON_EDIT_THREADS_COLUMN)); 901 } else if (c == BUTTON_COLUMN) { 902 if (r >= _nextInOrder) { 903 return (true); 904 } 905 } 906 return (false); 907 } 908 909 @Override 910 public String getColumnName(int col) { 911 switch (col) { 912 case SNAME_COLUMN: 913 return Bundle.getMessage("ColumnSystemName"); // NOI18N 914 case UNAME_COLUMN: 915 return Bundle.getMessage("ColumnUserName"); // NOI18N 916 case ENABLED_COLUMN: 917 return Bundle.getMessage("ColumnHeadEnabled"); // NOI18N 918 case THREAD_COLUMN: 919 return Bundle.getMessage("ConditionalNG_Table_ColumnThreadName"); // NOI18N 920 case STARTUP_COLUMN: 921 return Bundle.getMessage("ConditionalNG_Table_ColumnStartup"); // NOI18N 922 case BUTTON_COLUMN: 923 return ""; // no label 924 case BUTTON_DEBUG_COLUMN: 925 return ""; // no label 926 case BUTTON_DELETE_COLUMN: 927 return ""; // no label 928 case BUTTON_EDIT_THREADS_COLUMN: 929 return ""; // no label 930 default: 931 throw new IllegalArgumentException("Unknown column"); 932 } 933 } 934 935 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES", 936 justification = "better to keep cases in column order rather than to combine") 937 public int getPreferredWidth(int col) { 938 switch (col) { 939 case SNAME_COLUMN: 940 return new JTextField(6).getPreferredSize().width; 941 case UNAME_COLUMN: 942 return new JTextField(17).getPreferredSize().width; 943 case ENABLED_COLUMN: 944 return new JTextField(5).getPreferredSize().width; 945 case THREAD_COLUMN: 946 return new JTextField(10).getPreferredSize().width; 947 case STARTUP_COLUMN: 948 return new JTextField(6).getPreferredSize().width; 949 case BUTTON_COLUMN: 950 return new JTextField(6).getPreferredSize().width; 951 case BUTTON_DEBUG_COLUMN: 952 return new JTextField(6).getPreferredSize().width; 953 case BUTTON_DELETE_COLUMN: 954 return new JTextField(6).getPreferredSize().width; 955 case BUTTON_EDIT_THREADS_COLUMN: 956 return new JTextField(12).getPreferredSize().width; 957 default: 958 throw new IllegalArgumentException("Unknown column"); 959 } 960 } 961 962 @Override 963 public Object getValueAt(int r, int col) { 964 ConditionalNG c; 965 int rx = r; 966 if ((rx > _numConditionalNGs) || (_curLogixNG == null)) { 967 return null; 968 } 969 switch (col) { 970 case BUTTON_COLUMN: 971 if (!_inReorderMode) { 972 return Bundle.getMessage("ButtonEdit"); // NOI18N 973 } else if (_nextInOrder == 0) { 974 return Bundle.getMessage("ButtonFirst"); // NOI18N 975 } else if (_nextInOrder <= r) { 976 return Bundle.getMessage("ButtonNext"); // NOI18N 977 } else { 978 return Integer.toString(rx + 1); 979 } 980 case BUTTON_DEBUG_COLUMN: 981 return Bundle.getMessage("ConditionalNG_Table_ButtonDebug"); // NOI18N 982 case BUTTON_DELETE_COLUMN: 983 return Bundle.getMessage("ButtonDelete"); // NOI18N 984 case BUTTON_EDIT_THREADS_COLUMN: 985 return Bundle.getMessage("ConditionalNG_Table_ButtonEditThreads"); // NOI18N 986 case SNAME_COLUMN: 987 return _curLogixNG.getConditionalNG(rx); 988 case UNAME_COLUMN: 989 //log.debug("ConditionalNGTableModel: {}", _curLogixNG.getConditionalNGByNumberOrder(rx)); // NOI18N 990 c = _curLogixNG.getConditionalNG(rx); 991 if (c != null) { 992 return c.getUserName(); 993 } 994 return ""; 995 case ENABLED_COLUMN: 996 c = _curLogixNG.getConditionalNG(rx); 997 if (c != null) { 998 return c.isEnabled(); 999 } 1000 return null; 1001 case THREAD_COLUMN: 1002 if (_showStartupThreads) { 1003 return LogixNG_Thread.getThread( 1004 _curLogixNG.getConditionalNG(r).getStartupThreadId()) 1005 .getThreadName(); 1006 } else { 1007 return _curLogixNG.getConditionalNG(r).getCurrentThread().getThreadName(); 1008 } 1009 case STARTUP_COLUMN: 1010 c = _curLogixNG.getConditionalNG(rx); 1011 if (c != null) { 1012 return c.isExecuteAtStartup(); 1013 } 1014 return false; 1015 default: 1016 throw new IllegalArgumentException("Unknown column"); 1017 } 1018 } 1019 1020 private void buttonStartupClicked(int row, Object value) { 1021 _curConditionalNG = _curLogixNG.getConditionalNG(row); 1022 if (_curConditionalNG == null) { 1023 log.error("Attempted edit of non-existant conditional."); // NOI18N 1024 return; 1025 } 1026 if (!(value instanceof Boolean)) { 1027 throw new IllegalArgumentException("value is not a Boolean"); 1028 } 1029 _curConditionalNG.setExecuteAtStartup((boolean)value); 1030 } 1031 1032 private void buttonColumnClicked(int row, int col) { 1033 if (_inReorderMode) { 1034 swapConditionalNG(row); 1035 } else { 1036 // Use separate Runnable so window is created on top 1037 class WindowMaker implements Runnable { 1038 1039 int theRow; 1040 1041 WindowMaker(int r) { 1042 theRow = r; 1043 } 1044 1045 @Override 1046 public void run() { 1047 editConditionalNGPressed(theRow); 1048 } 1049 } 1050 WindowMaker t = new WindowMaker(row); 1051 javax.swing.SwingUtilities.invokeLater(t); 1052 } 1053 } 1054 1055 private void buttonDebugClicked(int row, int col) { 1056 if (_inReorderMode) { 1057 swapConditionalNG(row); 1058 } else { 1059 // Use separate Runnable so window is created on top 1060 class WindowMaker implements Runnable { 1061 1062 int theRow; 1063 1064 WindowMaker(int r) { 1065 theRow = r; 1066 } 1067 1068 @Override 1069 public void run() { 1070 debugConditionalNGPressed(theRow); 1071 } 1072 } 1073 WindowMaker t = new WindowMaker(row); 1074 javax.swing.SwingUtilities.invokeLater(t); 1075 } 1076 } 1077 1078 private void deleteConditionalNG(int row) { 1079 DeleteBeanWorker worker = new DeleteBeanWorker(_curLogixNG.getConditionalNG(row), row); 1080 worker.execute(); 1081 } 1082 1083 private void changeUserName(Object value, int row) { 1084 String uName = (String) value; 1085 ConditionalNG cn = _curLogixNG.getConditionalNGByUserName(uName); 1086 if (cn == null) { 1087 ConditionalNG cdl = _curLogixNG.getConditionalNG(row); 1088 cdl.setUserName(uName.trim()); // N11N 1089 fireTableRowsUpdated(row, row); 1090 } else { 1091 // Duplicate user name 1092 if (cn != _curLogixNG.getConditionalNG(row)) { 1093 messageDuplicateConditionalNGUserName(cn.getSystemName()); 1094 } 1095 } 1096 } 1097 1098 @Override 1099 public void setValueAt(Object value, int row, int col) { 1100 if ((row > _numConditionalNGs) || (_curLogixNG == null)) { 1101 return; 1102 } 1103 switch (col) { 1104 case STARTUP_COLUMN: 1105 buttonStartupClicked(row, value); 1106 break; 1107 case BUTTON_COLUMN: 1108 buttonColumnClicked(row, col); 1109 break; 1110 case BUTTON_DEBUG_COLUMN: 1111 buttonDebugClicked(row, col); 1112 break; 1113 case BUTTON_DELETE_COLUMN: 1114 deleteConditionalNG(row); 1115 break; 1116 case BUTTON_EDIT_THREADS_COLUMN: 1117 EditThreadsDialog dialog = new EditThreadsDialog(_curLogixNG.getConditionalNG(row)); 1118 dialog.showDialog(); 1119 break; 1120 case SNAME_COLUMN: 1121 throw new IllegalArgumentException("System name cannot be changed"); 1122 case UNAME_COLUMN: 1123 changeUserName(value, row); 1124 break; 1125 case ENABLED_COLUMN: 1126 ConditionalNG c = _curLogixNG.getConditionalNG(row); 1127 if (c != null) { 1128 boolean v = c.isEnabled(); 1129 c.setEnabled(!v); 1130 } 1131 break; 1132 default: 1133 throw new IllegalArgumentException("Unknown column"); 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Send a duplicate Conditional user name message for Edit Logix pane. 1140 * 1141 * @param svName proposed name that duplicates an existing name 1142 */ 1143 void messageDuplicateConditionalNGUserName(String svName) { 1144 JmriJOptionPane.showMessageDialog(null, 1145 Bundle.getMessage("Error30", svName), 1146 Bundle.getMessage("ErrorTitle"), // NOI18N 1147 JmriJOptionPane.ERROR_MESSAGE); 1148 } 1149 1150 private String getClassName() { 1151 // The class that is returned must have a default constructor, 1152 // a constructor with no parameters. 1153 return jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName(); 1154 } 1155 1156 1157 // ------------ LogixNG Notifications ------------ 1158 // The ConditionalNG views support some direct changes to the parent logix. 1159 // This custom event is used to notify the parent LogixNG that changes are requested. 1160 // When the event occurs, the parent LogixNG can retrieve the necessary information 1161 // to carry out the actions. 1162 // 1163 // 1) Notify the calling LogixNG that the LogixNG user name has been changed. 1164 // 2) Notify the calling LogixNG that the conditional view is closing 1165 // 3) Notify the calling LogixNG that it is to be deleted 1166 /** 1167 * Create a custom listener event. 1168 */ 1169 public interface LogixNGEventListener extends EventListener { 1170 1171 void logixNGEventOccurred(); 1172 } 1173 1174 /** 1175 * Maintain a list of listeners -- normally only one. 1176 */ 1177 List<EditorEventListener> listenerList = new ArrayList<>(); 1178 1179 /** 1180 * This contains a list of commands to be processed by the listener 1181 * recipient. 1182 */ 1183 private HashMap<String, String> logixNG_Data = new HashMap<>(); 1184 1185 /** 1186 * Add a listener. 1187 * 1188 * @param listener The recipient 1189 */ 1190 @Override 1191 public void addEditorEventListener(EditorEventListener listener) { 1192 listenerList.add(listener); 1193 } 1194 1195 /** 1196 * Remove a listener -- not used. 1197 * 1198 * @param listener The recipient 1199 */ 1200 @Override 1201 public void removeEditorEventListener(EditorEventListener listener) { 1202 listenerList.remove(listener); 1203 } 1204 1205 /** 1206 * Notify the listeners to check for new data. 1207 */ 1208 private void fireEditorEvent() { 1209 for (EditorEventListener l : listenerList) { 1210 l.editorEventOccurred(logixNG_Data); 1211 } 1212 } 1213 1214 1215 private class LogixNGEventListenerImpl implements ConditionalNGEditor.ConditionalNGEventListener { 1216 1217 private final LogixNGEditor _logixNGEditor; 1218 1219 public LogixNGEventListenerImpl(LogixNGEditor logixNGEditor) { 1220 this._logixNGEditor = logixNGEditor; 1221 } 1222 1223 @Override 1224 public void conditionalNGEventOccurred() { 1225 String lgxName = _curLogixNG.getSystemName(); 1226 _treeEdit.logixNGData.forEach((key, value) -> { 1227 if (key.equals("Finish")) { // NOI18N 1228 _treeEdit = null; 1229 _inEditConditionalNGMode = false; 1230 _logixNGEditor.bringToFront(); 1231 } else if (key.equals("Delete")) { // NOI18N 1232 deletePressed(); 1233 } else if (key.equals("chgUname")) { // NOI18N 1234 LogixNG x = _logixNG_Manager.getBySystemName(lgxName); 1235 if (x == null) { 1236 log.error("Found no logixNG for name {} when changing user name (2)", lgxName); 1237 return; 1238 } 1239 x.setUserName(value); 1240 1241 if (beanTableDataModel != null) { 1242 beanTableDataModel.fireTableDataChanged(); 1243 } 1244 } 1245 }); 1246 } 1247 } 1248 1249 1250 private class LogixNG_DebuggerEventListenerImpl 1251 implements ConditionalNGDebugger.ConditionalNGEventListener { 1252 1253 private final LogixNGEditor _logixNGEditor; 1254 1255 public LogixNG_DebuggerEventListenerImpl(LogixNGEditor logixNGEditor) { 1256 this._logixNGEditor = logixNGEditor; 1257 } 1258 1259 @Override 1260 public void conditionalNGEventOccurred() { 1261 String lgxName = _curLogixNG.getSystemName(); 1262 _debugger.logixNGData.forEach((key, value) -> { 1263 if (key.equals("Finish")) { // NOI18N 1264 _debugger = null; 1265 _inEditConditionalNGMode = false; 1266 _logixNGEditor.bringToFront(); 1267 } else if (key.equals("Delete")) { // NOI18N 1268 deletePressed(); 1269 } else if (key.equals("chgUname")) { // NOI18N 1270 LogixNG x = _logixNG_Manager.getBySystemName(lgxName); 1271 if (x == null) { 1272 log.error("Found no logixNG for name {} when changing user name (2)", lgxName); 1273 return; 1274 } 1275 x.setUserName(value); 1276 1277 if (beanTableDataModel != null) { 1278 beanTableDataModel.fireTableDataChanged(); 1279 } 1280 } 1281 }); 1282 } 1283 } 1284 1285 1286 // This class is copied from BeanTableDataModel 1287 private class DeleteBeanWorker extends SwingWorker<Void, Void> { 1288 1289 private final ConditionalNG _conditionalNG; 1290 private final int _row; 1291 boolean _hasDeleted = false; 1292 1293 public DeleteBeanWorker(ConditionalNG conditionalNG, int row) { 1294 _conditionalNG = conditionalNG; 1295 _row = row; 1296 } 1297 1298 public int getDisplayDeleteMsg() { 1299 return InstanceManager.getDefault(UserPreferencesManager.class).getMultipleChoiceOption(TreeEditor.class.getName(), "deleteInUse"); 1300 } 1301 1302 public void setDisplayDeleteMsg(int boo) { 1303 InstanceManager.getDefault(UserPreferencesManager.class).setMultipleChoiceOption(TreeEditor.class.getName(), "deleteInUse", boo); 1304 } 1305 1306 public void doDelete() { 1307 try { 1308 InstanceManager.getDefault(ConditionalNG_Manager.class).deleteBean(_conditionalNG, "DoDelete"); // NOI18N 1309 _conditionalNGTableModel.fireTableRowsDeleted(_row, _row); 1310 _numConditionalNGs--; 1311 _showReminder = true; 1312 _hasDeleted = true; 1313 } catch (PropertyVetoException e) { 1314 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 1315 log.error("Unexpected doDelete failure for {}, {}", _conditionalNG, e.getMessage() ); 1316 } 1317 } 1318 1319 /** 1320 * {@inheritDoc} 1321 */ 1322 @Override 1323 public Void doInBackground() { 1324 _conditionalNG.getFemaleSocket().unregisterListeners(); 1325 1326 StringBuilder message = new StringBuilder(); 1327 try { 1328 InstanceManager.getDefault(ConditionalNG_Manager.class).deleteBean(_conditionalNG, "CanDelete"); // NOI18N 1329 } catch (PropertyVetoException e) { 1330 if (e.getPropertyChangeEvent().getPropertyName().equals("DoNotDelete")) { // NOI18N 1331 log.warn("Do not Delete {}, {}", _conditionalNG, e.getMessage()); 1332 message.append(Bundle.getMessage("VetoDeleteBean", _conditionalNG.getBeanType(), _conditionalNG.getDisplayName(NamedBean.DisplayOptions.USERNAME_SYSTEMNAME), e.getMessage())); 1333 JmriJOptionPane.showMessageDialog(null, message.toString(), 1334 Bundle.getMessage("WarningTitle"), 1335 JmriJOptionPane.ERROR_MESSAGE); 1336 return null; 1337 } 1338 message.append(e.getMessage()); 1339 } 1340 List<String> listenerRefs = new ArrayList<>(); 1341 _conditionalNG.getListenerRefsIncludingChildren(listenerRefs); 1342 int listenerRefsCount = listenerRefs.size(); 1343 log.debug("Delete with {}", listenerRefsCount); 1344 if (getDisplayDeleteMsg() == 0x02 && message.toString().isEmpty()) { 1345 doDelete(); 1346 } else { 1347 final JDialog dialog = new JDialog(); 1348 dialog.setTitle(Bundle.getMessage("WarningTitle")); 1349 dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 1350 JPanel container = new JPanel(); 1351 container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 1352 container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS)); 1353 1354 if (listenerRefsCount > 0) { // warn of listeners attached before delete 1355 String prompt = _conditionalNG.getFemaleSocket().isConnected() 1356 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 1357 JLabel question = new JLabel(Bundle.getMessage(prompt, _conditionalNG.getDisplayName(NamedBean.DisplayOptions.USERNAME_SYSTEMNAME))); 1358 question.setAlignmentX(Component.CENTER_ALIGNMENT); 1359 container.add(question); 1360 1361 ArrayList<String> listeners = new ArrayList<>(); 1362 for (String listenerRef : listenerRefs) { 1363 if (!listeners.contains(listenerRef)) { 1364 listeners.add(listenerRef); 1365 } 1366 } 1367 1368 message.append("<br>"); 1369 message.append(Bundle.getMessage("ReminderInUse", listenerRefsCount)); 1370 message.append("<ul>"); 1371 for (String listener : listeners) { 1372 message.append("<li>"); 1373 message.append(listener); 1374 message.append("</li>"); 1375 } 1376 message.append("</ul>"); 1377 1378 JEditorPane pane = new JEditorPane(); 1379 pane.setContentType("text/html"); 1380 pane.setText("<html>" + message.toString() + "</html>"); 1381 pane.setEditable(false); 1382 JScrollPane jScrollPane = new JScrollPane(pane); 1383 container.add(jScrollPane); 1384 } else { 1385 String prompt = _conditionalNG.getFemaleSocket().isConnected() 1386 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 1387 String msg = MessageFormat.format( 1388 Bundle.getMessage(prompt), _conditionalNG.getSystemName()); 1389 JLabel question = new JLabel(msg); 1390 question.setAlignmentX(Component.CENTER_ALIGNMENT); 1391 container.add(question); 1392 } 1393 1394 final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting")); 1395 remember.setFont(remember.getFont().deriveFont(10f)); 1396 remember.setAlignmentX(Component.CENTER_ALIGNMENT); 1397 1398 JButton yesButton = new JButton(Bundle.getMessage("ButtonYes")); 1399 JButton noButton = new JButton(Bundle.getMessage("ButtonNo")); 1400 JPanel button = new JPanel(); 1401 button.setAlignmentX(Component.CENTER_ALIGNMENT); 1402 button.add(yesButton); 1403 button.add(noButton); 1404 container.add(button); 1405 1406 noButton.addActionListener((ActionEvent e) -> { 1407 //there is no point in remembering this the user will never be 1408 //able to delete a bean! 1409 dialog.dispose(); 1410 }); 1411 1412 yesButton.addActionListener((ActionEvent e) -> { 1413 if (remember.isSelected()) { 1414 setDisplayDeleteMsg(0x02); 1415 } 1416 doDelete(); 1417 dialog.dispose(); 1418 }); 1419 container.add(remember); 1420 container.setAlignmentX(Component.CENTER_ALIGNMENT); 1421 container.setAlignmentY(Component.CENTER_ALIGNMENT); 1422 dialog.getContentPane().add(container); 1423 dialog.pack(); 1424 1425 dialog.getRootPane().setDefaultButton(noButton); 1426 noButton.requestFocusInWindow(); // set default keyboard focus, after pack() before setVisible(true) 1427 dialog.getRootPane().registerKeyboardAction(e -> { // escape to exit 1428 dialog.setVisible(false); 1429 dialog.dispose(); }, 1430 KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); 1431 1432 dialog.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width) / 2 - dialog.getWidth() / 2, (Toolkit.getDefaultToolkit().getScreenSize().height) / 2 - dialog.getHeight() / 2); 1433 dialog.setModal(true); 1434 dialog.setVisible(true); 1435 } 1436 if (!_hasDeleted && _conditionalNG.getFemaleSocket().isActive()) _conditionalNG.getFemaleSocket().registerListeners(); 1437 return null; 1438 } 1439 1440 /** 1441 * {@inheritDoc} Minimal implementation to catch and log errors 1442 */ 1443 @Override 1444 protected void done() { 1445 try { 1446 get(); // called to get errors 1447 } catch (InterruptedException | java.util.concurrent.ExecutionException e) { 1448 log.error("Exception while deleting bean", e); 1449 } 1450 } 1451 } 1452 1453 1454 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGEditor.class); 1455 1456}