|
1 | 1 | package cuchaz.enigma.gui.panels; |
2 | 2 |
|
| 3 | +import java.awt.AWTKeyStroke; |
3 | 4 | import java.awt.Color; |
4 | 5 | import java.awt.Component; |
5 | 6 | import java.awt.Font; |
6 | 7 | import java.awt.GridBagConstraints; |
7 | 8 | import java.awt.GridBagLayout; |
8 | 9 | import java.awt.GridLayout; |
| 10 | +import java.awt.KeyboardFocusManager; |
9 | 11 | import java.awt.Rectangle; |
10 | 12 | import java.awt.event.ActionEvent; |
11 | 13 | import java.awt.event.ActionListener; |
|
16 | 18 | import java.util.ArrayList; |
17 | 19 | import java.util.Collection; |
18 | 20 | import java.util.Comparator; |
| 21 | +import java.util.HashSet; |
19 | 22 | import java.util.List; |
20 | 23 | import java.util.Map; |
| 24 | +import java.util.NavigableSet; |
| 25 | +import java.util.Set; |
21 | 26 |
|
22 | 27 | import javax.swing.JButton; |
23 | 28 | import javax.swing.JComponent; |
@@ -120,6 +125,11 @@ public EditorPanel(Gui gui) { |
120 | 125 | customizeEditor(this.editor); |
121 | 126 | this.editor.addCaretListener(event -> onCaretMove(event.getDot(), this.mouseIsPressed)); |
122 | 127 |
|
| 128 | + // Remove the tab key from focus traversal keys (we give it a different meaning) |
| 129 | + Set<AWTKeyStroke> focusTraversalKeys = new HashSet<>(this.editor.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)); |
| 130 | + focusTraversalKeys.removeIf(key -> key.getKeyCode() == KeyEvent.VK_TAB); |
| 131 | + this.editor.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, focusTraversalKeys); |
| 132 | + |
123 | 133 | // set unit increment to height of one line, the amount scrolled per |
124 | 134 | // mouse wheel rotation is then controlled by OS settings |
125 | 135 | this.editorScrollPane.getVerticalScrollBar().setUnitIncrement(this.editor.getFontMetrics(this.editor.getFont()).getHeight()); |
@@ -199,6 +209,9 @@ public void keyPressed(KeyEvent event) { |
199 | 209 | EditorPanel.this.shouldNavigateOnClick = true; // CTRL |
200 | 210 | break; |
201 | 211 | } |
| 212 | + } else if (event.getKeyCode() == KeyEvent.VK_TAB) { |
| 213 | + EditorPanel.this.navigateToNextObfuscatedToken(); |
| 214 | + event.consume(); |
202 | 215 | } |
203 | 216 | } |
204 | 217 |
|
@@ -668,6 +681,36 @@ private void showReference0(EntryReference<Entry<?>, Entry<?>> reference) { |
668 | 681 | } |
669 | 682 | } |
670 | 683 |
|
| 684 | + /** |
| 685 | + * Navigate to the next obfuscated token that can be renamed. |
| 686 | + * |
| 687 | + * <p>If the tokens are damaged, then this method should not be called |
| 688 | + * synchronously. Instead, the call should be wrapped in a |
| 689 | + * {@link SwingUtilities#invokeLater(Runnable)}. Failing to do so |
| 690 | + * will induce invalid token highlighting regions. |
| 691 | + */ |
| 692 | + public void navigateToNextObfuscatedToken() { |
| 693 | + int caretPos = this.getEditor().getCaretPosition(); |
| 694 | + Token token = this.getToken(this.getEditor().getCaretPosition()); |
| 695 | + NavigableSet<Token> obfuscatedTokens = this.getSource().getTokenStore().getByType().get(RenamableTokenType.OBFUSCATED); |
| 696 | + Token next; |
| 697 | + |
| 698 | + if (token == null) { |
| 699 | + next = obfuscatedTokens.higher(new Token(caretPos, caretPos, null)); |
| 700 | + } else { |
| 701 | + next = obfuscatedTokens.higher(token); |
| 702 | + } |
| 703 | + |
| 704 | + if (next == null) { |
| 705 | + // Wrap to start of document |
| 706 | + next = obfuscatedTokens.pollFirst(); |
| 707 | + } |
| 708 | + |
| 709 | + if (next != null) { |
| 710 | + this.navigateToToken(next); |
| 711 | + } |
| 712 | + } |
| 713 | + |
671 | 714 | public void navigateToToken(Token token) { |
672 | 715 | if (token == null) { |
673 | 716 | throw new IllegalArgumentException("Token cannot be null!"); |
|
0 commit comments