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