Skip to content

Commit 7cc1908

Browse files
authored
Allow use of tab in editor panel, fix tab skipping tokens (#578)
* Gracefully fall back when lang/index.txt is absent * Allow use of tab in editor panel, fix tab skipping tokens * Document #navigateToNextObfuscatedToken async requirements
1 parent 7ac19e2 commit 7cc1908

File tree

3 files changed

+53
-24
lines changed

3 files changed

+53
-24
lines changed

enigma-swing/src/main/java/cuchaz/enigma/gui/panels/EditorPanel.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package cuchaz.enigma.gui.panels;
22

3+
import java.awt.AWTKeyStroke;
34
import java.awt.Color;
45
import java.awt.Component;
56
import java.awt.Font;
67
import java.awt.GridBagConstraints;
78
import java.awt.GridBagLayout;
89
import java.awt.GridLayout;
10+
import java.awt.KeyboardFocusManager;
911
import java.awt.Rectangle;
1012
import java.awt.event.ActionEvent;
1113
import java.awt.event.ActionListener;
@@ -16,8 +18,11 @@
1618
import java.util.ArrayList;
1719
import java.util.Collection;
1820
import java.util.Comparator;
21+
import java.util.HashSet;
1922
import java.util.List;
2023
import java.util.Map;
24+
import java.util.NavigableSet;
25+
import java.util.Set;
2126

2227
import javax.swing.JButton;
2328
import javax.swing.JComponent;
@@ -120,6 +125,11 @@ public EditorPanel(Gui gui) {
120125
customizeEditor(this.editor);
121126
this.editor.addCaretListener(event -> onCaretMove(event.getDot(), this.mouseIsPressed));
122127

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+
123133
// set unit increment to height of one line, the amount scrolled per
124134
// mouse wheel rotation is then controlled by OS settings
125135
this.editorScrollPane.getVerticalScrollBar().setUnitIncrement(this.editor.getFontMetrics(this.editor.getFont()).getHeight());
@@ -199,6 +209,9 @@ public void keyPressed(KeyEvent event) {
199209
EditorPanel.this.shouldNavigateOnClick = true; // CTRL
200210
break;
201211
}
212+
} else if (event.getKeyCode() == KeyEvent.VK_TAB) {
213+
EditorPanel.this.navigateToNextObfuscatedToken();
214+
event.consume();
202215
}
203216
}
204217

@@ -668,6 +681,36 @@ private void showReference0(EntryReference<Entry<?>, Entry<?>> reference) {
668681
}
669682
}
670683

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+
671714
public void navigateToToken(Token token) {
672715
if (token == null) {
673716
throw new IllegalArgumentException("Token cannot be null!");

enigma-swing/src/main/java/cuchaz/enigma/gui/panels/IdentifierPanel.java

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
import cuchaz.enigma.gui.util.GridBagConstraintsBuilder;
2424
import cuchaz.enigma.gui.util.GuiUtil;
2525
import cuchaz.enigma.gui.util.ScaleUtil;
26-
import cuchaz.enigma.source.RenamableTokenType;
27-
import cuchaz.enigma.source.Token;
2826
import cuchaz.enigma.translation.mapping.AccessModifier;
2927
import cuchaz.enigma.translation.mapping.EntryChange;
3028
import cuchaz.enigma.translation.mapping.EntryMapping;
@@ -197,36 +195,19 @@ public boolean tryStopEditing(ConvertingTextField field, StopEditingCause cause)
197195

198196
@Override
199197
public void onStopEditing(ConvertingTextField field, StopEditingCause cause) {
198+
EditorPanel e = gui.getActiveEditor();
199+
200200
if (cause != StopEditingCause.ABORT) {
201201
vc.reset();
202202
vc.setActiveElement(field);
203203
doRename(field.getText());
204204

205-
if (cause == StopEditingCause.TAB) {
206-
EditorPanel editor = gui.getActiveEditor();
207-
208-
if (editor == null) {
209-
return;
210-
}
211-
212-
Token token = editor.getToken(editor.getEditor().getCaretPosition());
213-
214-
SwingUtilities.invokeLater(() -> {
215-
Token next = editor.getSource().getTokenStore().getByType().get(RenamableTokenType.OBFUSCATED).higher(token);
216-
217-
if (next == null) {
218-
editor.getEditor().requestFocusInWindow();
219-
} else {
220-
editor.navigateToToken(next);
221-
}
222-
});
223-
224-
return;
205+
if (cause == StopEditingCause.TAB && e != null) {
206+
// invokeLater as per the method's javadocs
207+
SwingUtilities.invokeLater(e::navigateToNextObfuscatedToken);
225208
}
226209
}
227210

228-
EditorPanel e = gui.getActiveEditor();
229-
230211
if (e != null) {
231212
e.getEditor().requestFocusInWindow();
232213
}

enigma/src/main/java/cuchaz/enigma/utils/I18n.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public static ArrayList<String> getAvailableLanguages() {
101101
List<String> availableTranslations;
102102

103103
try (InputStream is = cl.getResourceAsStream("lang/index.txt")) {
104+
if (is == null) {
105+
// This scenario should only really happen when launching from an IDE that does not run the necessary gradle tasks
106+
throw new IOException("Resource 'lang/index.txt' not found");
107+
}
108+
104109
availableTranslations = Arrays.asList(
105110
new String(is.readAllBytes(), StandardCharsets.UTF_8)
106111
.split("\n")

0 commit comments

Comments
 (0)