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