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