1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 package ffx.ui;
39
40 import ffx.potential.bonded.MSNode;
41 import ffx.potential.bonded.MSRoot;
42 import ffx.potential.bonded.ROLSP;
43 import ffx.potential.bonded.RendererCache;
44 import java.awt.Color;
45 import java.awt.GraphicsEnvironment;
46 import java.io.Serial;
47 import java.util.ArrayList;
48 import java.util.Enumeration;
49 import java.util.logging.Logger;
50 import javax.swing.JLabel;
51 import javax.swing.JTree;
52 import javax.swing.SwingUtilities;
53 import javax.swing.event.TreeModelEvent;
54 import javax.swing.event.TreeModelListener;
55 import javax.swing.event.TreeSelectionEvent;
56 import javax.swing.event.TreeSelectionListener;
57 import javax.swing.tree.DefaultTreeCellRenderer;
58 import javax.swing.tree.DefaultTreeModel;
59 import javax.swing.tree.DefaultTreeSelectionModel;
60 import javax.swing.tree.RowMapper;
61 import javax.swing.tree.TreeNode;
62 import javax.swing.tree.TreePath;
63
64
65
66
67
68
69
70 public final class Hierarchy extends JTree implements TreeSelectionListener, TreeModelListener {
71
72 @Serial
73 private static final long serialVersionUID = 1L;
74
75 private static final Logger logger = Logger.getLogger(Hierarchy.class.getName());
76 private final MSRoot root;
77 private final MainPanel mainPanel;
78 private final ArrayList<MSNode> activeNodes = new ArrayList<>();
79 private DefaultTreeModel hierarchyModel;
80 private DefaultTreeCellRenderer treeCellRenderer;
81 private DefaultTreeSelectionModel treeSelectionModel;
82
83 private FFXSystem activeSystem = null;
84
85 private MSNode activeNode = null;
86 private JLabel status = null;
87 private JLabel step = null;
88 private JLabel energy = null;
89 private ArrayList<TreePath> newPaths = new ArrayList<>();
90 private ArrayList<TreePath> previousPaths = new ArrayList<>();
91 private final ArrayList<TreePath> removedPaths = new ArrayList<>();
92 private final TreePath nullPath = null;
93
94
95
96
97
98
99
100 Hierarchy(MainPanel mainPanel) {
101 super(mainPanel.getDataRoot());
102 this.mainPanel = mainPanel;
103 root = this.mainPanel.getDataRoot();
104 initTree();
105 }
106
107
108
109
110
111
112 public void addSelections(ArrayList<MSNode> a) {
113 synchronized (this) {
114 for (MSNode f : a) {
115 addSelection(f);
116 }
117 }
118 }
119
120
121
122
123
124
125 public FFXSystem getActive() {
126 return activeSystem;
127 }
128
129
130
131
132
133
134 public void setActive(FFXSystem ffxSystem) {
135 synchronized (this) {
136 if (ffxSystem == activeSystem) {
137 return;
138 }
139 if (ffxSystem != null) {
140
141 if (ffxSystem.isClosing()) {
142
143 for (int i = root.getChildCount() - 1; i >= 0; i--) {
144 FFXSystem child = (FFXSystem) root.getChildAt(i);
145 if (!child.isClosing()) {
146 setActive(child);
147 return;
148 }
149 }
150 setActive(null);
151 return;
152 }
153 }
154 activeSystem = ffxSystem;
155 updateStatus();
156 if (mainPanel.getKeywordPanel() != null) {
157 mainPanel.getKeywordPanel().loadActive(activeSystem);
158 }
159 if (mainPanel.getModelingPanel() != null) {
160 mainPanel.getModelingPanel().loadActive(activeSystem);
161 }
162 if (mainPanel.getModelingShell() != null) {
163 mainPanel.getModelingShell().sync();
164 }
165 }
166 }
167
168
169
170
171
172
173 public void setActive(int i) {
174 synchronized (this) {
175 if (i < root.getChildCount()) {
176 setActive((FFXSystem) root.getChildAt(i));
177 } else if (root.getChildCount() == 0) {
178 setActive(null);
179 }
180 }
181 }
182
183
184
185
186
187
188 public MSNode getActiveNode() {
189 return activeNode;
190 }
191
192
193
194
195
196
197 public FFXSystem[] getSystems() {
198 synchronized (this) {
199 int childCount = root.getChildCount();
200 if (childCount == 0) {
201 return null;
202 }
203 FFXSystem[] systems = new FFXSystem[childCount];
204 int index = 0;
205 for (Enumeration<TreeNode> e = root.children(); e.hasMoreElements(); ) {
206 systems[index++] = (FFXSystem) e.nextElement();
207 }
208 return systems;
209 }
210 }
211
212
213
214
215
216
217
218 public void groupSelection(MSNode f1, MSNode f2) {
219 if (f1 == null || f2 == null) {
220 return;
221 }
222 synchronized (this) {
223 TreePath[] paths = new TreePath[2];
224 paths[0] = new TreePath(f1.getPath());
225 paths[1] = new TreePath(f2.getPath());
226 RowMapper rm = treeSelectionModel.getRowMapper();
227 int[] rows = rm.getRowsForPaths(paths);
228 setSelectionInterval(rows[0], rows[1]);
229 }
230 }
231
232
233
234
235
236
237 public void removeSelections(ArrayList<MSNode> a) {
238 synchronized (this) {
239 for (MSNode f : a) {
240 removeSelection(f);
241 }
242 }
243 }
244
245
246 @Override
247 public String toString() {
248 return "Structural Hierarchy";
249 }
250
251
252
253
254
255
256 public void toggleSelections(ArrayList<MSNode> a) {
257 synchronized (this) {
258 for (MSNode f : a) {
259 toggleSelection(f);
260 }
261 }
262 }
263
264
265 @Override
266 public void valueChanged(TreeSelectionEvent e) {
267 synchronized (this) {
268
269
270 MSNode lastNode = (MSNode) getLastSelectedPathComponent();
271 if (lastNode != null) {
272 activeNode = lastNode;
273 FFXSystem s = activeNode.getMSNode(FFXSystem.class);
274 if (s != null) {
275 setActive(s);
276 }
277 }
278 TreePath[] paths = e.getPaths();
279 if (paths == null) {
280 return;
281 }
282
283
284 ArrayList<TreePath> temp = previousPaths;
285 previousPaths = newPaths;
286 newPaths = temp;
287
288 newPaths.clear();
289 removedPaths.clear();
290 for (int i = 0; i < paths.length; i++) {
291 if (e.isAddedPath(i)) {
292 newPaths.add(paths[i]);
293 } else {
294 removedPaths.add(paths[i]);
295 }
296 }
297
298 TreePath pathi, pathj;
299 for (int i = 0; i < newPaths.size(); i++) {
300 pathi = newPaths.get(i);
301 if (pathi == nullPath) {
302 continue;
303 }
304 for (int j = i + 1; j < newPaths.size(); j++) {
305 pathj = newPaths.get(j);
306 if (pathi == nullPath || pathj == nullPath) {
307 continue;
308 }
309 if (pathi.isDescendant(pathj)) {
310 newPaths.set(j, nullPath);
311 } else if (pathj.isDescendant(pathi)) {
312 newPaths.set(i, nullPath);
313 }
314 }
315 }
316 boolean check = true;
317 while (check) {
318 check = newPaths.remove(nullPath);
319 }
320 for (int i = 0; i < removedPaths.size(); i++) {
321 pathi = removedPaths.get(i);
322 if (pathi == nullPath) {
323 continue;
324 }
325 for (int j = i + 1; j < removedPaths.size(); j++) {
326 pathj = removedPaths.get(j);
327 if (pathi == nullPath || pathj == nullPath) {
328 continue;
329 }
330 if (pathi.isDescendant(pathj)) {
331 removedPaths.set(j, nullPath);
332 } else if (pathj.isDescendant(pathi)) {
333 removedPaths.set(i, nullPath);
334 }
335 }
336 }
337 check = true;
338 while (check) {
339 check = removedPaths.remove(nullPath);
340 }
341
342 for (int i = 0; i < previousPaths.size(); i++) {
343 pathi = previousPaths.get(i);
344 for (TreePath removedPath : removedPaths) {
345 if (removedPath.isDescendant(pathi)) {
346 previousPaths.set(i, nullPath);
347 break;
348 }
349 }
350 }
351 check = true;
352 while (check) {
353 check = previousPaths.remove(nullPath);
354 }
355
356 for (int i = 0; i < newPaths.size(); i++) {
357 pathi = newPaths.get(i);
358 if (pathi == nullPath) {
359 continue;
360 }
361 for (int j = 0; j < previousPaths.size(); j++) {
362 pathj = previousPaths.get(j);
363 if (pathj == nullPath) {
364 continue;
365 }
366 if (pathi == nullPath) {
367 continue;
368 }
369 if (pathi.isDescendant(pathj)) {
370 previousPaths.set(j, nullPath);
371 } else if (pathj.isDescendant(pathi)) {
372 newPaths.set(i, nullPath);
373 }
374 }
375 }
376 check = true;
377 while (check) {
378 check = newPaths.remove(nullPath);
379 }
380 check = true;
381 while (check) {
382 check = previousPaths.remove(nullPath);
383 }
384 newPaths.addAll(previousPaths);
385 activeNodes.clear();
386 for (TreePath newPath : newPaths) {
387 pathi = newPath;
388 activeNodes.add((MSNode) pathi.getLastPathComponent());
389 }
390 if (activeNode != null) {
391 TreePath activePath = new TreePath(activeNode);
392 expandPath(activePath.getParentPath());
393 makeVisible(activePath);
394 scrollPathToVisible(activePath);
395 }
396
397
398 ArrayList<MSNode> picks = new ArrayList<>();
399
400 for (TreePath r : removedPaths) {
401 boolean change = true;
402 for (TreePath n : newPaths) {
403 if (n.isDescendant(r)) {
404 change = false;
405 }
406 }
407 if (change) {
408 MSNode f = (MSNode) r.getLastPathComponent();
409 f.setSelected(false);
410 picks.add(f);
411 }
412 }
413 for (TreePath n : newPaths) {
414 boolean change = true;
415 for (TreePath p : previousPaths) {
416 if (p.isDescendant(n)) {
417 change = false;
418 }
419 }
420 if (change) {
421 MSNode f = (MSNode) n.getLastPathComponent();
422 f.setSelected(true);
423 picks.add(f);
424 }
425 }
426 if (RendererCache.highlightSelections) {
427 mainPanel
428 .getGraphics3D()
429 .updateScene(picks, false, false, null, true, RendererCache.ColorModel.SELECT);
430 } else if (RendererCache.labelAtoms || RendererCache.labelResidues) {
431 mainPanel.getGraphics3D().setLabelsUpdated();
432 }
433 }
434 }
435
436
437
438
439
440
441 private void addSelection(MSNode f) {
442 if (f == null) {
443 return;
444 }
445 synchronized (this) {
446 TreePath path = new TreePath(f.getPath());
447 addSelectionPath(path);
448 f.setSelected(true);
449 }
450 }
451
452
453
454
455
456
457 void addSystemNode(FFXSystem newSystem) {
458 addTreeNode(newSystem, root, root.getChildCount());
459 }
460
461
462
463
464
465
466
467
468 private void addTreeNode(MSNode nodeToAdd, MSNode parent, int index) {
469 synchronized (this) {
470 if (nodeToAdd == null || nodeToAdd.getParent() != null) {
471 return;
472 }
473 int childCount = parent.getChildCount();
474 if (index < 0 || index > childCount) {
475 index = parent.getChildCount();
476 }
477
478 String name = nodeToAdd.getName();
479
480 for (int i = 0; i < parent.getChildCount(); i++) {
481 MSNode node = (MSNode) parent.getChildAt(i);
482 if (node.getName().equals(name)) {
483 logger.fine(" Parent already has a node with the name " + name);
484 }
485 }
486
487
488 if (ROLSP.GO_PARALLEL) {
489 ROLSP parallelNode = new ROLSP();
490 parallelNode.add(nodeToAdd);
491 hierarchyModel.insertNodeInto(parallelNode, parent, index);
492 } else {
493 hierarchyModel.insertNodeInto(nodeToAdd, parent, index);
494 }
495 if (nodeToAdd instanceof FFXSystem) {
496 attach((FFXSystem) nodeToAdd);
497 hierarchyModel.nodeStructureChanged(nodeToAdd);
498 }
499 onlySelection(nodeToAdd);
500 if (!isRootVisible()) {
501 setRootVisible(true);
502 }
503 }
504 }
505
506 private void attach(FFXSystem newModel) {
507 if (newModel == null) {
508 return;
509 }
510 newModel.finalize(true, newModel.getForceField());
511 GraphicsCanvas graphics = mainPanel.getGraphics3D();
512 if (graphics != null) {
513 graphics.attachModel(newModel);
514 if (newModel.getBondList().isEmpty()) {
515 mainPanel
516 .getGraphics3D()
517 .updateScene(newModel, false, true, RendererCache.ViewModel.SPACEFILL, false, null);
518 }
519 }
520 }
521
522
523 private void collapseAll() {
524 int row = getRowCount() - 1;
525 while (row >= 0) {
526 collapseRow(row);
527 row--;
528 }
529 }
530
531
532
533
534
535
536 ArrayList<MSNode> getActiveNodes() {
537 return activeNodes;
538 }
539
540
541
542
543
544
545 FFXSystem[] getNonActiveSystems() {
546 synchronized (this) {
547 int childCount = root.getChildCount();
548 if (childCount == 0) {
549 return null;
550 }
551 FFXSystem[] systems = new FFXSystem[childCount - 1];
552 int index = 0;
553 for (Enumeration<TreeNode> e = root.children(); e.hasMoreElements(); ) {
554 FFXSystem system = (FFXSystem) e.nextElement();
555 if (system != getActive()) {
556 systems[index++] = system;
557 }
558 }
559 return systems;
560 }
561 }
562
563
564 private void initTree() {
565 if (!GraphicsEnvironment.isHeadless() && !SwingUtilities.isEventDispatchThread()) {
566 logger.severe(" Initialization of Hierarchy outside EDT.");
567 }
568 addTreeSelectionListener(this);
569 setExpandsSelectedPaths(true);
570 setScrollsOnExpand(true);
571
572 setEditable(false);
573 putClientProperty("JTree.lineStyle", "Angled");
574 setShowsRootHandles(true);
575 treeCellRenderer = new DefaultTreeCellRenderer();
576 treeCellRenderer.setBackgroundSelectionColor(Color.yellow);
577 treeCellRenderer.setBorderSelectionColor(Color.black);
578 treeCellRenderer.setTextSelectionColor(Color.black);
579 setCellRenderer(treeCellRenderer);
580 hierarchyModel = new DefaultTreeModel(root);
581 root.setUserObject(hierarchyModel);
582 setModel(hierarchyModel);
583 hierarchyModel.addTreeModelListener(this);
584
585 treeSelectionModel = new DefaultTreeSelectionModel();
586 setSelectionModel(treeSelectionModel);
587 setRootVisible(false);
588 }
589
590
591
592
593
594
595 void onlySelection(MSNode f) {
596 synchronized (this) {
597 int num = activeNodes.size();
598 TreePath[] paths = new TreePath[num];
599 for (int i = 0; i < num; i++) {
600 paths[i] = new TreePath((activeNodes.get(i)).getPath());
601 }
602 removeSelectionPaths(paths);
603 collapseAll();
604 addSelection(f);
605 }
606 }
607
608
609
610
611
612
613 private void removeSelection(MSNode f) {
614 synchronized (this) {
615 if (f == null) {
616 return;
617 }
618 TreePath path = new TreePath(f.getPath());
619 for (Enumeration<TreePath> e = getExpandedDescendants(path); e.hasMoreElements(); ) {
620 TreePath treePath = new TreePath(e.nextElement());
621 collapsePath(treePath);
622 }
623 removeSelectionPath(path);
624 f.setSelected(false);
625 }
626 }
627
628
629
630
631
632
633 void removeTreeNode(MSNode nodeToRemove) {
634 synchronized (this) {
635 if (nodeToRemove == null) {
636 return;
637 }
638
639 hierarchyModel.removeNodeFromParent(nodeToRemove);
640
641
642
643
644
645
646 if (nodeToRemove instanceof FFXSystem) {
647 hierarchyModel = new DefaultTreeModel(root);
648
649 root.setUserObject(hierarchyModel);
650 setModel(hierarchyModel);
651 hierarchyModel.addTreeModelListener(this);
652 treeSelectionModel = new DefaultTreeSelectionModel();
653 setSelectionModel(treeSelectionModel);
654 }
655
656
657 activeNodes.clear();
658 previousPaths.clear();
659 newPaths.clear();
660 removedPaths.clear();
661
662 if (getActive() == nodeToRemove && root.getChildCount() != 0) {
663 FFXSystem m = (FFXSystem) root.getChildAt(0);
664 setActive(m);
665 onlySelection(activeSystem);
666 } else {
667 setActive(null);
668 }
669
670 if (root.getChildCount() <= 1) {
671 setRootVisible(false);
672 }
673 }
674 }
675
676
677 void selectAll() {
678 synchronized (this) {
679 if (activeSystem == null) {
680 return;
681 }
682 onlySelection(root);
683 }
684 }
685
686
687
688
689
690
691 void setHighlighting(boolean h) {
692 synchronized (this) {
693 if (RendererCache.highlightSelections != h) {
694 RendererCache.highlightSelections = h;
695 for (MSNode node : activeNodes) {
696 node.setSelected(h);
697 }
698 mainPanel
699 .getGraphics3D()
700 .updateScene(activeNodes, false, false, null, true, RendererCache.ColorModel.SELECT);
701 }
702 }
703 }
704
705
706
707
708
709
710
711
712 void setStatus(JLabel s, JLabel t, JLabel e) {
713 status = s;
714 step = t;
715 energy = e;
716 }
717
718
719
720
721
722
723 void toggleSelection(MSNode f) {
724 synchronized (this) {
725 if (f == null) {
726 return;
727 }
728 TreePath path = new TreePath(f.getPath());
729 if (isPathSelected(path)) {
730 removeSelectionPath(path);
731 } else {
732 addSelectionPath(path);
733 }
734 }
735 }
736
737
738 void updateStatus() {
739 if (activeSystem == null) {
740 status.setText(" ");
741 step.setText(" ");
742 energy.setText(" ");
743 return;
744 }
745 if (activeSystem.getFile() != null) {
746 status.setText(" " + activeSystem.toFFString());
747 } else {
748 status.setText(" " + activeSystem.toString());
749 }
750
751 if (activeSystem.getCycles() > 1) {
752 step.setText(activeSystem.getCurrentCycle() + "/" + activeSystem.getCycles());
753 } else {
754 step.setText("");
755 }
756
757 if (activeSystem.getCycles() > 1) {
758 energy.setText("");
759 } else {
760 energy.setText("");
761 }
762 }
763
764 @Override
765 public void treeNodesChanged(TreeModelEvent e) {
766 }
767
768 @Override
769 public void treeNodesInserted(TreeModelEvent e) {
770 }
771
772 @Override
773 public void treeNodesRemoved(TreeModelEvent e) {
774 }
775
776 @Override
777 public void treeStructureChanged(TreeModelEvent e) {
778 }
779 }