001package jmri.jmrit.logixng.tools.swing; 002 003import java.awt.*; 004import java.awt.event.*; 005import java.beans.PropertyChangeEvent; 006import java.beans.PropertyChangeListener; 007import java.util.*; 008import java.util.List; 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.TreePath; 016 017import jmri.jmrit.logixng.FemaleSocket; 018import jmri.InstanceManager; 019import jmri.jmrit.logixng.*; 020import jmri.jmrit.logixng.Module; 021import jmri.jmrit.logixng.implementation.DefaultSymbolTable; 022import jmri.jmrit.logixng.tools.debugger.AbstractDebuggerMaleSocket; 023import jmri.jmrit.logixng.tools.debugger.Debugger; 024import jmri.jmrit.logixng.tools.swing.TreePane.FemaleSocketDecorator; 025import jmri.util.JmriJFrame; 026 027/** 028 * Editor of ConditionalNG 029 * 030 * @author Daniel Bergqvist 2018 031 */ 032public final class ConditionalNGDebugger extends JmriJFrame implements PropertyChangeListener { 033 034 private static final int panelWidth = 700; 035 private static final int panelHeight = 500; 036 037 private final Debugger _debugger = InstanceManager.getDefault(Debugger.class); 038 private final JPanel _currentTreePanePanel; 039 private final TreePane _conditionalNG_TreePane; 040 private TreePane _currentTreePane; 041 private final List<TreePane> _treePanes = new ArrayList<>(); 042 private final JMenuItem _execute; 043 private final JMenuItem _runItem; 044 private final JMenuItem _stepOverItem; 045 private final JMenuItem _stepIntoItem; 046 private final FemaleSocketDecorator _decorator; 047 private final ConditionalNG _conditionalNG; 048 private Deque<Module> _lastModuleStack = new LinkedList<>(); 049 private Module _currentModule; 050 private AbstractDebuggerMaleSocket _currentMaleSocket; 051 private State _currentState = State.None; 052 private boolean _run = false; 053 private MaleSocket _rootSocket; 054 private final JLabel _actionExpressionInfoLabel = new JLabel(); 055 056 private final Object _lock = new Object(); 057 private boolean _continue = false; 058 059 private final DebuggerSymbolTableModel _symbolTableModel; 060 061 /** 062 * Maintain a list of listeners -- normally only one. 063 */ 064 private final List<ConditionalNGEventListener> listenerList = new ArrayList<>(); 065 066 /** 067 * This contains a list of commands to be processed by the listener 068 * recipient. 069 */ 070 final Map<String, String> logixNGData = new HashMap<>(); 071 072 /** 073 * Construct a ConditionalEditor. 074 * 075 * @param conditionalNG the ConditionalNG to be edited 076 */ 077 public ConditionalNGDebugger(@Nonnull ConditionalNG conditionalNG) { 078 079 _conditionalNG = conditionalNG; 080 081 _decorator = (FemaleSocket femaleSocket, JPanel panel) -> { 082 if (femaleSocket.isConnected()) { 083 MaleSocket maleSocket = femaleSocket.getConnectedSocket(); 084 AbstractDebuggerMaleSocket debugMaleSocket = 085 (AbstractDebuggerMaleSocket) maleSocket.find(AbstractDebuggerMaleSocket.class); 086 if (debugMaleSocket == null) throw new RuntimeException("AbstractDebuggerMaleSocket is not found"); 087 boolean breakpointBefore = debugMaleSocket.getBreakpointBefore(); 088 boolean breakpointAfter = debugMaleSocket.getBreakpointAfter(); 089 if (breakpointBefore || breakpointAfter) { 090 JPanel newPanel = new JPanel(); 091 newPanel.setBorder(BorderFactory.createMatteBorder( 092 breakpointBefore ? 5 : 1, 093 5, 094 breakpointAfter ? 5 : 1, 095 1, Color.red)); 096 newPanel.add(panel); 097 panel = newPanel; 098 } 099 } 100 if (_currentMaleSocket != null) { 101 Base parent = _currentMaleSocket.getParent(); 102 while ((parent != null) && (!(parent instanceof FemaleSocket))) { 103 parent = parent.getParent(); 104 } 105 if (parent == femaleSocket) { 106 JPanel newPanel = new JPanel(); 107 switch (_currentState) { 108 case Before: 109 newPanel.setBorder(BorderFactory.createMatteBorder(8, 5, 1, 1, Color.black)); 110 break; 111 case After: 112 newPanel.setBorder(BorderFactory.createMatteBorder(1, 5, 8, 1, Color.black)); 113 break; 114 default: 115 // Return without adding a border 116 return panel; 117 } 118 newPanel.add(panel); 119 return newPanel; 120 } 121 } 122 return panel; 123 }; 124 125 _conditionalNG_TreePane = new TreePane(conditionalNG.getFemaleSocket()); 126 _conditionalNG_TreePane.initComponents(_decorator); 127 128 _currentTreePanePanel = new JPanel(new CardLayout()); 129 130 _currentTreePane = _conditionalNG_TreePane; 131 _currentTreePanePanel.add(_conditionalNG.getSystemName(), _conditionalNG_TreePane); 132 _treePanes.add(_conditionalNG_TreePane); 133 134 // build menu 135 JMenuBar menuBar = new JMenuBar(); 136 137 JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile")); 138 JMenuItem closeWindowItem = new JMenuItem(Bundle.getMessage("CloseWindow")); 139 closeWindowItem.addActionListener((ActionEvent e) -> { 140 dispose(); 141 }); 142 fileMenu.add(closeWindowItem); 143 menuBar.add(fileMenu); 144 145 JMenu debugMenu = new JMenu(Bundle.getMessage("Debug_MenuDebug")); 146 147 _execute = new JMenuItem(Bundle.getMessage("Debug_MenuItem_Execute")); 148 _execute.addActionListener((event) -> { _conditionalNG.execute(); }); 149// _execute.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F7, ActionEvent.CTRL_MASK)); 150 debugMenu.add(_execute); 151 152 debugMenu.addSeparator(); 153 154 _runItem = new JMenuItem(Bundle.getMessage("Debug_MenuItem_Run")); 155 _runItem.addActionListener((event) -> { doRun(); }); 156 _runItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F7, ActionEvent.CTRL_MASK)); 157 _runItem.setEnabled(false); 158 debugMenu.add(_runItem); 159 160 _stepOverItem = new JMenuItem(Bundle.getMessage("Debug_MenuItem_StepOver")); 161 _stepOverItem.addActionListener((ActionEvent e) -> { doStepOver(); }); 162 _stepOverItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F7, ActionEvent.SHIFT_MASK)); 163 _stepOverItem.setEnabled(false); 164 debugMenu.add(_stepOverItem); 165 166 _stepIntoItem = new JMenuItem(Bundle.getMessage("Debug_MenuItem_StepInto")); 167 _stepIntoItem.addActionListener((ActionEvent e) -> { doStepInto(); }); 168 _stepIntoItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0)); 169 _stepIntoItem.setEnabled(false); 170 debugMenu.add(_stepIntoItem); 171 172 menuBar.add(debugMenu); 173 174 setJMenuBar(menuBar); 175// addHelpMenu("package.jmri.jmrit.operations.Operations_Settings", true); // NOI18N 176 177 setTitle((Module)null); 178 179 JPanel actionExpressionInfo = new JPanel(); 180 actionExpressionInfo.add(_actionExpressionInfoLabel); 181 JScrollPane actionExpressionInfoScrollPane = new JScrollPane(actionExpressionInfo); 182 actionExpressionInfoScrollPane.setPreferredSize(new Dimension(400, 200)); 183 184 185 JTable table = new JTable(); 186 _symbolTableModel = new DebuggerSymbolTableModel(); 187 table.setModel(_symbolTableModel); 188 JScrollPane variableScrollPane = new JScrollPane(table); 189 190 191 JSplitPane variableSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, 192 actionExpressionInfoScrollPane, variableScrollPane); 193 variableSplitPane.setOneTouchExpandable(true); 194 variableSplitPane.setDividerLocation(50); 195 196 197 JPanel watchPanel = new JPanel(); 198 JScrollPane watchScrollPane = new JScrollPane(watchPanel); 199 200 JSplitPane watchSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, 201 variableSplitPane, watchScrollPane); 202 watchSplitPane.setOneTouchExpandable(true); 203 watchSplitPane.setDividerLocation(150); 204 205 // Provide minimum sizes for the two components in the split pane 206 Dimension minimumWatchSize = new Dimension(100, 150); 207 variableScrollPane.setMinimumSize(minimumWatchSize); 208 watchScrollPane.setMinimumSize(minimumWatchSize); 209 210 JSplitPane mainSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, 211 _currentTreePanePanel, watchSplitPane); 212 mainSplitPane.setOneTouchExpandable(true); 213 mainSplitPane.setDividerLocation(150); 214 215 // Provide minimum sizes for the two components in the split pane 216 Dimension minimumMainSize = new Dimension(100, 50); 217 _currentTreePanePanel.setMinimumSize(minimumMainSize); 218 watchSplitPane.setMinimumSize(minimumMainSize); 219 220 // add panels 221 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 222 getContentPane().add(mainSplitPane); 223 224 initMinimumSize(new Dimension(panelWidth, panelHeight)); 225 226 _debugger.addPropertyChangeListener(this); 227 _debugger.setBreak(true); 228 _debugger.activateDebugger(conditionalNG); 229 230 PopupMenu popup = new PopupMenu(); 231 popup.init(); 232 } 233 234 private void setTitle(Module module) { 235 if (module != null) { 236 if (_conditionalNG.getUserName() == null) { 237 if (module.getUserName() == null) { 238 setTitle(Bundle.getMessage("TitleDebugConditionalNG_Module", 239 _conditionalNG.getSystemName(), 240 module.getSystemName())); 241 } else { 242 setTitle(Bundle.getMessage("TitleDebugConditionalNG_Module", 243 _conditionalNG.getSystemName(), 244 module.getSystemName(), module.getUserName())); 245 } 246 } else { 247 if (module.getUserName() == null) { 248 setTitle(Bundle.getMessage("TitleDebugConditionalNG2_Module", 249 _conditionalNG.getSystemName(), _conditionalNG.getUserName(), 250 module.getSystemName())); 251 } else { 252 setTitle(Bundle.getMessage("TitleDebugConditionalNG2_Module", 253 _conditionalNG.getSystemName(), _conditionalNG.getUserName(), 254 module.getSystemName(), module.getUserName())); 255 } 256 } 257 } else { 258 if (_conditionalNG.getUserName() == null) { 259 setTitle(Bundle.getMessage("TitleDebugConditionalNG", _conditionalNG.getSystemName())); 260 } else { 261 setTitle(Bundle.getMessage("TitleDebugConditionalNG2", _conditionalNG.getSystemName(), _conditionalNG.getUserName())); 262 } 263 } 264 } 265 266 private void doStepOver() { 267 AbstractDebuggerMaleSocket maleSocket = _currentMaleSocket; 268 if ((_currentState == State.After) && (_rootSocket == _currentMaleSocket)) { 269 _run = false; 270 _runItem.setEnabled(false); 271 } 272 _currentMaleSocket.setStepInto(false); 273 _currentMaleSocket = null; 274 _currentState = State.None; 275 _stepOverItem.setEnabled(false); 276 _stepIntoItem.setEnabled(false); 277 _currentTreePane.updateTree(maleSocket); 278 synchronized(_lock) { 279 _continue = true; 280 _lock.notify(); 281 } 282 } 283 284 private void doStepInto() { 285 AbstractDebuggerMaleSocket maleSocket = _currentMaleSocket; 286 if ((_currentState == State.After) && (_rootSocket == _currentMaleSocket)) { 287 _run = false; 288 _runItem.setEnabled(false); 289 } 290 _currentMaleSocket.setStepInto(true); 291 _currentMaleSocket = null; 292 _currentState = State.None; 293 _stepOverItem.setEnabled(false); 294 _stepIntoItem.setEnabled(false); 295 _currentTreePane.updateTree(maleSocket); 296 synchronized(_lock) { 297 _continue = true; 298 _lock.notify(); 299 } 300 } 301 302 private void doRun() { 303 _run = true; 304 305 if (_currentMaleSocket != null) { 306 AbstractDebuggerMaleSocket maleSocket = _currentMaleSocket; 307 if ((_currentState == State.After) && (_rootSocket == _currentMaleSocket)) { 308 _run = false; 309 _runItem.setEnabled(false); 310 } 311 _currentMaleSocket.setStepInto(false); 312 _currentMaleSocket = null; 313 _currentState = State.None; 314 _stepOverItem.setEnabled(false); 315 _stepIntoItem.setEnabled(false); 316 _currentTreePane.updateTree(maleSocket); 317 synchronized(_lock) { 318 _continue = true; 319 _lock.notify(); 320 } 321 } 322 } 323 324 public void initMinimumSize(Dimension dimension) { 325 setMinimumSize(dimension); 326 pack(); 327 setVisible(true); 328 } 329 330 /** {@inheritDoc} */ 331 @Override 332 public void windowClosed(WindowEvent e) { 333 doRun(); // Ensure the ConditionalNG is not waiting for us 334 _debugger.removePropertyChangeListener(this); 335 _debugger.deActivateDebugger(); 336 logixNGData.clear(); 337 logixNGData.put("Finish", _conditionalNG.getSystemName()); // NOI18N 338 fireLogixNGEvent(); 339 } 340 341 public void addLogixNGEventListener(ConditionalNGEventListener listener) { 342 listenerList.add(listener); 343 } 344 345 /** 346 * Notify the listeners to check for new data. 347 */ 348 void fireLogixNGEvent() { 349 for (ConditionalNGEventListener l : listenerList) { 350 l.conditionalNGEventOccurred(); 351 } 352 } 353 354 @Override 355 public void propertyChange(PropertyChangeEvent evt) { 356 if (Debugger.STEP_BEFORE.equals(evt.getPropertyName()) 357 || Debugger.STEP_AFTER.equals(evt.getPropertyName())) { 358 359 String infoString = ""; 360 _currentMaleSocket = (AbstractDebuggerMaleSocket) evt.getNewValue(); 361 if (_rootSocket == null) _rootSocket = _currentMaleSocket; 362 363// System.out.format("propertyChange: %s, %s, run: %b, currentState: %s, BP before: %b, BP after: %b%n", evt.getPropertyName(), ((MaleSocket)evt.getNewValue()).getLongDescription(), _run, _currentState.name(), _currentMaleSocket.getBreakpointBefore(), _currentMaleSocket.getBreakpointAfter()); 364// System.out.format("propertyChange: current: %s, root: %s%n", _currentMaleSocket, _rootSocket); 365 366 AtomicBoolean enableMenuItems = new AtomicBoolean(true); 367 368 369 Module module = _currentMaleSocket.getModule(); 370 371 // Have we either entered or exited a Module? 372 if (module != _currentModule) { 373 setTitle(module); 374 if ( !_lastModuleStack.isEmpty() && module == _lastModuleStack.peek()) { 375 _currentTreePanePanel.remove(_currentTreePane); 376 _treePanes.remove(_treePanes.size()-1); 377 _currentTreePane = _treePanes.get(_treePanes.size()-1); 378 if (module != null) { 379 ((CardLayout)_currentTreePanePanel.getLayout()) 380 .show(_currentTreePanePanel, module.getSystemName()); 381 } else { 382 ((CardLayout)_currentTreePanePanel.getLayout()) 383 .show(_currentTreePanePanel, _conditionalNG.getSystemName()); 384 } 385 _lastModuleStack.pop(); 386 } else { // Enter a module 387 if (module == null) throw new NullPointerException("module is null"); 388 _lastModuleStack.push(_currentModule); 389 _currentTreePane = new TreePane(module.getRootSocket()); 390 _currentTreePane.initComponents(_decorator); 391 _treePanes.add(_currentTreePane); 392 _currentTreePanePanel.add(module.getSystemName(), _currentTreePane); 393 ((CardLayout)_currentTreePanePanel.getLayout()) 394 .show(_currentTreePanePanel, module.getSystemName()); 395 } 396 _currentModule = module; 397 } 398 399 400 401 switch (evt.getPropertyName()) { 402 case Debugger.STEP_BEFORE: 403 if (!_run || _currentMaleSocket.getBreakpointBefore()) { 404 _currentState = State.Before; 405 } else { 406 _currentState = State.None; 407 } 408 infoString = _currentMaleSocket.getBeforeInfo(); 409 break; 410 case Debugger.STEP_AFTER: 411 if (!_run || _currentMaleSocket.getBreakpointAfter()) { 412 _currentState = State.After; 413 } else { 414 if (_rootSocket == _currentMaleSocket) { 415 _run = false; 416 jmri.util.ThreadingUtil.runOnGUIEventually(() -> { 417 _runItem.setEnabled(false); 418 _stepOverItem.setEnabled(false); 419 _stepIntoItem.setEnabled(false); 420 enableMenuItems.set(false); 421 }); 422 } 423 _currentState = State.None; 424 } 425 infoString = _currentMaleSocket.getAfterInfo(); 426 break; 427 default: 428 _currentState = State.None; 429 } 430 431 SymbolTable symbolTable = new DefaultSymbolTable(_conditionalNG.getSymbolTable()); 432 String infStr = infoString; 433 jmri.util.ThreadingUtil.runOnGUIEventually(() -> { 434 if (enableMenuItems.get()) { 435 _runItem.setEnabled(true); 436 _stepOverItem.setEnabled(true); 437 _stepIntoItem.setEnabled(true); 438 } 439 _actionExpressionInfoLabel.setText(infStr); 440 _currentTreePane.updateTree(_currentMaleSocket); 441 _symbolTableModel.update(symbolTable); 442 }); 443 444// System.out.format("propertyChange middle: %s, %s, run: %b, currentState: %s%n", evt.getPropertyName(), ((MaleSocket)evt.getNewValue()).getLongDescription(), _run, _currentState.name()); 445 446 if (_currentState != State.None) { 447 try { 448 synchronized(_lock) { 449 _continue = false; 450 while (!_continue) _lock.wait(); 451 } 452 } catch (InterruptedException e) { 453 log.error("LogixNG thread was interrupted: {}", _conditionalNG.getCurrentThread().getThreadName()); 454 Thread.currentThread().interrupt(); 455 } 456 } 457 458// System.out.format("propertyChange done: %s, %s, run: %b, currentState: %s%n", evt.getPropertyName(), ((MaleSocket)evt.getNewValue()).getLongDescription(), _run, _currentState.name()); 459 } 460 } 461 462 463 public interface ConditionalNGEventListener extends EventListener { 464 465 public void conditionalNGEventOccurred(); 466 } 467 468 469 private static enum State { 470 None, 471 Before, 472 After, 473 } 474 475 476 protected class PopupMenu extends JPopupMenu implements ActionListener { 477 478 private static final String ACTION_COMMAND_BREAKPOINT_BEFORE = "breakpoint_before"; 479 private static final String ACTION_COMMAND_BREAKPOINT_AFTER = "breakpoint_after"; 480// private static final String ACTION_COMMAND_EXPAND_TREE = "expandTree"; 481 482 private final JTree _tree; 483 private FemaleSocket _currentFemaleSocket; 484 private TreePath _currentPath; 485 486 private JMenuItem menuItemBreakpointBefore; 487 private JMenuItem menuItemBreakpointAfter; 488// private JMenuItem menuItemExpandTree; 489 490 PopupMenu() { 491 if (_currentTreePane._tree == null) throw new IllegalArgumentException("_tree is null"); 492 _tree = _currentTreePane._tree; 493 } 494 495 private void init() { 496 menuItemBreakpointBefore = new JMenuItem(Bundle.getMessage("PopupMenuBreakpointBefore")); 497 menuItemBreakpointBefore.addActionListener(this); 498 menuItemBreakpointBefore.setActionCommand(ACTION_COMMAND_BREAKPOINT_BEFORE); 499 add(menuItemBreakpointBefore); 500 addSeparator(); 501 menuItemBreakpointAfter = new JMenuItem(Bundle.getMessage("PopupMenuBreakpointAfter")); 502 menuItemBreakpointAfter.addActionListener(this); 503 menuItemBreakpointAfter.setActionCommand(ACTION_COMMAND_BREAKPOINT_AFTER); 504 add(menuItemBreakpointAfter); 505/* 506 addSeparator(); 507 menuItemExpandTree = new JMenuItem(Bundle.getMessage("PopupMenuExpandTree")); 508 menuItemExpandTree.addActionListener(this); 509 menuItemExpandTree.setActionCommand(ACTION_COMMAND_EXPAND_TREE); 510 add(menuItemExpandTree); 511*/ 512 setOpaque(true); 513 setLightWeightPopupEnabled(true); 514 515 final PopupMenu popupMenu = this; 516 517 _tree.addMouseListener( 518 new MouseAdapter() { 519 520 // On Windows, the popup is opened on mousePressed, 521 // on some other OS, the popup is opened on mouseReleased 522 523 @Override 524 public void mousePressed(MouseEvent e) { 525 openPopupMenu(e); 526 } 527 528 @Override 529 public void mouseReleased(MouseEvent e) { 530 openPopupMenu(e); 531 } 532 533 private void openPopupMenu(MouseEvent e) { 534 if (e.isPopupTrigger() && !popupMenu.isVisible()) { 535 // Get the row the user has clicked on 536 TreePath path = _tree.getClosestPathForLocation(e.getX(), e.getY()); 537 if (path != null) { 538 // Check that the user has clicked on a row. 539 Rectangle rect = _tree.getPathBounds(path); 540 if ((e.getY() >= rect.y) && (e.getY() <= rect.y + rect.height)) { 541 FemaleSocket femaleSocket = (FemaleSocket) path.getLastPathComponent(); 542 _tree.getLocationOnScreen(); 543 _tree.getX(); 544 showPopup(e.getX(), e.getY(), femaleSocket, path); 545 } 546 } 547 } 548 } 549 } 550 ); 551 } 552 553 private void showPopup(int x, int y, FemaleSocket femaleSocket, TreePath path) { 554 _currentFemaleSocket = femaleSocket; 555 _currentPath = path; 556 557 boolean isConnected = femaleSocket.isConnected(); 558 559 menuItemBreakpointBefore.setEnabled(isConnected); 560 menuItemBreakpointAfter.setEnabled(isConnected); 561 562 show(_tree, x, y); 563 } 564 565 @Override 566 public void actionPerformed(ActionEvent e) { 567 switch (e.getActionCommand()) { 568 case ACTION_COMMAND_BREAKPOINT_BEFORE: 569 MaleSocket maleSocket1 = _currentFemaleSocket.getConnectedSocket(); 570 AbstractDebuggerMaleSocket debugMaleSocket1 = 571 (AbstractDebuggerMaleSocket) maleSocket1.find(AbstractDebuggerMaleSocket.class); 572 if (debugMaleSocket1 == null) throw new RuntimeException("AbstractDebuggerMaleSocket is not found"); 573 // Invert breakpoint setting 574 debugMaleSocket1.setBreakpointBefore(!debugMaleSocket1.getBreakpointBefore()); 575 for (TreeModelListener l : _currentTreePane.femaleSocketTreeModel.listeners) { 576 TreeModelEvent tme = new TreeModelEvent( 577 _currentFemaleSocket, 578 _currentPath.getPath() 579 ); 580 l.treeNodesChanged(tme); 581 } 582 _currentTreePane._tree.updateUI(); 583 break; 584 585 case ACTION_COMMAND_BREAKPOINT_AFTER: 586 MaleSocket maleSocket2 = _currentFemaleSocket.getConnectedSocket(); 587 AbstractDebuggerMaleSocket debugMaleSocket2 = 588 (AbstractDebuggerMaleSocket) maleSocket2.find(AbstractDebuggerMaleSocket.class); 589 if (debugMaleSocket2 == null) throw new RuntimeException("AbstractDebuggerMaleSocket is not found"); 590 // Invert breakpoint setting 591 debugMaleSocket2.setBreakpointAfter(!debugMaleSocket2.getBreakpointAfter()); 592 for (TreeModelListener l : _currentTreePane.femaleSocketTreeModel.listeners) { 593 TreeModelEvent tme = new TreeModelEvent( 594 _currentFemaleSocket, 595 _currentPath.getPath() 596 ); 597 l.treeNodesChanged(tme); 598 } 599 _currentTreePane._tree.updateUI(); 600 break; 601/* 602 case ACTION_COMMAND_EXPAND_TREE: 603 // jtree expand sub tree 604 // https://stackoverflow.com/questions/15210979/how-do-i-auto-expand-a-jtree-when-setting-a-new-treemodel 605 // https://www.tutorialspoint.com/how-to-expand-jtree-row-to-display-all-the-nodes-and-child-nodes-in-java 606 // To expand all rows, do this: 607 for (int i = 0; i < tree.getRowCount(); i++) { 608 tree.expandRow(i); 609 } 610 611 tree.expandPath(_currentPath); 612 tree.updateUI(); 613 break; 614*/ 615 default: 616 // Do nothing 617 } 618 } 619 } 620 621 622 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConditionalNGDebugger.class); 623 624}