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 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 boolean _inEditConditionalNGMode = false; 106 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 // Already editing a ConditionalNG, ask for completion of that edit 740 JmriJOptionPane.showMessageDialog(_editConditionalNGFrame, 741 Bundle.getMessage("Error_ConditionalNGInEditMode", _curConditionalNG.getSystemName()), // NOI18N 742 Bundle.getMessage("ErrorTitle"), // NOI18N 743 JmriJOptionPane.ERROR_MESSAGE); 744 _editConditionalNGFrame.setVisible(true); 745 return true; 746 } 747 return false; 748 } 749 750 boolean checkConditionalNGUserName(String uName, LogixNG logixNG) { 751 if ((uName != null) && (!(uName.equals("")))) { 752 for (int i=0; i < logixNG.getNumConditionalNGs(); i++) { 753 ConditionalNG p = logixNG.getConditionalNG(i); 754 if (uName.equals(p.getUserName())) { 755 // ConditionalNG with this user name already exists 756 log.error("Failure to update ConditionalNG with Duplicate User Name: {}", uName); // NOI18N 757 JmriJOptionPane.showMessageDialog(_editConditionalNGFrame, 758 Bundle.getMessage("Error10"), // NOI18N 759 Bundle.getMessage("ErrorTitle"), // NOI18N 760 JmriJOptionPane.ERROR_MESSAGE); 761 return false; 762 } 763 } 764 } // else return false; 765 return true; 766 } 767 768 /** 769 * Check form of ConditionalNG systemName. 770 * 771 * @param sName system name of bean to be checked 772 * @return false if sName is empty string or null 773 */ 774 boolean checkConditionalNGSystemName(String sName) { 775 if ((sName != null) && (!(sName.equals("")))) { 776 ConditionalNG p = _curLogixNG.getConditionalNG(sName); 777 if (p != null) { 778 return false; 779 } 780 } else { 781 return false; 782 } 783 return true; 784 } 785 786 // ------------ Table Models ------------ 787 788 /** 789 * Table model for ConditionalNGs in the Edit LogixNG pane. 790 */ 791 public final class ConditionalNGTableModel extends AbstractTableModel 792 implements PropertyChangeListener { 793 794 public static final int SNAME_COLUMN = 0; 795 public static final int UNAME_COLUMN = SNAME_COLUMN + 1; 796 public static final int THREAD_COLUMN = UNAME_COLUMN + 1; 797 public static final int ENABLED_COLUMN = THREAD_COLUMN + 1; 798 public static final int STARTUP_COLUMN = ENABLED_COLUMN + 1; 799 public static final int BUTTON_COLUMN = STARTUP_COLUMN + 1; 800 public static final int BUTTON_DEBUG_COLUMN = BUTTON_COLUMN + 1; 801 public static final int BUTTON_DELETE_COLUMN = BUTTON_DEBUG_COLUMN + 1; 802 public static final int BUTTON_EDIT_THREADS_COLUMN = BUTTON_DELETE_COLUMN + 1; 803 public static final int NUM_COLUMNS = BUTTON_EDIT_THREADS_COLUMN + 1; 804 805 private boolean _showStartupThreads; 806 807 808 public ConditionalNGTableModel() { 809 super(); 810 updateConditionalNGListeners(); 811 } 812 813 synchronized void updateConditionalNGListeners() { 814 // first, remove listeners from the individual objects 815 ConditionalNG c; 816 _numConditionalNGs = _curLogixNG.getNumConditionalNGs(); 817 for (int i = 0; i < _numConditionalNGs; i++) { 818 // if object has been deleted, it's not here; ignore it 819 c = _curLogixNG.getConditionalNG(i); 820 if (c != null) { 821 c.removePropertyChangeListener(this); 822 } 823 } 824 // and add them back in 825 for (int i = 0; i < _numConditionalNGs; i++) { 826 c = _curLogixNG.getConditionalNG(i); 827 if (c != null) { 828 c.addPropertyChangeListener(this); 829 } 830 } 831 } 832 833 public void setShowStartupThreads(boolean showStartupThreads) { 834 _showStartupThreads = showStartupThreads; 835 fireTableRowsUpdated(0, _curLogixNG.getNumConditionalNGs()-1); 836 } 837 838 @Override 839 public void propertyChange(java.beans.PropertyChangeEvent e) { 840 if (e.getPropertyName().equals("length")) { // NOI18N 841 // a new NamedBean is available in the manager 842 updateConditionalNGListeners(); 843 fireTableDataChanged(); 844 } else if (matchPropertyName(e)) { 845 // a value changed. 846 fireTableDataChanged(); 847 } 848 } 849 850 /** 851 * Check if this property event is announcing a change this table should 852 * display. 853 * <p> 854 * Note that events will come both from the NamedBeans and from the 855 * manager. 856 * 857 * @param e the event heard 858 * @return true if a change in State or Appearance was heard 859 */ 860 boolean matchPropertyName(java.beans.PropertyChangeEvent e) { 861 return (e.getPropertyName().contains("UserName") || // NOI18N 862 e.getPropertyName().contains("Thread")); // NOI18N 863 } 864 865 @Override 866 public Class<?> getColumnClass(int c) { 867 if ((c == BUTTON_COLUMN) 868 || (c == BUTTON_DEBUG_COLUMN) 869 || (c == BUTTON_DELETE_COLUMN) 870 || (c == BUTTON_EDIT_THREADS_COLUMN)) { 871 return JButton.class; 872 } 873 if (c == STARTUP_COLUMN 874 || c == ENABLED_COLUMN) { 875 return Boolean.class; 876 } 877 return String.class; 878 } 879 880 @Override 881 public int getColumnCount() { 882 return NUM_COLUMNS; 883 } 884 885 @Override 886 public int getRowCount() { 887 return (_numConditionalNGs); 888 } 889 890 @Override 891 public boolean isCellEditable(int r, int c) { 892 if (!_inReorderMode) { 893 return ((c == UNAME_COLUMN) 894 || (c == ENABLED_COLUMN) 895 || (c == STARTUP_COLUMN) 896 || (c == BUTTON_COLUMN) 897 || ((c == BUTTON_DEBUG_COLUMN) && InstanceManager.getDefault(LogixNGPreferences.class).getInstallDebugger()) 898 || (c == BUTTON_DELETE_COLUMN) 899 || (c == BUTTON_EDIT_THREADS_COLUMN)); 900 } else if (c == BUTTON_COLUMN) { 901 if (r >= _nextInOrder) { 902 return (true); 903 } 904 } 905 return (false); 906 } 907 908 @Override 909 public String getColumnName(int col) { 910 switch (col) { 911 case SNAME_COLUMN: 912 return Bundle.getMessage("ColumnSystemName"); // NOI18N 913 case UNAME_COLUMN: 914 return Bundle.getMessage("ColumnUserName"); // NOI18N 915 case ENABLED_COLUMN: 916 return Bundle.getMessage("ColumnHeadEnabled"); // NOI18N 917 case THREAD_COLUMN: 918 return Bundle.getMessage("ConditionalNG_Table_ColumnThreadName"); // NOI18N 919 case STARTUP_COLUMN: 920 return Bundle.getMessage("ConditionalNG_Table_ColumnStartup"); // NOI18N 921 case BUTTON_COLUMN: 922 return ""; // no label 923 case BUTTON_DEBUG_COLUMN: 924 return ""; // no label 925 case BUTTON_DELETE_COLUMN: 926 return ""; // no label 927 case BUTTON_EDIT_THREADS_COLUMN: 928 return ""; // no label 929 default: 930 throw new IllegalArgumentException("Unknown column"); 931 } 932 } 933 934 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value = "DB_DUPLICATE_SWITCH_CLAUSES", 935 justification = "better to keep cases in column order rather than to combine") 936 public int getPreferredWidth(int col) { 937 switch (col) { 938 case SNAME_COLUMN: 939 return new JTextField(6).getPreferredSize().width; 940 case UNAME_COLUMN: 941 return new JTextField(17).getPreferredSize().width; 942 case ENABLED_COLUMN: 943 return new JTextField(5).getPreferredSize().width; 944 case THREAD_COLUMN: 945 return new JTextField(10).getPreferredSize().width; 946 case STARTUP_COLUMN: 947 return new JTextField(6).getPreferredSize().width; 948 case BUTTON_COLUMN: 949 return new JTextField(6).getPreferredSize().width; 950 case BUTTON_DEBUG_COLUMN: 951 return new JTextField(6).getPreferredSize().width; 952 case BUTTON_DELETE_COLUMN: 953 return new JTextField(6).getPreferredSize().width; 954 case BUTTON_EDIT_THREADS_COLUMN: 955 return new JTextField(12).getPreferredSize().width; 956 default: 957 throw new IllegalArgumentException("Unknown column"); 958 } 959 } 960 961 @Override 962 public Object getValueAt(int r, int col) { 963 ConditionalNG c; 964 int rx = r; 965 if ((rx > _numConditionalNGs) || (_curLogixNG == null)) { 966 return null; 967 } 968 switch (col) { 969 case BUTTON_COLUMN: 970 if (!_inReorderMode) { 971 return Bundle.getMessage("ButtonEdit"); // NOI18N 972 } else if (_nextInOrder == 0) { 973 return Bundle.getMessage("ButtonFirst"); // NOI18N 974 } else if (_nextInOrder <= r) { 975 return Bundle.getMessage("ButtonNext"); // NOI18N 976 } else { 977 return Integer.toString(rx + 1); 978 } 979 case BUTTON_DEBUG_COLUMN: 980 return Bundle.getMessage("ConditionalNG_Table_ButtonDebug"); // NOI18N 981 case BUTTON_DELETE_COLUMN: 982 return Bundle.getMessage("ButtonDelete"); // NOI18N 983 case BUTTON_EDIT_THREADS_COLUMN: 984 return Bundle.getMessage("ConditionalNG_Table_ButtonEditThreads"); // NOI18N 985 case SNAME_COLUMN: 986 return _curLogixNG.getConditionalNG(rx); 987 case UNAME_COLUMN: 988 //log.debug("ConditionalNGTableModel: {}", _curLogixNG.getConditionalNGByNumberOrder(rx)); // NOI18N 989 c = _curLogixNG.getConditionalNG(rx); 990 if (c != null) { 991 return c.getUserName(); 992 } 993 return ""; 994 case ENABLED_COLUMN: 995 c = _curLogixNG.getConditionalNG(rx); 996 if (c != null) { 997 return c.isEnabled(); 998 } 999 return null; 1000 case THREAD_COLUMN: 1001 if (_showStartupThreads) { 1002 return LogixNG_Thread.getThread( 1003 _curLogixNG.getConditionalNG(r).getStartupThreadId()) 1004 .getThreadName(); 1005 } else { 1006 return _curLogixNG.getConditionalNG(r).getCurrentThread().getThreadName(); 1007 } 1008 case STARTUP_COLUMN: 1009 c = _curLogixNG.getConditionalNG(rx); 1010 if (c != null) { 1011 return c.isExecuteAtStartup(); 1012 } 1013 return false; 1014 default: 1015 throw new IllegalArgumentException("Unknown column"); 1016 } 1017 } 1018 1019 private void buttonStartupClicked(int row, Object value) { 1020 _curConditionalNG = _curLogixNG.getConditionalNG(row); 1021 if (_curConditionalNG == null) { 1022 log.error("Attempted edit of non-existant conditional."); // NOI18N 1023 return; 1024 } 1025 if (!(value instanceof Boolean)) { 1026 throw new IllegalArgumentException("value is not a Boolean"); 1027 } 1028 _curConditionalNG.setExecuteAtStartup((boolean)value); 1029 } 1030 1031 private void buttonColumnClicked(int row, int col) { 1032 if (_inReorderMode) { 1033 swapConditionalNG(row); 1034 } else { 1035 // Use separate Runnable so window is created on top 1036 class WindowMaker implements Runnable { 1037 1038 int theRow; 1039 1040 WindowMaker(int r) { 1041 theRow = r; 1042 } 1043 1044 @Override 1045 public void run() { 1046 editConditionalNGPressed(theRow); 1047 } 1048 } 1049 WindowMaker t = new WindowMaker(row); 1050 javax.swing.SwingUtilities.invokeLater(t); 1051 } 1052 } 1053 1054 private void buttonDebugClicked(int row, int col) { 1055 if (_inReorderMode) { 1056 swapConditionalNG(row); 1057 } else { 1058 // Use separate Runnable so window is created on top 1059 class WindowMaker implements Runnable { 1060 1061 int theRow; 1062 1063 WindowMaker(int r) { 1064 theRow = r; 1065 } 1066 1067 @Override 1068 public void run() { 1069 debugConditionalNGPressed(theRow); 1070 } 1071 } 1072 WindowMaker t = new WindowMaker(row); 1073 javax.swing.SwingUtilities.invokeLater(t); 1074 } 1075 } 1076 1077 private void deleteConditionalNG(int row) { 1078 DeleteBeanWorker worker = new DeleteBeanWorker(_curLogixNG.getConditionalNG(row), row); 1079 worker.execute(); 1080 } 1081 1082 private void changeUserName(Object value, int row) { 1083 String uName = (String) value; 1084 ConditionalNG cn = _curLogixNG.getConditionalNGByUserName(uName); 1085 if (cn == null) { 1086 ConditionalNG cdl = _curLogixNG.getConditionalNG(row); 1087 cdl.setUserName(uName.trim()); // N11N 1088 fireTableRowsUpdated(row, row); 1089 } else { 1090 // Duplicate user name 1091 if (cn != _curLogixNG.getConditionalNG(row)) { 1092 messageDuplicateConditionalNGUserName(cn.getSystemName()); 1093 } 1094 } 1095 } 1096 1097 @Override 1098 public void setValueAt(Object value, int row, int col) { 1099 if ((row > _numConditionalNGs) || (_curLogixNG == null)) { 1100 return; 1101 } 1102 switch (col) { 1103 case STARTUP_COLUMN: 1104 buttonStartupClicked(row, value); 1105 break; 1106 case BUTTON_COLUMN: 1107 buttonColumnClicked(row, col); 1108 break; 1109 case BUTTON_DEBUG_COLUMN: 1110 buttonDebugClicked(row, col); 1111 break; 1112 case BUTTON_DELETE_COLUMN: 1113 deleteConditionalNG(row); 1114 break; 1115 case BUTTON_EDIT_THREADS_COLUMN: 1116 EditThreadsDialog dialog = new EditThreadsDialog(_curLogixNG.getConditionalNG(row)); 1117 dialog.showDialog(); 1118 break; 1119 case SNAME_COLUMN: 1120 throw new IllegalArgumentException("System name cannot be changed"); 1121 case UNAME_COLUMN: 1122 changeUserName(value, row); 1123 break; 1124 case ENABLED_COLUMN: 1125 ConditionalNG c = _curLogixNG.getConditionalNG(row); 1126 if (c != null) { 1127 boolean v = c.isEnabled(); 1128 c.setEnabled(!v); 1129 } 1130 break; 1131 default: 1132 throw new IllegalArgumentException("Unknown column"); 1133 } 1134 } 1135 } 1136 1137 /** 1138 * Send a duplicate Conditional user name message for Edit Logix pane. 1139 * 1140 * @param svName proposed name that duplicates an existing name 1141 */ 1142 void messageDuplicateConditionalNGUserName(String svName) { 1143 JmriJOptionPane.showMessageDialog(null, 1144 Bundle.getMessage("Error30", svName), 1145 Bundle.getMessage("ErrorTitle"), // NOI18N 1146 JmriJOptionPane.ERROR_MESSAGE); 1147 } 1148 1149 private String getClassName() { 1150 // The class that is returned must have a default constructor, 1151 // a constructor with no parameters. 1152 return jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName(); 1153 } 1154 1155 1156 // ------------ LogixNG Notifications ------------ 1157 // The ConditionalNG views support some direct changes to the parent logix. 1158 // This custom event is used to notify the parent LogixNG that changes are requested. 1159 // When the event occurs, the parent LogixNG can retrieve the necessary information 1160 // to carry out the actions. 1161 // 1162 // 1) Notify the calling LogixNG that the LogixNG user name has been changed. 1163 // 2) Notify the calling LogixNG that the conditional view is closing 1164 // 3) Notify the calling LogixNG that it is to be deleted 1165 /** 1166 * Create a custom listener event. 1167 */ 1168 public interface LogixNGEventListener extends EventListener { 1169 1170 void logixNGEventOccurred(); 1171 } 1172 1173 /** 1174 * Maintain a list of listeners -- normally only one. 1175 */ 1176 List<EditorEventListener> listenerList = new ArrayList<>(); 1177 1178 /** 1179 * This contains a list of commands to be processed by the listener 1180 * recipient. 1181 */ 1182 private HashMap<String, String> logixNG_Data = new HashMap<>(); 1183 1184 /** 1185 * Add a listener. 1186 * 1187 * @param listener The recipient 1188 */ 1189 @Override 1190 public void addEditorEventListener(EditorEventListener listener) { 1191 listenerList.add(listener); 1192 } 1193 1194 /** 1195 * Remove a listener -- not used. 1196 * 1197 * @param listener The recipient 1198 */ 1199 @Override 1200 public void removeEditorEventListener(EditorEventListener listener) { 1201 listenerList.remove(listener); 1202 } 1203 1204 /** 1205 * Notify the listeners to check for new data. 1206 */ 1207 private void fireEditorEvent() { 1208 for (EditorEventListener l : listenerList) { 1209 l.editorEventOccurred(logixNG_Data); 1210 } 1211 } 1212 1213 1214 private class LogixNGEventListenerImpl implements ConditionalNGEditor.ConditionalNGEventListener { 1215 1216 private final LogixNGEditor _logixNGEditor; 1217 1218 public LogixNGEventListenerImpl(LogixNGEditor logixNGEditor) { 1219 this._logixNGEditor = logixNGEditor; 1220 } 1221 1222 @Override 1223 public void conditionalNGEventOccurred() { 1224 String lgxName = _curLogixNG.getSystemName(); 1225 _treeEdit.logixNGData.forEach((key, value) -> { 1226 if (key.equals("Finish")) { // NOI18N 1227 _treeEdit = null; 1228 _inEditConditionalNGMode = false; 1229 _logixNGEditor.bringToFront(); 1230 } else if (key.equals("Delete")) { // NOI18N 1231 deletePressed(); 1232 } else if (key.equals("chgUname")) { // NOI18N 1233 LogixNG x = _logixNG_Manager.getBySystemName(lgxName); 1234 if (x == null) { 1235 log.error("Found no logixNG for name {} when changing user name (2)", lgxName); 1236 return; 1237 } 1238 x.setUserName(value); 1239 1240 if (beanTableDataModel != null) { 1241 beanTableDataModel.fireTableDataChanged(); 1242 } 1243 } 1244 }); 1245 } 1246 } 1247 1248 1249 private class LogixNG_DebuggerEventListenerImpl 1250 implements ConditionalNGDebugger.ConditionalNGEventListener { 1251 1252 private final LogixNGEditor _logixNGEditor; 1253 1254 public LogixNG_DebuggerEventListenerImpl(LogixNGEditor logixNGEditor) { 1255 this._logixNGEditor = logixNGEditor; 1256 } 1257 1258 @Override 1259 public void conditionalNGEventOccurred() { 1260 String lgxName = _curLogixNG.getSystemName(); 1261 _debugger.logixNGData.forEach((key, value) -> { 1262 if (key.equals("Finish")) { // NOI18N 1263 _debugger = null; 1264 _inEditConditionalNGMode = false; 1265 _logixNGEditor.bringToFront(); 1266 } else if (key.equals("Delete")) { // NOI18N 1267 deletePressed(); 1268 } else if (key.equals("chgUname")) { // NOI18N 1269 LogixNG x = _logixNG_Manager.getBySystemName(lgxName); 1270 if (x == null) { 1271 log.error("Found no logixNG for name {} when changing user name (2)", lgxName); 1272 return; 1273 } 1274 x.setUserName(value); 1275 1276 if (beanTableDataModel != null) { 1277 beanTableDataModel.fireTableDataChanged(); 1278 } 1279 } 1280 }); 1281 } 1282 } 1283 1284 1285 // This class is copied from BeanTableDataModel 1286 private class DeleteBeanWorker extends SwingWorker<Void, Void> { 1287 1288 private final ConditionalNG _conditionalNG; 1289 private final int _row; 1290 boolean _hasDeleted = false; 1291 1292 public DeleteBeanWorker(ConditionalNG conditionalNG, int row) { 1293 _conditionalNG = conditionalNG; 1294 _row = row; 1295 } 1296 1297 public int getDisplayDeleteMsg() { 1298 return InstanceManager.getDefault(UserPreferencesManager.class).getMultipleChoiceOption(TreeEditor.class.getName(), "deleteInUse"); 1299 } 1300 1301 public void setDisplayDeleteMsg(int boo) { 1302 InstanceManager.getDefault(UserPreferencesManager.class).setMultipleChoiceOption(TreeEditor.class.getName(), "deleteInUse", boo); 1303 } 1304 1305 public void doDelete() { 1306 try { 1307 InstanceManager.getDefault(ConditionalNG_Manager.class).deleteBean(_conditionalNG, "DoDelete"); // NOI18N 1308 _conditionalNGTableModel.fireTableRowsDeleted(_row, _row); 1309 _numConditionalNGs--; 1310 _showReminder = true; 1311 _hasDeleted = true; 1312 } catch (PropertyVetoException e) { 1313 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 1314 log.error("Unexpected doDelete failure for {}, {}", _conditionalNG, e.getMessage() ); 1315 } 1316 } 1317 1318 /** 1319 * {@inheritDoc} 1320 */ 1321 @Override 1322 public Void doInBackground() { 1323 _conditionalNG.getFemaleSocket().unregisterListeners(); 1324 1325 StringBuilder message = new StringBuilder(); 1326 try { 1327 InstanceManager.getDefault(ConditionalNG_Manager.class).deleteBean(_conditionalNG, "CanDelete"); // NOI18N 1328 } catch (PropertyVetoException e) { 1329 if (e.getPropertyChangeEvent().getPropertyName().equals("DoNotDelete")) { // NOI18N 1330 log.warn("Do not Delete {}, {}", _conditionalNG, e.getMessage()); 1331 message.append(Bundle.getMessage("VetoDeleteBean", _conditionalNG.getBeanType(), _conditionalNG.getDisplayName(NamedBean.DisplayOptions.USERNAME_SYSTEMNAME), e.getMessage())); 1332 JmriJOptionPane.showMessageDialog(null, message.toString(), 1333 Bundle.getMessage("WarningTitle"), 1334 JmriJOptionPane.ERROR_MESSAGE); 1335 return null; 1336 } 1337 message.append(e.getMessage()); 1338 } 1339 List<String> listenerRefs = new ArrayList<>(); 1340 _conditionalNG.getListenerRefsIncludingChildren(listenerRefs); 1341 int listenerRefsCount = listenerRefs.size(); 1342 log.debug("Delete with {}", listenerRefsCount); 1343 if (getDisplayDeleteMsg() == 0x02 && message.toString().isEmpty()) { 1344 doDelete(); 1345 } else { 1346 final JDialog dialog = new JDialog(); 1347 dialog.setTitle(Bundle.getMessage("WarningTitle")); 1348 dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 1349 JPanel container = new JPanel(); 1350 container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 1351 container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS)); 1352 1353 if (listenerRefsCount > 0) { // warn of listeners attached before delete 1354 String prompt = _conditionalNG.getFemaleSocket().isConnected() 1355 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 1356 JLabel question = new JLabel(Bundle.getMessage(prompt, _conditionalNG.getDisplayName(NamedBean.DisplayOptions.USERNAME_SYSTEMNAME))); 1357 question.setAlignmentX(Component.CENTER_ALIGNMENT); 1358 container.add(question); 1359 1360 ArrayList<String> listeners = new ArrayList<>(); 1361 for (String listenerRef : listenerRefs) { 1362 if (!listeners.contains(listenerRef)) { 1363 listeners.add(listenerRef); 1364 } 1365 } 1366 1367 message.append("<br>"); 1368 message.append(Bundle.getMessage("ReminderInUse", listenerRefsCount)); 1369 message.append("<ul>"); 1370 for (String listener : listeners) { 1371 message.append("<li>"); 1372 message.append(listener); 1373 message.append("</li>"); 1374 } 1375 message.append("</ul>"); 1376 1377 JEditorPane pane = new JEditorPane(); 1378 pane.setContentType("text/html"); 1379 pane.setText("<html>" + message.toString() + "</html>"); 1380 pane.setEditable(false); 1381 JScrollPane jScrollPane = new JScrollPane(pane); 1382 container.add(jScrollPane); 1383 } else { 1384 String prompt = _conditionalNG.getFemaleSocket().isConnected() 1385 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 1386 String msg = MessageFormat.format( 1387 Bundle.getMessage(prompt), _conditionalNG.getSystemName()); 1388 JLabel question = new JLabel(msg); 1389 question.setAlignmentX(Component.CENTER_ALIGNMENT); 1390 container.add(question); 1391 } 1392 1393 final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting")); 1394 remember.setFont(remember.getFont().deriveFont(10f)); 1395 remember.setAlignmentX(Component.CENTER_ALIGNMENT); 1396 1397 JButton yesButton = new JButton(Bundle.getMessage("ButtonYes")); 1398 JButton noButton = new JButton(Bundle.getMessage("ButtonNo")); 1399 JPanel button = new JPanel(); 1400 button.setAlignmentX(Component.CENTER_ALIGNMENT); 1401 button.add(yesButton); 1402 button.add(noButton); 1403 container.add(button); 1404 1405 noButton.addActionListener((ActionEvent e) -> { 1406 //there is no point in remembering this the user will never be 1407 //able to delete a bean! 1408 dialog.dispose(); 1409 }); 1410 1411 yesButton.addActionListener((ActionEvent e) -> { 1412 if (remember.isSelected()) { 1413 setDisplayDeleteMsg(0x02); 1414 } 1415 doDelete(); 1416 dialog.dispose(); 1417 }); 1418 container.add(remember); 1419 container.setAlignmentX(Component.CENTER_ALIGNMENT); 1420 container.setAlignmentY(Component.CENTER_ALIGNMENT); 1421 dialog.getContentPane().add(container); 1422 dialog.pack(); 1423 1424 dialog.getRootPane().setDefaultButton(noButton); 1425 noButton.requestFocusInWindow(); // set default keyboard focus, after pack() before setVisible(true) 1426 dialog.getRootPane().registerKeyboardAction(e -> { // escape to exit 1427 dialog.setVisible(false); 1428 dialog.dispose(); }, 1429 KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); 1430 1431 dialog.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width) / 2 - dialog.getWidth() / 2, (Toolkit.getDefaultToolkit().getScreenSize().height) / 2 - dialog.getHeight() / 2); 1432 dialog.setModal(true); 1433 dialog.setVisible(true); 1434 } 1435 if (!_hasDeleted && _conditionalNG.getFemaleSocket().isActive()) _conditionalNG.getFemaleSocket().registerListeners(); 1436 return null; 1437 } 1438 1439 /** 1440 * {@inheritDoc} Minimal implementation to catch and log errors 1441 */ 1442 @Override 1443 protected void done() { 1444 try { 1445 get(); // called to get errors 1446 } catch (InterruptedException | java.util.concurrent.ExecutionException e) { 1447 log.error("Exception while deleting bean", e); 1448 } 1449 } 1450 } 1451 1452 1453 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogixNGEditor.class); 1454 1455}