View Javadoc
1   package org.opentrafficsim.editor;
2   
3   import java.awt.event.ActionEvent;
4   import java.util.LinkedList;
5   
6   import javax.swing.AbstractAction;
7   import javax.swing.Action;
8   import javax.swing.KeyStroke;
9   
10  /**
11   * Navigation through coupled nodes.
12   * <p>
13   * Copyright (c) 2026-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * </p>
16   * @author Wouter Schakel
17   */
18  public class Navigation
19  {
20  
21      /** OTS editor. */
22      private final OtsEditor editor;
23  
24      /** Maximum number of navigation steps in the list. */
25      private final int maxNavigate;
26  
27      /** Candidate keyref node that was coupled from to a key node, may be {@code null}. */
28      private XsdTreeNode candidateBackNode;
29  
30      /** Keyref node that was coupled from to a key node, may be {@code null}. */
31      private final LinkedList<XsdTreeNode> backNode = new LinkedList<>();
32  
33      /** Candidate attribute of back node referring to coupled node, may be {@code null}. */
34      private String candidateBackAttribute;
35  
36      /** Attribute of back node referring to coupled node, may be {@code null}. */
37      private final LinkedList<String> backAttribute = new LinkedList<>();
38  
39      /** Key node that is coupled to from a keyref node, may be {@code null}. */
40      private XsdTreeNode coupledNode;
41  
42      /**
43       * Constructor.
44       * @param editor editor
45       * @param maxNavigate maximum number of navigation steps
46       */
47      public Navigation(final OtsEditor editor, final int maxNavigate)
48      {
49          this.editor = editor;
50          this.maxNavigate = maxNavigate;
51          clear();
52      }
53  
54      /**
55       * Clear navigation list.
56       */
57      public void clear()
58      {
59          this.goBackAction.setEnabled(false);
60          this.goBackAction.putValue(Action.NAME, "Go back");
61          this.goToCoupledNodeAction.setEnabled(false);
62          this.goToCoupledNodeAction.putValue(Action.NAME, "Go to coupled item");
63      }
64  
65      /**
66       * Sets coupled node from user action, i.e. the node that contains the key value to which a user selected node with keyref
67       * refers to.
68       * @param toNode key node that is coupled to from a keyref node, may be {@code null}.
69       * @param fromNode keyref node that is coupled from to a key node, may be {@code null}.
70       * @param fromAttribute attribute in keyref node that refers to coupled node, may be {@code null}.
71       */
72      public void setCoupledNode(final XsdTreeNode toNode, final XsdTreeNode fromNode, final String fromAttribute)
73      {
74          if (toNode == null)
75          {
76              this.goToCoupledNodeAction.setEnabled(false);
77              this.goToCoupledNodeAction.putValue(Action.NAME, "Go to coupled item");
78          }
79          else
80          {
81              this.goToCoupledNodeAction.setEnabled(true);
82              this.goToCoupledNodeAction.putValue(Action.NAME,
83                      "Go to " + (fromAttribute != null ? fromNode.getAttributeValueOrDefault(fromAttribute)
84                              : (fromNode.isIdentifiable() ? fromNode.getId() : fromNode.getValueOrDefault())));
85          }
86          this.coupledNode = toNode;
87          this.candidateBackNode = fromNode;
88          this.candidateBackAttribute = fromAttribute;
89      }
90  
91      /**
92       * Returns the go-back action.
93       * @return go-back action
94       */
95      public Action getGoBackAction()
96      {
97          return this.goBackAction;
98      }
99  
100     /** Go-back action. */
101     private final Action goBackAction = new AbstractAction()
102     {
103         {
104             putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("pressed F3"));
105         }
106 
107         @Override
108         public void actionPerformed(final ActionEvent e)
109         {
110             Navigation.this.editor.show(Navigation.this.backNode.pollLast(), Navigation.this.backAttribute.pollLast());
111             if (Navigation.this.backNode.isEmpty())
112             {
113                 putValue(NAME, "Go back");
114                 setEnabled(false);
115             }
116             else
117             {
118                 XsdTreeNode back = Navigation.this.backNode.peekLast();
119                 Navigation.this.goBackAction.putValue(NAME,
120                         "Go back to " + back.getNodeName() + (back.isIdentifiable() ? " " + back.getId() : ""));
121                 Navigation.this.goBackAction.setEnabled(true);
122             }
123         }
124     };
125 
126     /**
127      * Returns go-to-coupled-item action.
128      * @return go-to-coupled-item action
129      */
130     public Action getGotoCoupledNodeAction()
131     {
132         return this.goToCoupledNodeAction;
133     }
134 
135     /** Go-to-coupled-item action. */
136     private final Action goToCoupledNodeAction = new AbstractAction()
137     {
138         {
139             putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("pressed F4"));
140         }
141 
142         @Override
143         public void actionPerformed(final ActionEvent e)
144         {
145             if (Navigation.this.coupledNode != null)
146             {
147                 Navigation.this.backNode.add(Navigation.this.candidateBackNode);
148                 Navigation.this.backAttribute.add(Navigation.this.candidateBackAttribute);
149                 while (Navigation.this.backNode.size() > Navigation.this.maxNavigate)
150                 {
151                     Navigation.this.backNode.remove();
152                     Navigation.this.backAttribute.remove();
153                 }
154                 XsdTreeNode back = Navigation.this.backNode.peekLast();
155                 Navigation.this.goBackAction.putValue(NAME,
156                         "Go back to " + back.getNodeName() + (back.isIdentifiable() ? " " + back.getId() : ""));
157                 Navigation.this.goBackAction.setEnabled(Navigation.this.backNode.peekLast() != null);
158                 Navigation.this.editor.show(Navigation.this.coupledNode, null);
159             }
160         }
161     };
162 
163 }