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