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