001package jmri.jmrit.logixng.tools.swing; 002 003import java.awt.*; 004import java.awt.event.*; 005import java.beans.PropertyVetoException; 006import java.text.MessageFormat; 007import java.util.List; 008import java.util.*; 009import java.util.concurrent.atomic.AtomicBoolean; 010 011import javax.annotation.Nonnull; 012import javax.swing.*; 013import javax.swing.event.TreeModelEvent; 014import javax.swing.event.TreeModelListener; 015import javax.swing.tree.*; 016 017import jmri.*; 018import jmri.jmrit.logixng.FemaleSocket; 019import jmri.jmrit.logixng.*; 020import jmri.jmrit.logixng.SymbolTable.InitialValueType; 021import jmri.jmrit.logixng.swing.SwingConfiguratorInterface; 022import jmri.jmrit.logixng.swing.SwingTools; 023import jmri.jmrit.logixng.util.LogixNG_Thread; 024import jmri.jmrit.logixng.util.parser.swing.FunctionsHelpDialog; 025import jmri.util.swing.JmriJOptionPane; 026import jmri.util.ThreadingUtil; 027 028import org.apache.commons.lang3.mutable.MutableObject; 029 030/** 031 * Base class for LogixNG editors 032 * 033 * @author Daniel Bergqvist 2020 034 */ 035public class TreeEditor extends TreeViewer { 036 037 // Enums used to configure TreeEditor 038 public enum EnableClipboard { EnableClipboard, DisableClipboard } 039 public enum EnableRootRemoveCutCopy { EnableRootRemoveCutCopy, DisableRootRemoveCutCopy } 040 public enum EnableRootPopup { EnableRootPopup, DisableRootPopup } 041 public enum EnableExecuteEvaluate { EnableExecuteEvaluate, DisableExecuteEvaluate } 042 043 044 private static final String ACTION_COMMAND_RENAME_SOCKET = "rename_socket"; 045 private static final String ACTION_COMMAND_REMOVE = "remove"; 046 private static final String ACTION_COMMAND_EDIT = "edit"; 047 private static final String ACTION_COMMAND_CUT = "cut"; 048 private static final String ACTION_COMMAND_COPY = "copy"; 049 private static final String ACTION_COMMAND_PASTE = "paste"; 050 private static final String ACTION_COMMAND_PASTE_COPY = "pasteCopy"; 051 private static final String ACTION_COMMAND_ENABLE = "enable"; 052 private static final String ACTION_COMMAND_DISABLE = "disable"; 053 private static final String ACTION_COMMAND_LOCK = "lock"; 054 private static final String ACTION_COMMAND_UNLOCK = "unlock"; 055 private static final String ACTION_COMMAND_LOCAL_VARIABLES = "local_variables"; 056 private static final String ACTION_COMMAND_CHANGE_USERNAME = "change_username"; 057 private static final String ACTION_COMMAND_EXECUTE_EVALUATE = "execute_evaluate"; 058// private static final String ACTION_COMMAND_EXPAND_TREE = "expandTree"; 059 060 // There should only be one clipboard editor open at any time so this is static. 061 // This field must only be accessed on the GUI thread. 062 private static ClipboardEditor _clipboardEditor = null; 063 064 private final LogixNGPreferences _prefs = InstanceManager.getDefault(LogixNGPreferences.class); 065 066 private JDialog _renameSocketDialog = null; 067 private JDialog _addItemDialog = null; 068 private JDialog _editActionExpressionDialog = null; 069 private JDialog _editLocalVariablesDialog = null; 070 private JDialog _changeUsernameDialog = null; 071 private final JTextField _socketNameTextField = new JTextField(20); 072 private final JTextField _systemName = new JTextField(20); 073 private final JTextField _addUserName = new JTextField(20); 074 private final JTextField _usernameField = new JTextField(50); 075 076 protected boolean _showReminder = false; 077 private boolean _lockPopupMenu = false; 078 079 private final JLabel _renameSocketLabel = new JLabel(Bundle.getMessage("SocketName") + ":"); // NOI18N 080 private final JCheckBox _autoSystemName = new JCheckBox(Bundle.getMessage("LabelAutoSysName")); // NOI18N 081 private final JLabel _sysNameLabel = new JLabel(Bundle.getMessage("SystemName") + ":"); // NOI18N 082 private final JLabel _userNameLabel = new JLabel(Bundle.getMessage("UserName") + ":"); // NOI18N 083 private final String _systemNameAuto = getClassName() + ".AutoSystemName"; // NOI18N 084 private JButton _create; 085 private JButton _edit; 086 087 private SwingConfiguratorInterface _addSwingConfiguratorInterface; 088 private SwingConfiguratorInterface _addSwingConfiguratorInterfaceMaleSocket; 089 private SwingConfiguratorInterface _editSwingConfiguratorInterface; 090 private final List<Map.Entry<SwingConfiguratorInterface, Base>> _swingConfiguratorInterfaceList = new ArrayList<>(); 091 092 private LocalVariableTableModel _localVariableTableModel; 093 094 private final boolean _enableClipboard; 095 private final boolean _disableRootRemoveCutCopy; 096 private final boolean _disableRootPopup; 097 private final boolean _enableExecuteEvaluate; 098 099 /** 100 * Construct a TreeEditor. 101 * 102 * @param femaleRootSocket the root of the tree 103 * @param enableClipboard should clipboard be enabled on the menu? 104 * @param enableRootRemoveCutCopy should the popup menu items remove, 105 * cut and copy be enabled or disabled? 106 * @param enableRootPopup should the popup menu be disabled for root? 107 * @param enableExecuteEvaluate should the popup menu show execute/evaluate? 108 */ 109 public TreeEditor( 110 @Nonnull FemaleSocket femaleRootSocket, 111 EnableClipboard enableClipboard, 112 EnableRootRemoveCutCopy enableRootRemoveCutCopy, 113 EnableRootPopup enableRootPopup, 114 EnableExecuteEvaluate enableExecuteEvaluate) { 115 116 super(femaleRootSocket); 117 _enableClipboard = enableClipboard == EnableClipboard.EnableClipboard; 118 _disableRootRemoveCutCopy = enableRootRemoveCutCopy == EnableRootRemoveCutCopy.DisableRootRemoveCutCopy; 119 _disableRootPopup = enableRootPopup == EnableRootPopup.DisableRootPopup; 120 _enableExecuteEvaluate = enableExecuteEvaluate == EnableExecuteEvaluate.EnableExecuteEvaluate; 121 } 122 123 @Override 124 final public void initComponents() { 125 super.initComponents(); 126 127 // The menu is created in parent class TreeViewer 128 JMenuBar menuBar = getJMenuBar(); 129 130 JMenu toolsMenu = new JMenu(Bundle.getMessage("MenuTools")); 131 if (_enableClipboard) { 132 JMenuItem openClipboardItem = new JMenuItem(Bundle.getMessage("MenuOpenClipboard")); 133 openClipboardItem.addActionListener((ActionEvent e) -> { 134 openClipboard(); 135 }); 136 toolsMenu.add(openClipboardItem); 137 } 138 menuBar.add(toolsMenu); 139 140 JTree tree = _treePane._tree; 141 142 tree.addKeyListener(new KeyListener(){ 143 @Override 144 public void keyTyped(KeyEvent e) { 145 } 146 147 @Override 148 public void keyPressed(KeyEvent e) { 149 if (e.getModifiersEx() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx()) { 150 if (e.getKeyCode() == 'R') { // Remove 151 TreePath path = tree.getSelectionPath(); 152 if (path != null) { 153 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 154 if (femaleSocket.isConnected()) { 155 removeItem((FemaleSocket) path.getLastPathComponent(), path); 156 } 157 } 158 } 159 if (e.getKeyCode() == 'E') { // Edit 160 TreePath path = tree.getSelectionPath(); 161 if (path != null) { 162 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 163 if (femaleSocket.isConnected()) { 164 editItem(femaleSocket, path); 165 } 166 } 167 } 168 if (e.getKeyCode() == 'N') { // New 169 TreePath path = tree.getSelectionPath(); 170 if (path != null) { 171 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 172 if (femaleSocket.isConnected()) { 173 return; 174 } 175 if (parentIsSystem(femaleSocket) && abortEditAboutSystem(femaleSocket.getParent())) { 176 return; 177 } 178 Rectangle rect = tree.getPathBounds(path); 179 openPopupMenu(tree, path, rect.x, rect.y, true); 180 } 181 } 182 if (e.getKeyCode() == 'D') { // Disable 183 TreePath path = tree.getSelectionPath(); 184 if (path != null) { 185 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 186 if (femaleSocket.isConnected()) { 187 doIt(ACTION_COMMAND_DISABLE, femaleSocket, path); 188 } 189 } 190 } 191 } 192 if (e.getModifiersEx() == Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() + InputEvent.SHIFT_DOWN_MASK) { 193 if (e.getKeyCode() == 'V') { // Paste copy 194 TreePath path = tree.getSelectionPath(); 195 if (path != null) { 196 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 197 if (!femaleSocket.isConnected()) { 198 pasteCopy((FemaleSocket) path.getLastPathComponent(), path); 199 } 200 } 201 } 202 if (e.getKeyCode() == 'D') { // Enable 203 TreePath path = tree.getSelectionPath(); 204 if (path != null) { 205 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 206 if (femaleSocket.isConnected()) { 207 doIt(ACTION_COMMAND_ENABLE, femaleSocket, path); 208 } 209 } 210 } 211 } 212 213 for (FemaleSocketOperation oper : FemaleSocketOperation.values()) { 214 if (e.getKeyCode() == oper.getKeyCode() 215 && e.getModifiersEx() == oper.getModifiers()) { 216 217 TreePath path = tree.getSelectionPath(); 218 if (path != null) { 219 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 220 if (femaleSocket.isSocketOperationAllowed(oper) && !parentIsLocked(femaleSocket)) { 221 doIt(oper.name(), femaleSocket, path); 222 } 223 } 224 } 225 } 226 } 227 228 @Override 229 public void keyReleased(KeyEvent e) { 230 } 231 }); 232 233 var mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); 234 tree.getActionMap().put(tree.getInputMap().get(KeyStroke.getKeyStroke(KeyEvent.VK_X, mask)), new AbstractAction() { 235 @Override 236 public void actionPerformed(ActionEvent e) { 237 TreePath path = tree.getSelectionPath(); 238 if (path != null) { 239 cutItem((FemaleSocket) path.getLastPathComponent(), path); 240 } 241 } 242 }); 243 244 tree.getActionMap().put(tree.getInputMap().get(KeyStroke.getKeyStroke(KeyEvent.VK_C, mask)), new AbstractAction() { 245 @Override 246 public void actionPerformed(ActionEvent e) { 247 TreePath path = tree.getSelectionPath(); 248 if (path != null) { 249 copyItem((FemaleSocket) path.getLastPathComponent()); 250 } 251 } 252 }); 253 254 tree.getActionMap().put(tree.getInputMap().get(KeyStroke.getKeyStroke(KeyEvent.VK_V, mask)), new AbstractAction() { 255 @Override 256 public void actionPerformed(ActionEvent e) { 257 TreePath path = tree.getSelectionPath(); 258 if (path != null) { 259 pasteItem((FemaleSocket) path.getLastPathComponent(), path); 260 } 261 } 262 }); 263 264 265 tree.addMouseListener( 266 new MouseAdapter() { 267 // On Windows, the popup is opened on mousePressed, 268 // on some other OS, the popup is opened on mouseReleased 269 270 @Override 271 public void mousePressed(MouseEvent e) { 272 if (e.isPopupTrigger()) { 273 openPopupMenu(tree, tree.getClosestPathForLocation(e.getX(), e.getY()), e.getX(), e.getY(), false); 274 } 275 } 276 277 @Override 278 public void mouseReleased(MouseEvent e) { 279 if (e.isPopupTrigger()) { 280 openPopupMenu(tree, tree.getClosestPathForLocation(e.getX(), e.getY()), e.getX(), e.getY(), false); 281 } 282 } 283 } 284 ); 285 } 286 287 private void openPopupMenu(JTree tree, TreePath path, int x, int y, boolean onlyAddItems) { 288 if (isPopupMenuLocked()) return; 289 290 if (path != null) { 291 // Check that the user has clicked on a row. 292 Rectangle rect = tree.getPathBounds(path); 293 if ((y >= rect.y) && (y <= rect.y + rect.height)) { 294 // Select the row the user clicked on 295 tree.setSelectionPath(path); 296 297 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 298 new PopupMenu(x, y, femaleSocket, path, onlyAddItems); 299 } 300 } 301 } 302 303 public static void openClipboard() { 304 if (_clipboardEditor == null) { 305 _clipboardEditor = new ClipboardEditor(); 306 _clipboardEditor.initComponents(); 307 _clipboardEditor.setVisible(true); 308 309 _clipboardEditor.addClipboardEventListener(() -> { 310 _clipboardEditor.clipboardData.forEach((key, value) -> { 311 if (key.equals("Finish")) { // NOI18N 312 _clipboardEditor = null; 313 } 314 }); 315 }); 316 } else { 317 _clipboardEditor.setVisible(true); 318 } 319 } 320 321 private static String getClassName() { 322 return jmri.jmrit.logixng.LogixNG_UserPreferences.class.getName(); 323 } 324 325 /** 326 * Run the thread action on either the ConditionalNG thread or the 327 * GUI thread. 328 * If the conditionalNG is not null, run it on the conditionalNG thread. 329 * If the conditionalNG is null, run it on the GUI thread. 330 * The conditionalNG is null when editing the clipboard or a module. 331 * @param conditionalNG the conditionalNG or null if no conditionalNG 332 * @param ta the thread action 333 */ 334 private void runOnConditionalNGThreadOrGUIThreadEventually( 335 ConditionalNG conditionalNG, ThreadingUtil.ThreadAction ta) { 336 337 if (conditionalNG != null) { 338 LogixNG_Thread thread = conditionalNG.getCurrentThread(); 339 thread.runOnLogixNGEventually(ta); 340 } else { 341 // Run the thread action on the GUI thread. And we already are on the GUI thread. 342 ta.run(); 343 } 344 } 345 346 /** 347 * When a pop-up action is selected that opens a dialog, the popup menu is locked until the 348 * dialog is closed. 349 * @return true if the popup menu is locked. 350 */ 351 final protected boolean isPopupMenuLocked() { 352 if (_lockPopupMenu) { 353 JmriJOptionPane.showMessageDialog(this, 354 Bundle.getMessage("TreeEditor_PopupLockMessage"), 355 Bundle.getMessage("TreeEditor_PopupLockTitle"), 356 JmriJOptionPane.INFORMATION_MESSAGE); 357 } 358 return _lockPopupMenu; 359 } 360 361 final protected void setPopupMenuLock(boolean lock) { 362 _lockPopupMenu = lock; 363 } 364 365 366 /** 367 * Respond to the Add menu choice in the popup menu. 368 * 369 * @param femaleSocket the female socket 370 * @param path the path to the item the user has clicked on 371 */ 372 final protected void renameSocketPressed(FemaleSocket femaleSocket, TreePath path) { 373 setPopupMenuLock(true); 374 _renameSocketDialog = new JDialog( 375 this, 376 Bundle.getMessage( 377 "RenameSocketDialogTitle", 378 femaleSocket.getLongDescription()), 379 false); 380// _renameSocketDialog.addHelpMenu( 381// "package.jmri.jmrit.logixng.tools.swing.ConditionalNGAddEdit", true); // NOI18N 382 _renameSocketDialog.setLocation(50, 30); 383 Container contentPanel = _renameSocketDialog.getContentPane(); 384 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 385 386 JPanel p; 387 p = new JPanel(); 388// p.setLayout(new FlowLayout()); 389 p.setLayout(new java.awt.GridBagLayout()); 390 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 391 c.gridwidth = 1; 392 c.gridheight = 1; 393 c.gridx = 0; 394 c.gridy = 0; 395 c.anchor = java.awt.GridBagConstraints.EAST; 396 p.add(_renameSocketLabel, c); 397 c.gridx = 1; 398 c.gridy = 0; 399 c.anchor = java.awt.GridBagConstraints.WEST; 400 c.weightx = 1.0; 401 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 402 p.add(_socketNameTextField, c); 403 _socketNameTextField.setText(femaleSocket.getName()); 404 405 contentPanel.add(p); 406 407 // set up create and cancel buttons 408 JPanel panel5 = new JPanel(); 409 panel5.setLayout(new FlowLayout()); 410 // Cancel 411 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 412 panel5.add(cancel); 413 cancel.addActionListener((ActionEvent e) -> { 414 cancelRenameSocketPressed(null); 415 }); 416// cancel.setToolTipText(Bundle.getMessage("CancelLogixButtonHint")); // NOI18N 417 cancel.setToolTipText("CancelLogixButtonHint"); // NOI18N 418 419 _renameSocketDialog.addWindowListener(new java.awt.event.WindowAdapter() { 420 @Override 421 public void windowClosing(java.awt.event.WindowEvent e) { 422 cancelRenameSocketPressed(null); 423 } 424 }); 425 426 _create = new JButton(Bundle.getMessage("ButtonOK")); // NOI18N 427 panel5.add(_create); 428 _create.addActionListener((ActionEvent e) -> { 429 if (femaleSocket.validateName(_socketNameTextField.getText())) { 430 femaleSocket.setName(_socketNameTextField.getText()); 431 cancelRenameSocketPressed(null); 432 for (TreeModelListener l : _treePane.femaleSocketTreeModel.listeners) { 433 TreeModelEvent tme = new TreeModelEvent( 434 femaleSocket, 435 path.getPath() 436 ); 437 l.treeNodesChanged(tme); 438 } 439 _treePane._tree.updateUI(); 440 setPopupMenuLock(false); 441 } else { 442 JmriJOptionPane.showMessageDialog(null, 443 Bundle.getMessage("ValidateFemaleSocketMessage", _socketNameTextField.getText()), 444 Bundle.getMessage("ValidateFemaleSocketTitle"), 445 JmriJOptionPane.ERROR_MESSAGE); 446 } 447 }); 448 449 contentPanel.add(panel5); 450 451// _renameSocketDialog.setLocationRelativeTo(component); 452 _renameSocketDialog.setLocationRelativeTo(null); 453 _renameSocketDialog.pack(); 454 _renameSocketDialog.setVisible(true); 455 } 456 457 /** 458 * Respond to the Add menu choice in the popup menu. 459 * 460 * @param femaleSocket the female socket 461 * @param swingConfiguratorInterface the swing configurator used to configure the new class 462 * @param path the path to the item the user has clicked on 463 */ 464 final protected void createAddFrame(FemaleSocket femaleSocket, TreePath path, 465 SwingConfiguratorInterface swingConfiguratorInterface) { 466 // possible change 467 _showReminder = true; 468 // make an Add Item Frame 469 if (_addItemDialog == null) { 470 MutableObject<String> commentStr = new MutableObject<>(); 471 _addSwingConfiguratorInterface = swingConfiguratorInterface; 472 // Create item 473 _create = new JButton(Bundle.getMessage("ButtonCreate")); // NOI18N 474 _create.addActionListener((ActionEvent e) -> { 475 _treePane._femaleRootSocket.unregisterListeners(); 476 477 runOnConditionalNGThreadOrGUIThreadEventually( 478 _treePane._femaleRootSocket.getConditionalNG(), 479 () -> { 480 481 List<String> errorMessages = new ArrayList<>(); 482 483 boolean isValid = true; 484 485 if (!_prefs.getShowSystemUserNames() 486 || (_systemName.getText().isEmpty() && _autoSystemName.isSelected())) { 487 _systemName.setText(_addSwingConfiguratorInterface.getAutoSystemName()); 488 } 489 490 if (_addSwingConfiguratorInterface.getManager() 491 .validSystemNameFormat(_systemName.getText()) != Manager.NameValidity.VALID) { 492 isValid = false; 493 errorMessages.add(Bundle.getMessage("InvalidSystemName", _systemName.getText())); 494 } 495 496 isValid &= _addSwingConfiguratorInterface.validate(errorMessages); 497 498 if (isValid) { 499 MaleSocket socket; 500 if (_addUserName.getText().isEmpty()) { 501 socket = _addSwingConfiguratorInterface.createNewObject(_systemName.getText(), null); 502 } else { 503 socket = _addSwingConfiguratorInterface.createNewObject(_systemName.getText(), _addUserName.getText()); 504 } 505 _addSwingConfiguratorInterfaceMaleSocket.updateObject(socket); 506 // for (Map.Entry<SwingConfiguratorInterface, Base> entry : _swingConfiguratorInterfaceList) { 507 // entry.getKey().updateObject(entry.getValue()); 508 // } 509 socket.setComment(commentStr.getValue()); 510 try { 511 femaleSocket.connect(socket); 512 } catch (SocketAlreadyConnectedException ex) { 513 throw new RuntimeException(ex); 514 } 515 516 femaleSocket.forEntireTree((Base b) -> { 517 b.addPropertyChangeListener(_treePane); 518 }); 519 520 ThreadingUtil.runOnGUIEventually(() -> { 521 _addSwingConfiguratorInterface.dispose(); 522 _addItemDialog.dispose(); 523 _addItemDialog = null; 524 525 for (TreeModelListener l : _treePane.femaleSocketTreeModel.listeners) { 526 TreeModelEvent tme = new TreeModelEvent( 527 femaleSocket, 528 path.getPath() 529 ); 530 l.treeNodesChanged(tme); 531 } 532 _treePane._tree.expandPath(path); 533 _treePane._tree.updateUI(); 534 535 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> { 536 prefMgr.setCheckboxPreferenceState(_systemNameAuto, _autoSystemName.isSelected()); 537 }); 538 }); 539 setPopupMenuLock(false); 540 } else { 541 StringBuilder errorMsg = new StringBuilder(); 542 for (String s : errorMessages) { 543 if (errorMsg.length() > 0) errorMsg.append("<br>"); 544 errorMsg.append(s); 545 } 546 JmriJOptionPane.showMessageDialog(null, 547 Bundle.getMessage("ValidateErrorMessage", errorMsg), 548 Bundle.getMessage("ValidateErrorTitle"), 549 JmriJOptionPane.ERROR_MESSAGE); 550 } 551 ThreadingUtil.runOnGUIEventually(() -> { 552 if (_treePane._femaleRootSocket.isActive()) { 553 _treePane._femaleRootSocket.registerListeners(); 554 } 555 }); 556 }); 557 }); 558 _create.setToolTipText(Bundle.getMessage("CreateButtonHint")); // NOI18N 559 560 if (_addSwingConfiguratorInterface != null) { 561 makeAddEditFrame(true, femaleSocket, _create, commentStr); 562 } 563 } 564 } 565 566 /** 567 * Respond to the Edit menu choice in the popup menu. 568 * 569 * @param femaleSocket the female socket 570 * @param path the path to the item the user has clicked on 571 */ 572 final protected void editPressed(FemaleSocket femaleSocket, TreePath path) { 573 setPopupMenuLock(true); 574 575 // possible change 576 _showReminder = true; 577 // make an Edit Frame 578 if (_editActionExpressionDialog == null) { 579 Base object = femaleSocket.getConnectedSocket().getObject(); 580 MutableObject<String> commentStr = new MutableObject<>(object.getComment()); 581 582 // Edit ConditionalNG 583 _edit = new JButton(Bundle.getMessage("ButtonOK")); // NOI18N 584 _edit.addActionListener((ActionEvent e) -> { 585 586 runOnConditionalNGThreadOrGUIThreadEventually( 587 _treePane._femaleRootSocket.getConditionalNG(), 588 () -> { 589 590 List<String> errorMessages = new ArrayList<>(); 591 592 boolean isValid = true; 593 594 if (_editSwingConfiguratorInterface.getManager() != null) { 595 if (_editSwingConfiguratorInterface.getManager() 596 .validSystemNameFormat(_systemName.getText()) != Manager.NameValidity.VALID) { 597 isValid = false; 598 errorMessages.add(Bundle.getMessage("InvalidSystemName", _systemName.getText())); 599 } 600 } else { 601 log.debug("_editSwingConfiguratorInterface.getManager() returns null"); 602 } 603 604 isValid &= _editSwingConfiguratorInterface.validate(errorMessages); 605 606 boolean canClose = true; 607 for (Map.Entry<SwingConfiguratorInterface, Base> entry : _swingConfiguratorInterfaceList) { 608 if (!entry.getKey().canClose()) { 609 canClose = false; 610 break; 611 } 612 } 613 614 if (isValid && canClose) { 615 ThreadingUtil.runOnGUIEventually(() -> { 616 femaleSocket.unregisterListeners(); 617 618// Base object = femaleSocket.getConnectedSocket().getObject(); 619 if (_addUserName.getText().isEmpty()) { 620 ((NamedBean)object).setUserName(null); 621 } else { 622 ((NamedBean)object).setUserName(_addUserName.getText()); 623 } 624 ((NamedBean)object).setComment(commentStr.getValue()); 625 for (Map.Entry<SwingConfiguratorInterface, Base> entry : _swingConfiguratorInterfaceList) { 626 entry.getKey().updateObject(entry.getValue()); 627 entry.getKey().dispose(); 628 } 629 for (TreeModelListener l : _treePane.femaleSocketTreeModel.listeners) { 630 TreeModelEvent tme = new TreeModelEvent( 631 femaleSocket, 632 path.getPath() 633 ); 634 l.treeNodesChanged(tme); 635 } 636 _editActionExpressionDialog.dispose(); 637 _editActionExpressionDialog = null; 638 _treePane._tree.updateUI(); 639 640// if (femaleSocket.isActive()) femaleSocket.registerListeners(); 641 if (_treePane._femaleRootSocket.isActive()) { 642 _treePane._femaleRootSocket.registerListeners(); 643 } 644 }); 645 setPopupMenuLock(false); 646 } else if (!isValid) { 647 StringBuilder errorMsg = new StringBuilder(); 648 for (String s : errorMessages) { 649 if (errorMsg.length() > 0) errorMsg.append("<br>"); 650 errorMsg.append(s); 651 } 652 ThreadingUtil.runOnGUIEventually(() -> { 653 JmriJOptionPane.showMessageDialog(null, 654 Bundle.getMessage("ValidateErrorMessage", errorMsg), 655 Bundle.getMessage("ValidateErrorTitle"), 656 JmriJOptionPane.ERROR_MESSAGE); 657 }); 658 } 659 }); 660 }); 661 _edit.setToolTipText(Bundle.getMessage("EditButtonHint")); // NOI18N 662 663 makeAddEditFrame(false, femaleSocket, _edit, commentStr); 664 } 665 } 666 667 /** 668 * Create or edit action/expression dialog. 669 * 670 * @param addOrEdit true if add, false if edit 671 * @param femaleSocket the female socket to which we want to add something 672 * @param button a button to add to the dialog 673 * @param commentStr the new comment 674 */ 675 final protected void makeAddEditFrame( 676 boolean addOrEdit, 677 FemaleSocket femaleSocket, 678 JButton button, 679 MutableObject<String> commentStr) { 680 681 JDialog dialog = new JDialog( 682 this, 683 Bundle.getMessage( 684 addOrEdit ? "AddMaleSocketDialogTitle" : "EditMaleSocketDialogTitle", 685 femaleSocket.getLongDescription()), 686 false); 687// frame.addHelpMenu( 688// "package.jmri.jmrit.logixng.tools.swing.ConditionalNGAddEdit", true); // NOI18N 689 Container contentPanel = dialog.getContentPane(); 690 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 691 692 JPanel p; 693 p = new JPanel(); 694// p.setLayout(new FlowLayout()); 695 p.setLayout(new java.awt.GridBagLayout()); 696 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 697 c.gridwidth = 1; 698 c.gridheight = 1; 699 if (_prefs.getShowSystemUserNames()) { 700 c.gridx = 0; 701 c.gridy = 0; 702 c.anchor = java.awt.GridBagConstraints.EAST; 703 p.add(_sysNameLabel, c); 704 c.gridy = 1; 705 p.add(_userNameLabel, c); 706 c.gridy = 2; 707 c.gridx = 1; 708 c.gridy = 0; 709 c.anchor = java.awt.GridBagConstraints.WEST; 710 c.weightx = 1.0; 711 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 712 p.add(_systemName, c); 713 c.gridy = 1; 714 p.add(_addUserName, c); 715 if (!femaleSocket.isConnected()) { 716 c.gridx = 2; 717 c.gridy = 1; 718 c.anchor = java.awt.GridBagConstraints.WEST; 719 c.weightx = 1.0; 720 c.fill = java.awt.GridBagConstraints.HORIZONTAL; // text field will expand 721 c.gridy = 0; 722 p.add(_autoSystemName, c); 723 } 724 725 if (addOrEdit) { 726 _systemName.setToolTipText(Bundle.getMessage("SystemNameHint", 727 _addSwingConfiguratorInterface.getExampleSystemName())); 728 _addUserName.setToolTipText(Bundle.getMessage("UserNameHint")); 729 } 730 } else { 731 c.gridx = 0; 732 c.gridy = 0; 733 } 734 contentPanel.add(p); 735 736 if (femaleSocket.isConnected()) { 737 _systemName.setText(femaleSocket.getConnectedSocket().getSystemName()); 738 _systemName.setEnabled(false); 739 _addUserName.setText(femaleSocket.getConnectedSocket().getUserName()); 740 } else { 741 _systemName.setText(""); 742 _systemName.setEnabled(true); 743 _addUserName.setText(""); 744 } 745 746 // set up message 747 JPanel panel3 = new JPanel(); 748 panel3.setLayout(new BoxLayout(panel3, BoxLayout.Y_AXIS)); 749 750 // set up create and cancel buttons 751 JPanel panel5 = new JPanel(); 752 panel5.setLayout(new FlowLayout()); 753 754 Base object = null; 755 756 // Get panel for the item 757 _swingConfiguratorInterfaceList.clear(); 758 List<JPanel> panels = new ArrayList<>(); 759 if (femaleSocket.isConnected()) { 760 object = femaleSocket.getConnectedSocket(); 761 while (object instanceof MaleSocket) { 762 SwingConfiguratorInterface swi = 763 SwingTools.getSwingConfiguratorForClass(object.getClass()); 764 panels.add(swi.getConfigPanel(object, panel5)); 765 _swingConfiguratorInterfaceList.add(new HashMap.SimpleEntry<>(swi, object)); 766 object = ((MaleSocket)object).getObject(); 767 } 768 if (object != null) { 769 _editSwingConfiguratorInterface = 770 SwingTools.getSwingConfiguratorForClass(object.getClass()); 771 _editSwingConfiguratorInterface.setJDialog(dialog); 772 panels.add(_editSwingConfiguratorInterface.getConfigPanel(object, panel5)); 773 _swingConfiguratorInterfaceList.add(new HashMap.SimpleEntry<>(_editSwingConfiguratorInterface, object)); 774 775 dialog.setTitle(Bundle.getMessage( 776 addOrEdit ? "AddMaleSocketDialogTitleWithType" : "EditMaleSocketDialogTitleWithType", 777 femaleSocket.getLongDescription(), 778 _editSwingConfiguratorInterface.toString()) 779 ); 780 } else { 781 // 'object' should be an action or expression but is null 782 JPanel panel = new JPanel(); 783 panel.add(new JLabel("Error: femaleSocket.getConnectedSocket().getObject().getObject()....getObject() doesn't return a non MaleSocket")); 784 panels.add(panel); 785 log.error("femaleSocket.getConnectedSocket().getObject().getObject()....getObject() doesn't return a non MaleSocket"); 786 } 787 } else { 788 Class<? extends MaleSocket> maleSocketClass = 789 _addSwingConfiguratorInterface.getManager().getMaleSocketClass(); 790 _addSwingConfiguratorInterfaceMaleSocket = 791 SwingTools.getSwingConfiguratorForClass(maleSocketClass); 792 793 _addSwingConfiguratorInterfaceMaleSocket.setJDialog(dialog); 794 panels.add(_addSwingConfiguratorInterfaceMaleSocket.getConfigPanel(panel5)); 795 796 _addSwingConfiguratorInterface.setJDialog(dialog); 797 panels.add(_addSwingConfiguratorInterface.getConfigPanel(panel5)); 798 799 dialog.setTitle(Bundle.getMessage( 800 addOrEdit ? "AddMaleSocketDialogTitleWithType" : "EditMaleSocketDialogTitleWithType", 801 femaleSocket.getLongDescription(), 802 _addSwingConfiguratorInterface.toString()) 803 ); 804 } 805 JPanel panel34 = new JPanel(); 806 panel34.setLayout(new BoxLayout(panel34, BoxLayout.Y_AXIS)); 807 for (int i = panels.size()-1; i >= 0; i--) { 808 JPanel panel = panels.get(i); 809 if (panel.getComponentCount() > 0) { 810 panel34.add(Box.createVerticalStrut(30)); 811 panel34.add(panel); 812 } 813 } 814 panel3.add(panel34); 815 contentPanel.add(panel3); 816 817 // Edit comment 818 JButton editComment = new JButton(Bundle.getMessage("ButtonEditComment")); // NOI18N 819 panel5.add(editComment); 820 String comment = object != null ? object.getComment() : ""; 821 editComment.addActionListener((ActionEvent e) -> { 822 commentStr.setValue(new EditCommentDialog().showDialog(comment)); 823 }); 824 825 // Function help 826 JButton showFunctionHelp = new JButton(Bundle.getMessage("ButtonFunctionHelp")); // NOI18N 827 panel5.add(showFunctionHelp); 828 showFunctionHelp.addActionListener((ActionEvent e) -> { 829 InstanceManager.getDefault(FunctionsHelpDialog.class).showDialog(); 830 }); 831// showFunctionHelp.setToolTipText("FunctionHelpButtonHint"); // NOI18N 832 833 // Cancel 834 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 835 panel5.add(cancel); 836 cancel.addActionListener((ActionEvent e) -> { 837 if (!femaleSocket.isConnected()) { 838 cancelCreateItem(null); 839 } else { 840 cancelEditPressed(null); 841 } 842 }); 843// cancel.setToolTipText(Bundle.getMessage("CancelLogixButtonHint")); // NOI18N 844 cancel.setToolTipText("CancelLogixButtonHint"); // NOI18N 845 846 panel5.add(button); 847 848 dialog.addWindowListener(new java.awt.event.WindowAdapter() { 849 @Override 850 public void windowClosing(java.awt.event.WindowEvent e) { 851 if (addOrEdit) { 852 cancelCreateItem(null); 853 } else { 854 cancelEditPressed(null); 855 } 856 } 857 }); 858 859 contentPanel.add(panel5); 860 861 _autoSystemName.addItemListener((ItemEvent e) -> { 862 autoSystemName(); 863 }); 864// addLogixNGFrame.setLocationRelativeTo(component); 865 dialog.pack(); 866 dialog.setLocationRelativeTo(null); 867 868 dialog.getRootPane().setDefaultButton(button); 869 870 if (addOrEdit) { 871 _addItemDialog = dialog; 872 } else { 873 _editActionExpressionDialog = dialog; 874 } 875 876 _autoSystemName.setSelected(true); 877 InstanceManager.getOptionalDefault(UserPreferencesManager.class).ifPresent((prefMgr) -> { 878 _autoSystemName.setSelected(prefMgr.getCheckboxPreferenceState(_systemNameAuto, true)); 879 }); 880 881 dialog.setVisible(true); 882 } 883 884 /** 885 * Respond to the Local Variables menu choice in the popup menu. 886 * 887 * @param femaleSocket the female socket 888 * @param path the path to the item the user has clicked on 889 */ 890 final protected void editLocalVariables(FemaleSocket femaleSocket, TreePath path) { 891 // possible change 892 _showReminder = true; 893 setPopupMenuLock(true); 894 // make an Edit Frame 895 if (_editLocalVariablesDialog == null) { 896 MaleSocket maleSocket = femaleSocket.getConnectedSocket(); 897 898 // Edit ConditionalNG 899 _edit = new JButton(Bundle.getMessage("ButtonOK")); // NOI18N 900 _edit.addActionListener((ActionEvent e) -> { 901 List<String> errorMessages = new ArrayList<>(); 902 boolean hasErrors = false; 903 for (SymbolTable.VariableData v : _localVariableTableModel.getVariables()) { 904 if (v.getName().isEmpty()) { 905 errorMessages.add(Bundle.getMessage("VariableNameIsEmpty", v.getName())); 906 hasErrors = true; 907 } 908 if (! SymbolTable.validateName(v.getName())) { 909 errorMessages.add(Bundle.getMessage("VariableNameIsNotValid", v.getName())); 910 hasErrors = true; 911 } 912 } 913 914 if (hasErrors) { 915 StringBuilder errorMsg = new StringBuilder(); 916 for (String s : errorMessages) { 917 if (errorMsg.length() > 0) errorMsg.append("<br>"); 918 errorMsg.append(s); 919 } 920 JmriJOptionPane.showMessageDialog(null, 921 Bundle.getMessage("ValidateErrorMessage", errorMsg), 922 Bundle.getMessage("ValidateErrorTitle"), 923 JmriJOptionPane.ERROR_MESSAGE); 924 925 } else { 926 _treePane._femaleRootSocket.unregisterListeners(); 927 928 runOnConditionalNGThreadOrGUIThreadEventually( 929 _treePane._femaleRootSocket.getConditionalNG(), 930 () -> { 931 932 maleSocket.clearLocalVariables(); 933 for (SymbolTable.VariableData variableData : _localVariableTableModel.getVariables()) { 934 maleSocket.addLocalVariable(variableData); 935 } 936 937 ThreadingUtil.runOnGUIEventually(() -> { 938 _editLocalVariablesDialog.dispose(); 939 _editLocalVariablesDialog = null; 940 if (_treePane._femaleRootSocket.isActive()) { 941 _treePane._femaleRootSocket.registerListeners(); 942 } 943 for (TreeModelListener l : _treePane.femaleSocketTreeModel.listeners) { 944 TreeModelEvent tme = new TreeModelEvent( 945 femaleSocket, 946 path.getPath() 947 ); 948 l.treeNodesChanged(tme); 949 } 950 _treePane._tree.updateUI(); 951 }); 952 setPopupMenuLock(false); 953 }); 954 } 955 }); 956// _edit.setToolTipText(Bundle.getMessage("EditButtonHint")); // NOI18N 957 958// makeAddEditFrame(false, femaleSocket, _editSwingConfiguratorInterface, _edit); // NOI18N 959 960 _editLocalVariablesDialog = new JDialog( 961 this, 962 Bundle.getMessage( 963 "EditLocalVariablesDialogTitle", 964 femaleSocket.getLongDescription()), 965 false); 966 // frame.addHelpMenu( 967 // "package.jmri.jmrit.logixng.tools.swing.ConditionalNGAddEdit", true); // NOI18N 968 Container contentPanel = _editLocalVariablesDialog.getContentPane(); 969 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 970 971 JTable table = new JTable(); 972 _localVariableTableModel = new LocalVariableTableModel(maleSocket); 973 table.setModel(_localVariableTableModel); 974 table.setDefaultRenderer(InitialValueType.class, 975 new LocalVariableTableModel.TypeCellRenderer()); 976 table.setDefaultEditor(InitialValueType.class, 977 new LocalVariableTableModel.TypeCellEditor()); 978 table.setDefaultRenderer(LocalVariableTableModel.Menu.class, 979 new LocalVariableTableModel.MenuCellRenderer()); 980 table.setDefaultEditor(LocalVariableTableModel.Menu.class, 981 new LocalVariableTableModel.MenuCellEditor(table, _localVariableTableModel)); 982 _localVariableTableModel.setColumnForMenu(table); 983 JScrollPane scrollpane = new JScrollPane(table); 984 scrollpane.setPreferredSize(new Dimension(400, 200)); 985 contentPanel.add(scrollpane); 986 987 // set up create and cancel buttons 988 JPanel buttonPanel = new JPanel(); 989 buttonPanel.setLayout(new FlowLayout()); 990 991 // Function help 992 JButton showFunctionHelp = new JButton(Bundle.getMessage("ButtonFunctionHelp")); // NOI18N 993 buttonPanel.add(showFunctionHelp); 994 showFunctionHelp.addActionListener((ActionEvent e) -> { 995 InstanceManager.getDefault(FunctionsHelpDialog.class).showDialog(); 996 }); 997// showFunctionHelp.setToolTipText("FunctionHelpButtonHint"); // NOI18N 998 999 // Add local variable 1000 JButton add = new JButton(Bundle.getMessage("TableAddVariable")); 1001 buttonPanel.add(add); 1002 add.addActionListener((ActionEvent e) -> { 1003 _localVariableTableModel.add(); 1004 }); 1005 1006 // Cancel 1007 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 1008 buttonPanel.add(cancel); 1009 cancel.addActionListener((ActionEvent e) -> { 1010 _editLocalVariablesDialog.setVisible(false); 1011 _editLocalVariablesDialog.dispose(); 1012 _editLocalVariablesDialog = null; 1013 setPopupMenuLock(false); 1014 }); 1015 // cancel.setToolTipText(Bundle.getMessage("CancelLogixButtonHint")); // NOI18N 1016 cancel.setToolTipText("CancelLogixButtonHint"); // NOI18N 1017 1018 buttonPanel.add(_edit); 1019 _editLocalVariablesDialog.getRootPane().setDefaultButton(_edit); 1020 1021 _editLocalVariablesDialog.addWindowListener(new java.awt.event.WindowAdapter() { 1022 @Override 1023 public void windowClosing(java.awt.event.WindowEvent e) { 1024 _editLocalVariablesDialog.setVisible(false); 1025 _editLocalVariablesDialog.dispose(); 1026 _editLocalVariablesDialog = null; 1027 setPopupMenuLock(false); 1028 } 1029 }); 1030 1031 contentPanel.add(buttonPanel); 1032 1033 _autoSystemName.addItemListener((ItemEvent e) -> { 1034 autoSystemName(); 1035 }); 1036 // addLogixNGFrame.setLocationRelativeTo(component); 1037 _editLocalVariablesDialog.pack(); 1038 _editLocalVariablesDialog.setLocationRelativeTo(null); 1039 1040 _editLocalVariablesDialog.setVisible(true); 1041 } 1042 } 1043 1044 /** 1045 * Respond to the Change user name menu choice in the popup menu. 1046 * 1047 * @param femaleSocket the female socket 1048 * @param path the path to the item the user has clicked on 1049 */ 1050 final protected void changeUsername(FemaleSocket femaleSocket, TreePath path) { 1051 // possible change 1052 _showReminder = true; 1053 setPopupMenuLock(true); 1054 // make an Edit Frame 1055 if (_changeUsernameDialog == null) { 1056 MaleSocket maleSocket = femaleSocket.getConnectedSocket(); 1057 1058 // Edit ConditionalNG 1059 _edit = new JButton(Bundle.getMessage("ButtonOK")); // NOI18N 1060 _edit.addActionListener((ActionEvent e) -> { 1061 1062 boolean hasErrors = false; 1063 if (hasErrors) { 1064 String errorMsg = ""; 1065 JmriJOptionPane.showMessageDialog(null, 1066 Bundle.getMessage("ValidateErrorMessage", errorMsg), 1067 Bundle.getMessage("ValidateErrorTitle"), 1068 JmriJOptionPane.ERROR_MESSAGE); 1069 1070 } else { 1071 _treePane._femaleRootSocket.unregisterListeners(); 1072 1073 runOnConditionalNGThreadOrGUIThreadEventually( 1074 _treePane._femaleRootSocket.getConditionalNG(), 1075 () -> { 1076 1077 String username = _usernameField.getText(); 1078 if (username.equals("")) username = null; 1079 1080 // Only change user name if it's changed 1081 if (((username == null) && (maleSocket.getUserName() != null)) 1082 || ((username != null) && !username.equals(maleSocket.getUserName()))) { 1083 1084 if (username != null) { 1085 NamedBean nB = maleSocket.getManager().getByUserName(username); 1086 if (nB != null) { 1087 String uname = username; 1088 ThreadingUtil.runOnGUIEventually(() -> { 1089 log.error("User name is not unique {}", uname); 1090 String msg = Bundle.getMessage("WarningUserName", new Object[]{("" + uname)}); 1091 JmriJOptionPane.showMessageDialog(null, msg, 1092 Bundle.getMessage("WarningTitle"), 1093 JmriJOptionPane.ERROR_MESSAGE); 1094 }); 1095 username = null; 1096 } 1097 } 1098 1099 maleSocket.setUserName(username); 1100 1101 MaleSocket m = maleSocket; 1102 while (! (m instanceof NamedBean)) m = (MaleSocket) m.getObject(); 1103 1104 NamedBeanHandleManager nbMan = InstanceManager.getDefault(NamedBeanHandleManager.class); 1105 if (nbMan.inUse(maleSocket.getSystemName(), (NamedBean)m)) { 1106 String msg = Bundle.getMessage("UpdateToUserName", new Object[]{maleSocket.getManager().getBeanTypeHandled(), username, maleSocket.getSystemName()}); 1107 int optionPane = JmriJOptionPane.showConfirmDialog(null, 1108 msg, Bundle.getMessage("UpdateToUserNameTitle"), 1109 JmriJOptionPane.YES_NO_OPTION); 1110 if (optionPane == JmriJOptionPane.YES_OPTION) { 1111 //This will update the bean reference from the systemName to the userName 1112 try { 1113 nbMan.updateBeanFromSystemToUser((NamedBean)m); 1114 } catch (JmriException ex) { 1115 //We should never get an exception here as we already check that the username is not valid 1116 log.error("Impossible exception setting user name", ex); 1117 } 1118 } 1119 } 1120 } 1121 1122 ThreadingUtil.runOnGUIEventually(() -> { 1123 if (_treePane._femaleRootSocket.isActive()) { 1124 _treePane._femaleRootSocket.registerListeners(); 1125 } 1126 _changeUsernameDialog.dispose(); 1127 _changeUsernameDialog = null; 1128 for (TreeModelListener l : _treePane.femaleSocketTreeModel.listeners) { 1129 TreeModelEvent tme = new TreeModelEvent( 1130 femaleSocket, 1131 path.getPath() 1132 ); 1133 l.treeNodesChanged(tme); 1134 } 1135 _treePane._tree.updateUI(); 1136 }); 1137 setPopupMenuLock(false); 1138 }); 1139 } 1140 }); 1141// _edit.setToolTipText(Bundle.getMessage("EditButtonHint")); // NOI18N 1142 1143// makeAddEditFrame(false, femaleSocket, _editSwingConfiguratorInterface, _edit); // NOI18N 1144 1145 _changeUsernameDialog = new JDialog( 1146 this, 1147 Bundle.getMessage( 1148 "EditLocalVariablesDialogTitle", 1149 femaleSocket.getLongDescription()), 1150 false); 1151 // frame.addHelpMenu( 1152 // "package.jmri.jmrit.logixng.tools.swing.ConditionalNGAddEdit", true); // NOI18N 1153 Container contentPanel = _changeUsernameDialog.getContentPane(); 1154 contentPanel.setLayout(new BoxLayout(contentPanel, BoxLayout.Y_AXIS)); 1155 1156// JPanel tablePanel = new JPanel(); 1157 1158 JLabel usernameLabel = new JLabel("Username"); 1159 _usernameField.setText(maleSocket.getUserName()); 1160 1161 contentPanel.add(usernameLabel); 1162 contentPanel.add(_usernameField); 1163 1164 // set up create and cancel buttons 1165 JPanel buttonPanel = new JPanel(); 1166 buttonPanel.setLayout(new FlowLayout()); 1167 1168 // Cancel 1169 JButton cancel = new JButton(Bundle.getMessage("ButtonCancel")); // NOI18N 1170 buttonPanel.add(cancel); 1171 cancel.addActionListener((ActionEvent e) -> { 1172 _changeUsernameDialog.setVisible(false); 1173 _changeUsernameDialog.dispose(); 1174 _changeUsernameDialog = null; 1175 setPopupMenuLock(false); 1176 }); 1177 // cancel.setToolTipText(Bundle.getMessage("CancelLogixButtonHint")); // NOI18N 1178 cancel.setToolTipText("CancelLogixButtonHint"); // NOI18N 1179 1180 buttonPanel.add(_edit); 1181 _changeUsernameDialog.getRootPane().setDefaultButton(_edit); 1182 1183 _changeUsernameDialog.addWindowListener(new java.awt.event.WindowAdapter() { 1184 @Override 1185 public void windowClosing(java.awt.event.WindowEvent e) { 1186 _changeUsernameDialog.setVisible(false); 1187 _changeUsernameDialog.dispose(); 1188 _changeUsernameDialog = null; 1189 setPopupMenuLock(false); 1190 } 1191 }); 1192 1193 contentPanel.add(buttonPanel); 1194 1195 _autoSystemName.addItemListener((ItemEvent e) -> { 1196 autoSystemName(); 1197 }); 1198 // addLogixNGFrame.setLocationRelativeTo(component); 1199 _changeUsernameDialog.pack(); 1200 _changeUsernameDialog.setLocationRelativeTo(null); 1201 1202 _changeUsernameDialog.setVisible(true); 1203 } 1204 } 1205 1206 /** 1207 * Enable/disable fields for data entry when user selects to have system 1208 * name automatically generated. 1209 */ 1210 final protected void autoSystemName() { 1211 if (_autoSystemName.isSelected()) { 1212 _systemName.setEnabled(false); 1213 _sysNameLabel.setEnabled(false); 1214 } else { 1215 _systemName.setEnabled(true); 1216 _sysNameLabel.setEnabled(true); 1217 } 1218 } 1219 1220 /** 1221 * Respond to the Cancel button in Rename socket window. 1222 * <p> 1223 * Note: Also get there if the user closes the Rename socket window. 1224 * 1225 * @param e The event heard 1226 */ 1227 final protected void cancelRenameSocketPressed(ActionEvent e) { 1228 _renameSocketDialog.setVisible(false); 1229 _renameSocketDialog.dispose(); 1230 _renameSocketDialog = null; 1231 setPopupMenuLock(false); 1232 this.setVisible(true); 1233 } 1234 1235 /** 1236 * Respond to the Cancel button in Add ConditionalNG window. 1237 * <p> 1238 * Note: Also get there if the user closes the Add ConditionalNG window. 1239 * 1240 * @param e The event heard 1241 */ 1242 final protected void cancelCreateItem(ActionEvent e) { 1243 _addItemDialog.setVisible(false); 1244 _addSwingConfiguratorInterface.dispose(); 1245 _addItemDialog.dispose(); 1246 _addItemDialog = null; 1247 setPopupMenuLock(false); 1248// _inCopyMode = false; 1249 this.setVisible(true); 1250 } 1251 1252 1253 /** 1254 * Respond to the Cancel button in Add ConditionalNG window. 1255 * <p> 1256 * Note: Also get there if the user closes the Add ConditionalNG window. 1257 * 1258 * @param e The event heard 1259 */ 1260 final protected void cancelEditPressed(ActionEvent e) { 1261 for (Map.Entry<SwingConfiguratorInterface, Base> entry : _swingConfiguratorInterfaceList) { 1262 // Abort if we cannot close the dialog 1263 if (!entry.getKey().canClose()) return; 1264 } 1265 1266 _editActionExpressionDialog.setVisible(false); 1267 1268 for (Map.Entry<SwingConfiguratorInterface, Base> entry : _swingConfiguratorInterfaceList) { 1269 entry.getKey().dispose(); 1270 } 1271 _editActionExpressionDialog.dispose(); 1272 _editActionExpressionDialog = null; 1273 setPopupMenuLock(false); 1274 this.setVisible(true); 1275 } 1276 1277 1278 protected void executeEvaluate(SwingConfiguratorInterface swi, MaleSocket maleSocket) { 1279 swi.executeEvaluate(maleSocket); 1280 } 1281 1282 private boolean itemIsSystem(FemaleSocket femaleSocket) { 1283 return (femaleSocket.isConnected()) 1284 && femaleSocket.getConnectedSocket().isSystem(); 1285 } 1286 1287 private boolean parentIsSystem(FemaleSocket femaleSocket) { 1288 Base parent = femaleSocket.getParent(); 1289 while ((parent != null) && !(femaleSocket.getParent() instanceof MaleSocket)) { 1290 parent = parent.getParent(); 1291 } 1292 return (parent != null) && ((MaleSocket)parent).isSystem(); 1293 } 1294 1295 /** 1296 * Asks the user if edit a system node. 1297 * @return true if not edit system node, else return false 1298 */ 1299 private boolean abortEditAboutSystem(Base b) { 1300 int result = JmriJOptionPane.showConfirmDialog( 1301 this, 1302 Bundle.getMessage("TreeEditor_ChangeSystemNode"), 1303 b.getLongDescription(), 1304 JmriJOptionPane.YES_NO_OPTION); 1305 1306 return ( result != JmriJOptionPane.YES_OPTION ); 1307 } 1308 1309 private void editItem(FemaleSocket femaleSocket, TreePath path) { 1310 if (itemIsSystem(femaleSocket) && abortEditAboutSystem(femaleSocket.getConnectedSocket())) { 1311 return; 1312 } 1313 editPressed(femaleSocket, path); 1314 } 1315 1316 private void removeItem(FemaleSocket femaleSocket, TreePath path) { 1317 if ((parentIsSystem(femaleSocket) || itemIsSystem(femaleSocket)) && abortEditAboutSystem(femaleSocket.getConnectedSocket())) { 1318 return; 1319 } 1320 DeleteBeanWorker worker = new DeleteBeanWorker(femaleSocket, path); 1321 worker.execute(); 1322 } 1323 1324 private void cutItem(FemaleSocket femaleSocket, TreePath path) { 1325 if ((parentIsSystem(femaleSocket) || itemIsSystem(femaleSocket)) && abortEditAboutSystem(femaleSocket.getConnectedSocket())) { 1326 return; 1327 } 1328 1329 if (femaleSocket.isConnected()) { 1330 _treePane._femaleRootSocket.unregisterListeners(); 1331 1332 runOnConditionalNGThreadOrGUIThreadEventually( 1333 _treePane._femaleRootSocket.getConditionalNG(), 1334 () -> { 1335 Clipboard clipboard = 1336 InstanceManager.getDefault(LogixNG_Manager.class).getClipboard(); 1337 List<String> errors = new ArrayList<>(); 1338 MaleSocket maleSocket = femaleSocket.getConnectedSocket(); 1339 femaleSocket.disconnect(); 1340 if (!clipboard.add(maleSocket, errors)) { 1341 JmriJOptionPane.showMessageDialog(this, 1342 String.join("<br>", errors), 1343 Bundle.getMessage("TitleError"), 1344 JmriJOptionPane.ERROR_MESSAGE); 1345 } 1346 ThreadingUtil.runOnGUIEventually(() -> { 1347 maleSocket.forEntireTree((Base b) -> { 1348 b.removePropertyChangeListener(_treePane); 1349 if (_clipboardEditor != null) { 1350 b.addPropertyChangeListener(_clipboardEditor._treePane); 1351 } 1352 }); 1353 _treePane._femaleRootSocket.registerListeners(); 1354 _treePane.updateTree(femaleSocket, path.getPath()); 1355 }); 1356 }); 1357 } else { 1358 log.error("_currentFemaleSocket is not connected"); 1359 } 1360 } 1361 1362 private void copyItem(FemaleSocket femaleSocket) { 1363 if ((parentIsSystem(femaleSocket) || itemIsSystem(femaleSocket)) && abortEditAboutSystem(femaleSocket.getConnectedSocket())) { 1364 return; 1365 } 1366 1367 if (femaleSocket.isConnected()) { 1368 _treePane._femaleRootSocket.unregisterListeners(); 1369 1370 runOnConditionalNGThreadOrGUIThreadEventually( 1371 _treePane._femaleRootSocket.getConditionalNG(), 1372 () -> { 1373 Clipboard clipboard = 1374 InstanceManager.getDefault(LogixNG_Manager.class).getClipboard(); 1375 Map<String, String> systemNames = new HashMap<>(); 1376 Map<String, String> userNames = new HashMap<>(); 1377 MaleSocket maleSocket = null; 1378 try { 1379 maleSocket = (MaleSocket) femaleSocket 1380 .getConnectedSocket() 1381 .getDeepCopy(systemNames, userNames); 1382 List<String> errors = new ArrayList<>(); 1383 if (!clipboard.add( 1384 maleSocket, 1385 errors)) { 1386 JmriJOptionPane.showMessageDialog(this, 1387 String.join("<br>", errors), 1388 Bundle.getMessage("TitleError"), 1389 JmriJOptionPane.ERROR_MESSAGE); 1390 } 1391 } catch (JmriException ex) { 1392 log.error("getDeepCopy thrown exception: {}", ex, ex); 1393 ThreadingUtil.runOnGUIEventually(() -> { 1394 JmriJOptionPane.showMessageDialog(null, 1395 "An exception has occured: "+ex.getMessage(), 1396 "An error has occured", 1397 JmriJOptionPane.ERROR_MESSAGE); 1398 }); 1399 } 1400 if (maleSocket != null) { 1401 MaleSocket socket = maleSocket; 1402 ThreadingUtil.runOnGUIEventually(() -> { 1403 socket.forEntireTree((Base b) -> { 1404 if (_clipboardEditor != null) { 1405 b.addPropertyChangeListener(_clipboardEditor._treePane); 1406 } 1407 }); 1408 }); 1409 } 1410 }); 1411 1412 _treePane._femaleRootSocket.registerListeners(); 1413 } else { 1414 log.error("_currentFemaleSocket is not connected"); 1415 } 1416 } 1417 1418 private void pasteItem(FemaleSocket femaleSocket, TreePath path) { 1419 if (parentIsSystem(femaleSocket) && abortEditAboutSystem(femaleSocket.getParent())) { 1420 return; 1421 } 1422 1423 if (! femaleSocket.isConnected()) { 1424 _treePane._femaleRootSocket.unregisterListeners(); 1425 1426 runOnConditionalNGThreadOrGUIThreadEventually( 1427 _treePane._femaleRootSocket.getConditionalNG(), 1428 () -> { 1429 Clipboard clipboard = 1430 InstanceManager.getDefault(LogixNG_Manager.class).getClipboard(); 1431 try { 1432 if (clipboard.getTopItem() == null) { 1433 return; 1434 } 1435 femaleSocket.connect(clipboard.fetchTopItem()); 1436 List<String> errors = new ArrayList<>(); 1437 if (!femaleSocket.setParentForAllChildren(errors)) { 1438 JmriJOptionPane.showMessageDialog(this, 1439 String.join("<br>", errors), 1440 Bundle.getMessage("TitleError"), 1441 JmriJOptionPane.ERROR_MESSAGE); 1442 } 1443 } catch (SocketAlreadyConnectedException ex) { 1444 log.error("item cannot be connected", ex); 1445 } 1446 ThreadingUtil.runOnGUIEventually(() -> { 1447 _treePane._femaleRootSocket.forEntireTree((Base b) -> { 1448 // Remove the listener if it is already 1449 // added so we don't end up with duplicate 1450 // listeners. 1451 b.removePropertyChangeListener(_treePane); 1452 b.addPropertyChangeListener(_treePane); 1453 }); 1454 _treePane._femaleRootSocket.registerListeners(); 1455 _treePane.updateTree(femaleSocket, path.getPath()); 1456 }); 1457 }); 1458 } else { 1459 log.error("_currentFemaleSocket is connected"); 1460 } 1461 } 1462 1463 private void pasteCopy(FemaleSocket femaleSocket, TreePath path) { 1464 if (parentIsSystem(femaleSocket) && abortEditAboutSystem(femaleSocket.getParent())) { 1465 return; 1466 } 1467 1468 if (! femaleSocket.isConnected()) { 1469 _treePane._femaleRootSocket.unregisterListeners(); 1470 1471 runOnConditionalNGThreadOrGUIThreadEventually( 1472 _treePane._femaleRootSocket.getConditionalNG(), 1473 () -> { 1474 Clipboard clipboard = 1475 InstanceManager.getDefault(LogixNG_Manager.class).getClipboard(); 1476 1477 Map<String, String> systemNames = new HashMap<>(); 1478 Map<String, String> userNames = new HashMap<>(); 1479 MaleSocket maleSocket = null; 1480 try { 1481 maleSocket = (MaleSocket) clipboard.getTopItem() 1482 .getDeepCopy(systemNames, userNames); 1483 } catch (JmriException ex) { 1484 log.error("getDeepCopy thrown exception: {}", ex, ex); 1485 ThreadingUtil.runOnGUIEventually(() -> { 1486 JmriJOptionPane.showMessageDialog(null, 1487 "An exception has occured: "+ex.getMessage(), 1488 "An error has occured", 1489 JmriJOptionPane.ERROR_MESSAGE); 1490 }); 1491 } 1492 if (maleSocket != null) { 1493 try { 1494 femaleSocket.connect(maleSocket); 1495 List<String> errors = new ArrayList<>(); 1496 if (!femaleSocket.setParentForAllChildren(errors)) { 1497 JmriJOptionPane.showMessageDialog(this, 1498 String.join("<br>", errors), 1499 Bundle.getMessage("TitleError"), 1500 JmriJOptionPane.ERROR_MESSAGE); 1501 } 1502 } catch (SocketAlreadyConnectedException ex) { 1503 log.error("item cannot be connected", ex); 1504 } 1505 ThreadingUtil.runOnGUIEventually(() -> { 1506 _treePane._femaleRootSocket.forEntireTree((Base b) -> { 1507 // Remove the listener if it is already 1508 // added so we don't end up with duplicate 1509 // listeners. 1510 b.removePropertyChangeListener(_treePane); 1511 b.addPropertyChangeListener(_treePane); 1512 }); 1513 _treePane._femaleRootSocket.registerListeners(); 1514 _treePane.updateTree(femaleSocket, path.getPath()); 1515 }); 1516 } 1517 }); 1518 } else { 1519 log.error("_currentFemaleSocket is connected"); 1520 } 1521 } 1522 1523 private void doIt(String command, FemaleSocket femaleSocket, TreePath path) { 1524 Base parent = femaleSocket.getParent(); 1525 while ((parent != null) && !(femaleSocket.getParent() instanceof MaleSocket)) { 1526 parent = parent.getParent(); 1527 } 1528 boolean parentIsSystem = (parent != null) && ((MaleSocket)parent).isSystem(); 1529 boolean itemIsSystem = itemIsSystem(femaleSocket); 1530 1531 switch (command) { 1532 case ACTION_COMMAND_RENAME_SOCKET: 1533 if (parentIsSystem && abortEditAboutSystem(femaleSocket.getParent())) break; 1534 renameSocketPressed(femaleSocket, path); 1535 break; 1536 1537 case ACTION_COMMAND_EDIT: 1538 editItem(femaleSocket, path); 1539 break; 1540 1541 case ACTION_COMMAND_REMOVE: 1542 removeItem(femaleSocket, path); 1543 break; 1544 1545 case ACTION_COMMAND_CUT: 1546 cutItem(femaleSocket, path); 1547 break; 1548 1549 case ACTION_COMMAND_COPY: 1550 copyItem(femaleSocket); 1551 break; 1552 1553 case ACTION_COMMAND_PASTE: 1554 pasteItem(femaleSocket, path); 1555 break; 1556 1557 case ACTION_COMMAND_PASTE_COPY: 1558 pasteCopy(femaleSocket, path); 1559 break; 1560 1561 case ACTION_COMMAND_ENABLE: 1562 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1563 1564 femaleSocket.getConnectedSocket().setEnabled(true); 1565 runOnConditionalNGThreadOrGUIThreadEventually( 1566 _treePane._femaleRootSocket.getConditionalNG(), 1567 () -> { 1568 ThreadingUtil.runOnGUIEventually(() -> { 1569 _treePane._femaleRootSocket.unregisterListeners(); 1570 _treePane.updateTree(femaleSocket, path.getPath()); 1571 _treePane._femaleRootSocket.registerListeners(); 1572 }); 1573 }); 1574 break; 1575 1576 case ACTION_COMMAND_DISABLE: 1577 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1578 1579 femaleSocket.getConnectedSocket().setEnabled(false); 1580 runOnConditionalNGThreadOrGUIThreadEventually( 1581 _treePane._femaleRootSocket.getConditionalNG(), 1582 () -> { 1583 ThreadingUtil.runOnGUIEventually(() -> { 1584 _treePane._femaleRootSocket.unregisterListeners(); 1585 _treePane.updateTree(femaleSocket, path.getPath()); 1586 _treePane._femaleRootSocket.registerListeners(); 1587 }); 1588 }); 1589 break; 1590 1591 case ACTION_COMMAND_LOCK: 1592 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1593 1594 femaleSocket.forEntireTree((item) -> { 1595 if (item instanceof MaleSocket) { 1596 ((MaleSocket)item).setLocked(true); 1597 } 1598 }); 1599 _treePane.updateTree(femaleSocket, path.getPath()); 1600 break; 1601 1602 case ACTION_COMMAND_UNLOCK: 1603 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1604 1605 femaleSocket.forEntireTree((item) -> { 1606 if (item instanceof MaleSocket) { 1607 ((MaleSocket)item).setLocked(false); 1608 } 1609 }); 1610 _treePane.updateTree(femaleSocket, path.getPath()); 1611 break; 1612 1613 case ACTION_COMMAND_LOCAL_VARIABLES: 1614 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1615 editLocalVariables(femaleSocket, path); 1616 break; 1617 1618 case ACTION_COMMAND_CHANGE_USERNAME: 1619 if (itemIsSystem && abortEditAboutSystem(femaleSocket.getConnectedSocket())) break; 1620 changeUsername(femaleSocket, path); 1621 break; 1622 1623 case ACTION_COMMAND_EXECUTE_EVALUATE: 1624 Base object = femaleSocket.getConnectedSocket(); 1625 if (object == null) throw new NullPointerException("object is null"); 1626 while (object instanceof MaleSocket) { 1627 object = ((MaleSocket)object).getObject(); 1628 } 1629 SwingConfiguratorInterface swi = 1630 SwingTools.getSwingConfiguratorForClass(object.getClass()); 1631 executeEvaluate(swi, femaleSocket.getConnectedSocket()); 1632 break; 1633 1634/* 1635 case ACTION_COMMAND_EXPAND_TREE: 1636 // jtree expand sub tree 1637 // https://stackoverflow.com/questions/15210979/how-do-i-auto-expand-a-jtree-when-setting-a-new-treemodel 1638 // https://www.tutorialspoint.com/how-to-expand-jtree-row-to-display-all-the-nodes-and-child-nodes-in-java 1639 // To expand all rows, do this: 1640 for (int i = 0; i < tree.getRowCount(); i++) { 1641 tree.expandRow(i); 1642 } 1643 1644 tree.expandPath(_currentPath); 1645 tree.updateUI(); 1646 break; 1647*/ 1648 default: 1649 // Check if the action is a female socket operation 1650 if (!checkFemaleSocketOperation(femaleSocket, parentIsSystem, itemIsSystem, command)) { 1651 log.error("e.getActionCommand() returns unknown value {}", command); 1652 } 1653 } 1654 } 1655 1656 private boolean checkFemaleSocketOperation( 1657 FemaleSocket femaleSocket, 1658 boolean parentIsSystem, 1659 boolean itemIsSystem, 1660 String command) { 1661 1662 for (FemaleSocketOperation oper : FemaleSocketOperation.values()) { 1663 if (oper.name().equals(command)) { 1664 if ((parentIsSystem || itemIsSystem) && abortEditAboutSystem(femaleSocket.getParent())) return true; 1665 femaleSocket.doSocketOperation(oper); 1666 return true; 1667 } 1668 } 1669 return false; 1670 } 1671 1672 private boolean parentIsLocked(FemaleSocket femaleSocket) { 1673 Base parent = femaleSocket.getParent(); 1674 while ((parent != null) && !(parent instanceof MaleSocket)) { 1675 parent = parent.getParent(); 1676 } 1677 return (parent != null) && ((MaleSocket)parent).isLocked(); 1678 } 1679 1680 protected final class PopupMenu extends JPopupMenu implements ActionListener { 1681 1682 private final JTree _tree; 1683// private final FemaleSocketTreeModel _model; 1684 private final FemaleSocket _currentFemaleSocket; 1685 private final TreePath _currentPath; 1686 1687 private JMenuItem menuItemRenameSocket; 1688 private JMenuItem menuItemRemove; 1689 private JMenuItem menuItemCut; 1690 private JMenuItem menuItemCopy; 1691 private JMenuItem menuItemPaste; 1692 private JMenuItem menuItemPasteCopy; 1693 private final Map<FemaleSocketOperation, JMenuItem> menuItemFemaleSocketOperation 1694 = new HashMap<>(); 1695 private JMenuItem menuItemEnable; 1696 private JMenuItem menuItemDisable; 1697 private JMenuItem menuItemLock; 1698 private JMenuItem menuItemUnlock; 1699 private JMenuItem menuItemLocalVariables; 1700 private JMenuItem menuItemChangeUsername; 1701 private JMenuItem menuItemExecuteEvaluate; 1702// private JMenuItem menuItemExpandTree; 1703 1704 private final boolean _isConnected; 1705 private final boolean _canConnectFromClipboard; 1706 private final boolean _disableForRoot; 1707 private final boolean _isLocked; 1708 private final boolean _parentIsLocked; 1709 1710 1711 PopupMenu(int x, int y, FemaleSocket femaleSocket, TreePath path, boolean onlyAddItems) { 1712 1713 if (_treePane._tree == null) throw new IllegalArgumentException("_tree is null"); 1714 1715 _tree = _treePane._tree; 1716 1717 _currentFemaleSocket = femaleSocket; 1718 _currentPath = path; 1719 _isConnected = femaleSocket.isConnected(); 1720 1721 Clipboard clipboard = InstanceManager.getDefault(LogixNG_Manager.class).getClipboard(); 1722 1723 MaleSocket topItem = clipboard.getTopItem(); 1724 1725 _canConnectFromClipboard = 1726 topItem != null 1727 && femaleSocket.isCompatible(topItem) 1728 && !femaleSocket.isAncestor(topItem); 1729 1730 _disableForRoot = _disableRootRemoveCutCopy 1731 && (_currentFemaleSocket == _treePane._femaleRootSocket); 1732 1733 _isLocked = _isConnected && femaleSocket.getConnectedSocket().isLocked(); 1734 1735 _parentIsLocked = parentIsLocked(femaleSocket); 1736 1737 if (onlyAddItems) { 1738 addNewItemTypes(this); 1739 } else { 1740 if (_disableRootPopup 1741 && (_currentFemaleSocket == _treePane._femaleRootSocket)) { 1742 JmriJOptionPane.showMessageDialog(null, 1743 Bundle.getMessage("TreeEditor_RootHasNoPopupMenu"), 1744 Bundle.getMessage("TreeEditor_Info"), 1745 JmriJOptionPane.ERROR_MESSAGE); 1746 return; 1747 } 1748 1749 menuItemRenameSocket = new JMenuItem(Bundle.getMessage("PopupMenuRenameSocket")); 1750 menuItemRenameSocket.addActionListener(this); 1751 menuItemRenameSocket.setActionCommand(ACTION_COMMAND_RENAME_SOCKET); 1752 add(menuItemRenameSocket); 1753 addSeparator(); 1754 1755 if (!_isConnected && !_parentIsLocked) { 1756 JMenu addMenu = new JMenu(Bundle.getMessage("PopupMenuAdd")); 1757// addMenu.setMnemonic(KeyEvent.VK_F); 1758// addMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1759 addNewItemTypes(addMenu); 1760 add(addMenu); 1761 } 1762 1763 if (_isConnected && !_isLocked) { 1764 JMenuItem menuItemEdit = new JMenuItem(Bundle.getMessage("PopupMenuEdit")); 1765 menuItemEdit.addActionListener(this); 1766 menuItemEdit.setActionCommand(ACTION_COMMAND_EDIT); 1767 menuItemEdit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1768 add(menuItemEdit); 1769 } 1770 addSeparator(); 1771 menuItemRemove = new JMenuItem(Bundle.getMessage("PopupMenuRemove")); 1772 menuItemRemove.addActionListener(this); 1773 menuItemRemove.setActionCommand(ACTION_COMMAND_REMOVE); 1774 menuItemRemove.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1775 add(menuItemRemove); 1776 addSeparator(); 1777 menuItemCut = new JMenuItem(Bundle.getMessage("PopupMenuCut")); 1778 menuItemCut.addActionListener(this); 1779 menuItemCut.setActionCommand(ACTION_COMMAND_CUT); 1780 menuItemCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1781 add(menuItemCut); 1782 menuItemCopy = new JMenuItem(Bundle.getMessage("PopupMenuCopy")); 1783 menuItemCopy.addActionListener(this); 1784 menuItemCopy.setActionCommand(ACTION_COMMAND_COPY); 1785 menuItemCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1786 add(menuItemCopy); 1787 menuItemPaste = new JMenuItem(Bundle.getMessage("PopupMenuPaste")); 1788 menuItemPaste.addActionListener(this); 1789 menuItemPaste.setActionCommand(ACTION_COMMAND_PASTE); 1790 menuItemPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1791 add(menuItemPaste); 1792 menuItemPasteCopy = new JMenuItem(Bundle.getMessage("PopupMenuPasteCopy")); 1793 menuItemPasteCopy.addActionListener(this); 1794 menuItemPasteCopy.setActionCommand(ACTION_COMMAND_PASTE_COPY); 1795 menuItemPasteCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, 1796 Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() + InputEvent.SHIFT_DOWN_MASK)); 1797 add(menuItemPasteCopy); 1798 addSeparator(); 1799 1800 for (FemaleSocketOperation oper : FemaleSocketOperation.values()) { 1801 JMenuItem menuItem = new JMenuItem(oper.toString()); 1802 menuItem.addActionListener(this); 1803 menuItem.setActionCommand(oper.name()); 1804 add(menuItem); 1805 menuItemFemaleSocketOperation.put(oper, menuItem); 1806 if (oper.hasKey()) { 1807 menuItem.setAccelerator(KeyStroke.getKeyStroke( 1808 oper.getKeyCode(), oper.getModifiers())); 1809 } 1810 } 1811 1812 addSeparator(); 1813 menuItemEnable = new JMenuItem(Bundle.getMessage("PopupMenuEnable")); 1814 menuItemEnable.addActionListener(this); 1815 menuItemEnable.setActionCommand(ACTION_COMMAND_ENABLE); 1816 menuItemEnable.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, 1817 Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() + InputEvent.SHIFT_DOWN_MASK)); 1818 add(menuItemEnable); 1819 menuItemDisable = new JMenuItem(Bundle.getMessage("PopupMenuDisable")); 1820 menuItemDisable.addActionListener(this); 1821 menuItemDisable.setActionCommand(ACTION_COMMAND_DISABLE); 1822 menuItemDisable.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, 1823 Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx())); 1824 add(menuItemDisable); 1825 menuItemLock = new JMenuItem(Bundle.getMessage("PopupMenuLock")); 1826 menuItemLock.addActionListener(this); 1827 menuItemLock.setActionCommand(ACTION_COMMAND_LOCK); 1828 add(menuItemLock); 1829 menuItemUnlock = new JMenuItem(Bundle.getMessage("PopupMenuUnlock")); 1830 menuItemUnlock.addActionListener(this); 1831 menuItemUnlock.setActionCommand(ACTION_COMMAND_UNLOCK); 1832 add(menuItemUnlock); 1833 1834 addSeparator(); 1835 menuItemLocalVariables = new JMenuItem(Bundle.getMessage("PopupMenuLocalVariables")); 1836 menuItemLocalVariables.addActionListener(this); 1837 menuItemLocalVariables.setActionCommand(ACTION_COMMAND_LOCAL_VARIABLES); 1838 add(menuItemLocalVariables); 1839 1840 addSeparator(); 1841 menuItemChangeUsername = new JMenuItem(Bundle.getMessage("PopupMenuChangeUsername")); 1842 menuItemChangeUsername.addActionListener(this); 1843 menuItemChangeUsername.setActionCommand(ACTION_COMMAND_CHANGE_USERNAME); 1844 add(menuItemChangeUsername); 1845 1846 if (_enableExecuteEvaluate) { 1847 addSeparator(); 1848 menuItemExecuteEvaluate = new JMenuItem(); // The text is set later 1849 menuItemExecuteEvaluate.addActionListener(this); 1850 menuItemExecuteEvaluate.setActionCommand(ACTION_COMMAND_EXECUTE_EVALUATE); 1851 add(menuItemExecuteEvaluate); 1852 } 1853 /* 1854 addSeparator(); 1855 menuItemExpandTree = new JMenuItem(Bundle.getMessage("PopupMenuExpandTree")); 1856 menuItemExpandTree.addActionListener(this); 1857 menuItemExpandTree.setActionCommand(ACTION_COMMAND_EXPAND_TREE); 1858 add(menuItemExpandTree); 1859 */ 1860 setOpaque(true); 1861 setLightWeightPopupEnabled(true); 1862 1863 menuItemRemove.setEnabled(_isConnected && !_isLocked && !_parentIsLocked && !_disableForRoot); 1864 menuItemCut.setEnabled(_isConnected && !_isLocked && !_parentIsLocked && !_disableForRoot); 1865 menuItemCopy.setEnabled(_isConnected && !_disableForRoot); 1866 menuItemPaste.setEnabled(!_isConnected && !_parentIsLocked && _canConnectFromClipboard); 1867 menuItemPasteCopy.setEnabled(!_isConnected && !_parentIsLocked && _canConnectFromClipboard); 1868 1869 if (_isConnected && !_disableForRoot) { 1870 menuItemEnable.setEnabled(!femaleSocket.getConnectedSocket().isEnabled() && !_isLocked); 1871 menuItemDisable.setEnabled(femaleSocket.getConnectedSocket().isEnabled() && !_isLocked); 1872 } else { 1873 menuItemEnable.setEnabled(false); 1874 menuItemDisable.setEnabled(false); 1875 } 1876 1877 for (FemaleSocketOperation oper : FemaleSocketOperation.values()) { 1878 JMenuItem menuItem = menuItemFemaleSocketOperation.get(oper); 1879 menuItem.setEnabled(femaleSocket.isSocketOperationAllowed(oper) && !_parentIsLocked); 1880 } 1881 1882 AtomicBoolean isAnyLocked = new AtomicBoolean(false); 1883 AtomicBoolean isAnyUnlocked = new AtomicBoolean(false); 1884 1885 _currentFemaleSocket.forEntireTree((item) -> { 1886 if (item instanceof MaleSocket) { 1887 isAnyLocked.set(isAnyLocked.get() || ((MaleSocket)item).isLocked()); 1888 isAnyUnlocked.set(isAnyUnlocked.get() || !((MaleSocket)item).isLocked()); 1889 } 1890 }); 1891 menuItemLock.setEnabled(isAnyUnlocked.get()); 1892 menuItemUnlock.setEnabled(isAnyLocked.get()); 1893 1894 menuItemLocalVariables.setEnabled(femaleSocket.isConnected() && !_isLocked); 1895 1896 menuItemChangeUsername.setEnabled(femaleSocket.isConnected() && !_isLocked); 1897 1898 if (_enableExecuteEvaluate) { 1899 menuItemExecuteEvaluate.setEnabled(femaleSocket.isConnected()); 1900 1901 if (femaleSocket.isConnected()) { 1902 Base object = _currentFemaleSocket.getConnectedSocket(); 1903 if (object == null) throw new NullPointerException("object is null"); 1904 while (object instanceof MaleSocket) { 1905 object = ((MaleSocket)object).getObject(); 1906 } 1907 menuItemExecuteEvaluate.setText( 1908 SwingTools.getSwingConfiguratorForClass(object.getClass()) 1909 .getExecuteEvaluateMenuText()); 1910 } 1911 } 1912 } 1913 1914 show(_tree, x, y); 1915 } 1916 1917 private void addNewItemTypes(Container container) { 1918 Map<Category, List<Class<? extends Base>>> connectableClasses = 1919 _currentFemaleSocket.getConnectableClasses(); 1920 List<Category> list = new ArrayList<>(connectableClasses.keySet()); 1921 Collections.sort(list); 1922 for (Category category : list) { 1923 List<SwingConfiguratorInterface> sciList = new ArrayList<>(); 1924 List<Class<? extends Base>> classes = connectableClasses.get(category); 1925 if (classes != null && !classes.isEmpty()) { 1926 for (Class<? extends Base> clazz : classes) { 1927 SwingConfiguratorInterface sci = SwingTools.getSwingConfiguratorForClass(clazz); 1928 if (sci != null) { 1929 sciList.add(sci); 1930 } else { 1931 log.error("Class {} has no swing configurator interface", clazz.getName()); 1932 } 1933 } 1934 } 1935 1936 Collections.sort(sciList); 1937 1938 JMenu categoryMenu = new JMenu(category.toString()); 1939 for (SwingConfiguratorInterface sci : sciList) { 1940 JMenuItem item = new JMenuItem(sci.toString()); 1941 item.addActionListener((e) -> { 1942 createAddFrame(_currentFemaleSocket, _currentPath, sci); 1943 }); 1944 categoryMenu.add(item); 1945 } 1946 container.add(categoryMenu); 1947 } 1948 } 1949 1950 @Override 1951 public void actionPerformed(ActionEvent e) { 1952 doIt(e.getActionCommand(), _currentFemaleSocket, _currentPath); 1953 } 1954 1955 } 1956 1957 1958 // This class is copied from BeanTableDataModel 1959 private class DeleteBeanWorker extends SwingWorker<Void, Void> { 1960 1961 private final FemaleSocket _currentFemaleSocket; 1962 private final TreePath _currentPath; 1963 MaleSocket _maleSocket; 1964 1965 public DeleteBeanWorker(FemaleSocket currentFemaleSocket, TreePath currentPath) { 1966 _currentFemaleSocket = currentFemaleSocket; 1967 _currentPath = currentPath; 1968 _maleSocket = _currentFemaleSocket.getConnectedSocket(); 1969 } 1970 1971 public int getDisplayDeleteMsg() { 1972 return InstanceManager.getDefault(UserPreferencesManager.class).getMultipleChoiceOption(getClassName(), "deleteInUse"); 1973 } 1974 1975 public void setDisplayDeleteMsg(int boo) { 1976 InstanceManager.getDefault(UserPreferencesManager.class).setMultipleChoiceOption(getClassName(), "deleteInUse", boo); 1977 } 1978 1979 public void doDelete() { 1980 try { 1981 _currentFemaleSocket.disconnect(); 1982 1983 _maleSocket.getManager().deleteBean(_maleSocket, "DoDelete"); 1984 } catch (PropertyVetoException e) { 1985 //At this stage the DoDelete shouldn't fail, as we have already done a can delete, which would trigger a veto 1986 log.error("Unexpected doDelete failure for {}, {}", _maleSocket, e.getMessage() ); 1987 } 1988 } 1989 1990 /** 1991 * {@inheritDoc} 1992 */ 1993 @Override 1994 public Void doInBackground() { 1995 _treePane._femaleRootSocket.unregisterListeners(); 1996 1997 StringBuilder message = new StringBuilder(); 1998 try { 1999 _maleSocket.getManager().deleteBean(_maleSocket, "CanDelete"); // NOI18N 2000 } catch (PropertyVetoException e) { 2001 if (e.getPropertyChangeEvent().getPropertyName().equals("DoNotDelete")) { // NOI18N 2002 log.warn("Do not Delete {}, {}", _maleSocket, e.getMessage()); 2003 message.append(Bundle.getMessage( 2004 "VetoDeleteBean", 2005 ((NamedBean)_maleSocket.getObject()).getBeanType(), 2006 ((NamedBean)_maleSocket.getObject()).getDisplayName( 2007 NamedBean.DisplayOptions.USERNAME_SYSTEMNAME), 2008 e.getMessage())); 2009 JmriJOptionPane.showMessageDialog(null, message.toString(), 2010 Bundle.getMessage("WarningTitle"), 2011 JmriJOptionPane.ERROR_MESSAGE); 2012 return null; 2013 } 2014 message.append(e.getMessage()); 2015 } 2016 List<String> listenerRefs = new ArrayList<>(); 2017 _maleSocket.getListenerRefsIncludingChildren(listenerRefs); 2018 int count = listenerRefs.size(); 2019 log.debug("Delete with {}", count); 2020 if (getDisplayDeleteMsg() == 0x02 && message.toString().isEmpty()) { 2021 doDelete(); 2022 } else { 2023 final JDialog dialog = new JDialog(); 2024 dialog.setTitle(Bundle.getMessage("WarningTitle")); 2025 dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 2026 JPanel container = new JPanel(); 2027 container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); 2028 container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS)); 2029 if (count > 0) { // warn of listeners attached before delete 2030 2031 String prompt = _maleSocket.getChildCount() > 0 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 2032 JLabel question = new JLabel(Bundle.getMessage( 2033 prompt, 2034 ((NamedBean)_maleSocket.getObject()) 2035 .getDisplayName(NamedBean.DisplayOptions.USERNAME_SYSTEMNAME))); 2036 question.setAlignmentX(Component.CENTER_ALIGNMENT); 2037 container.add(question); 2038 2039 ArrayList<String> tempListenerRefs = new ArrayList<>(); 2040 2041 tempListenerRefs.addAll(listenerRefs); 2042 2043 if (tempListenerRefs.size() > 0) { 2044 ArrayList<String> listeners = new ArrayList<>(); 2045 for (int i = 0; i < tempListenerRefs.size(); i++) { 2046 if (!listeners.contains(tempListenerRefs.get(i))) { 2047 listeners.add(tempListenerRefs.get(i)); 2048 } 2049 } 2050 2051 message.append("<br>"); 2052 message.append(Bundle.getMessage("ReminderInUse", count)); 2053 message.append("<ul>"); 2054 for (int i = 0; i < listeners.size(); i++) { 2055 message.append("<li>"); 2056 message.append(listeners.get(i)); 2057 message.append("</li>"); 2058 } 2059 message.append("</ul>"); 2060 2061 JEditorPane pane = new JEditorPane(); 2062 pane.setContentType("text/html"); 2063 pane.setText("<html>" + message.toString() + "</html>"); 2064 pane.setEditable(false); 2065 JScrollPane jScrollPane = new JScrollPane(pane); 2066 container.add(jScrollPane); 2067 } 2068 } else { 2069 String prompt = _maleSocket.getChildCount() > 0 ? "DeleteWithChildrenPrompt" : "DeletePrompt"; 2070 String msg = MessageFormat.format(Bundle.getMessage(prompt), 2071 new Object[]{_maleSocket.getSystemName()}); 2072 JLabel question = new JLabel(msg); 2073 question.setAlignmentX(Component.CENTER_ALIGNMENT); 2074 container.add(question); 2075 } 2076 2077 final JCheckBox remember = new JCheckBox(Bundle.getMessage("MessageRememberSetting")); 2078 remember.setFont(remember.getFont().deriveFont(10f)); 2079 remember.setAlignmentX(Component.CENTER_ALIGNMENT); 2080 2081 JButton yesButton = new JButton(Bundle.getMessage("ButtonYes")); 2082 JButton noButton = new JButton(Bundle.getMessage("ButtonNo")); 2083 JPanel button = new JPanel(); 2084 button.setAlignmentX(Component.CENTER_ALIGNMENT); 2085 button.add(yesButton); 2086 button.add(noButton); 2087 container.add(button); 2088 2089 noButton.addActionListener((ActionEvent e) -> { 2090 //there is no point in remembering this the user will never be 2091 //able to delete a bean! 2092 dialog.dispose(); 2093 }); 2094 2095 yesButton.addActionListener((ActionEvent e) -> { 2096 if (remember.isSelected()) { 2097 setDisplayDeleteMsg(0x02); 2098 } 2099 doDelete(); 2100 dialog.dispose(); 2101 }); 2102 container.add(remember); 2103 container.setAlignmentX(Component.CENTER_ALIGNMENT); 2104 container.setAlignmentY(Component.CENTER_ALIGNMENT); 2105 dialog.getContentPane().add(container); 2106 dialog.pack(); 2107 dialog.setLocation( 2108 (Toolkit.getDefaultToolkit().getScreenSize().width) / 2 - dialog.getWidth() / 2, 2109 (Toolkit.getDefaultToolkit().getScreenSize().height) / 2 - dialog.getHeight() / 2); 2110 dialog.setModal(true); 2111 dialog.setVisible(true); 2112 } 2113 if (_treePane._femaleRootSocket.isActive()) { 2114 _treePane._femaleRootSocket.registerListeners(); 2115 } 2116 return null; 2117 } 2118 2119 /** 2120 * {@inheritDoc} Minimal implementation to catch and log errors 2121 */ 2122 @Override 2123 protected void done() { 2124 try { 2125 get(); // called to get errors 2126 } catch (InterruptedException | java.util.concurrent.ExecutionException e) { 2127 log.error("Exception while deleting bean", e); 2128 } 2129 _treePane.updateTree(_currentFemaleSocket, _currentPath.getPath()); 2130 } 2131 } 2132 2133 2134 2135 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TreeEditor.class); 2136 2137}