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