summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma
diff options
context:
space:
mode:
authorGravatar Erlend Ã…mdal2019-05-12 09:47:41 +0200
committerGravatar Gegy2019-05-12 09:47:41 +0200
commit65a8ff63bae4f6f2e025e3dbf0b7b8eb64193039 (patch)
treeae282f190a8e86a3f0be75ca168f2660d89c56f3 /src/main/java/cuchaz/enigma
parentSupport navigation to declaration on ctrl+click (diff)
downloadenigma-fork-65a8ff63bae4f6f2e025e3dbf0b7b8eb64193039.tar.gz
enigma-fork-65a8ff63bae4f6f2e025e3dbf0b7b8eb64193039.tar.xz
enigma-fork-65a8ff63bae4f6f2e025e3dbf0b7b8eb64193039.zip
Add forward and backward reference history with mouse navigation (#132)
* Add History * Add forward and backward reference history * Update PopupMenuBar text for history * Fix indentation * Fix more indentation
Diffstat (limited to 'src/main/java/cuchaz/enigma')
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java100
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java69
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java13
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java19
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/util/History.java49
8 files changed, 178 insertions, 78 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 92c68ac..3e85920 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -29,6 +29,7 @@ import cuchaz.enigma.gui.panels.PanelDeobf;
29import cuchaz.enigma.gui.panels.PanelEditor; 29import cuchaz.enigma.gui.panels.PanelEditor;
30import cuchaz.enigma.gui.panels.PanelIdentifier; 30import cuchaz.enigma.gui.panels.PanelIdentifier;
31import cuchaz.enigma.gui.panels.PanelObf; 31import cuchaz.enigma.gui.panels.PanelObf;
32import cuchaz.enigma.gui.util.History;
32import cuchaz.enigma.throwables.IllegalNameException; 33import cuchaz.enigma.throwables.IllegalNameException;
33import cuchaz.enigma.translation.mapping.AccessModifier; 34import cuchaz.enigma.translation.mapping.AccessModifier;
34import cuchaz.enigma.translation.representation.entry.*; 35import cuchaz.enigma.translation.representation.entry.*;
@@ -54,7 +55,8 @@ public class Gui {
54 55
55 private final MenuBar menuBar; 56 private final MenuBar menuBar;
56 // state 57 // state
57 public EntryReference<Entry<?>, Entry<?>> reference; 58 public History<EntryReference<Entry<?>, Entry<?>>> referenceHistory;
59 public EntryReference<Entry<?>, Entry<?>> cursorReference;
58 private boolean shouldNavigateOnClick; 60 private boolean shouldNavigateOnClick;
59 61
60 public FileDialog jarFileChooser; 62 public FileDialog jarFileChooser;
@@ -162,11 +164,11 @@ public class Gui {
162 Object node = path.getLastPathComponent(); 164 Object node = path.getLastPathComponent();
163 if (node instanceof ClassInheritanceTreeNode) { 165 if (node instanceof ClassInheritanceTreeNode) {
164 ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; 166 ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node;
165 navigateTo(new ClassEntry(classNode.getObfClassName())); 167 controller.navigateTo(new ClassEntry(classNode.getObfClassName()));
166 } else if (node instanceof MethodInheritanceTreeNode) { 168 } else if (node instanceof MethodInheritanceTreeNode) {
167 MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; 169 MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node;
168 if (methodNode.isImplemented()) { 170 if (methodNode.isImplemented()) {
169 navigateTo(methodNode.getMethodEntry()); 171 controller.navigateTo(methodNode.getMethodEntry());
170 } 172 }
171 } 173 }
172 } 174 }
@@ -195,10 +197,10 @@ public class Gui {
195 Object node = path.getLastPathComponent(); 197 Object node = path.getLastPathComponent();
196 if (node instanceof ClassImplementationsTreeNode) { 198 if (node instanceof ClassImplementationsTreeNode) {
197 ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; 199 ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node;
198 navigateTo(classNode.getClassEntry()); 200 controller.navigateTo(classNode.getClassEntry());
199 } else if (node instanceof MethodImplementationsTreeNode) { 201 } else if (node instanceof MethodImplementationsTreeNode) {
200 MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; 202 MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node;
201 navigateTo(methodNode.getMethodEntry()); 203 controller.navigateTo(methodNode.getMethodEntry());
202 } 204 }
203 } 205 }
204 } 206 }
@@ -225,9 +227,9 @@ public class Gui {
225 if (node instanceof ReferenceTreeNode) { 227 if (node instanceof ReferenceTreeNode) {
226 ReferenceTreeNode<Entry<?>, Entry<?>> referenceNode = ((ReferenceTreeNode<Entry<?>, Entry<?>>) node); 228 ReferenceTreeNode<Entry<?>, Entry<?>> referenceNode = ((ReferenceTreeNode<Entry<?>, Entry<?>>) node);
227 if (referenceNode.getReference() != null) { 229 if (referenceNode.getReference() != null) {
228 navigateTo(referenceNode.getReference()); 230 controller.navigateTo(referenceNode.getReference());
229 } else { 231 } else {
230 navigateTo(referenceNode.getEntry()); 232 controller.navigateTo(referenceNode.getEntry());
231 } 233 }
232 } 234 }
233 } 235 }
@@ -433,13 +435,13 @@ public class Gui {
433 } 435 }
434 } 436 }
435 437
436 private void showReference(EntryReference<Entry<?>, Entry<?>> reference) { 438 private void showCursorReference(EntryReference<Entry<?>, Entry<?>> reference) {
437 if (reference == null) { 439 if (reference == null) {
438 infoPanel.clearReference(); 440 infoPanel.clearReference();
439 return; 441 return;
440 } 442 }
441 443
442 this.reference = reference; 444 this.cursorReference = reference;
443 445
444 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); 446 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference);
445 447
@@ -526,12 +528,12 @@ public class Gui {
526 Token token = this.controller.getToken(pos); 528 Token token = this.controller.getToken(pos);
527 boolean isToken = token != null; 529 boolean isToken = token != null;
528 530
529 reference = this.controller.getReference(token); 531 cursorReference = this.controller.getReference(token);
530 Entry<?> referenceEntry = reference != null ? reference.entry : null; 532 Entry<?> referenceEntry = cursorReference != null ? cursorReference.entry : null;
531 533
532 if (referenceEntry != null && shouldNavigateOnClick) { 534 if (referenceEntry != null && shouldNavigateOnClick) {
533 shouldNavigateOnClick = false; 535 shouldNavigateOnClick = false;
534 navigateTo(referenceEntry); 536 this.controller.navigateTo(referenceEntry);
535 return; 537 return;
536 } 538 }
537 539
@@ -540,10 +542,10 @@ public class Gui {
540 boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor(); 542 boolean isMethodEntry = isToken && referenceEntry instanceof MethodEntry && !((MethodEntry) referenceEntry).isConstructor();
541 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); 543 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor();
542 boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry); 544 boolean isInJar = isToken && this.controller.entryIsInJar(referenceEntry);
543 boolean isRenamable = isToken && this.controller.getDeobfuscator().isRenamable(reference); 545 boolean isRenamable = isToken && this.controller.getDeobfuscator().isRenamable(cursorReference);
544 546
545 if (isToken) { 547 if (isToken) {
546 showReference(reference); 548 showCursorReference(cursorReference);
547 } else { 549 } else {
548 infoPanel.clearReference(); 550 infoPanel.clearReference();
549 } 551 }
@@ -554,7 +556,8 @@ public class Gui {
554 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); 556 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry);
555 this.popupMenu.showCallsSpecificMenu.setEnabled(isMethodEntry); 557 this.popupMenu.showCallsSpecificMenu.setEnabled(isMethodEntry);
556 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); 558 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry));
557 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 559 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousReference());
560 this.popupMenu.openNextMenu.setEnabled(this.controller.hasNextReference());
558 this.popupMenu.toggleMappingMenu.setEnabled(isRenamable); 561 this.popupMenu.toggleMappingMenu.setEnabled(isRenamable);
559 562
560 if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) { 563 if (isToken && this.controller.getDeobfuscator().isRemapped(referenceEntry)) {
@@ -564,33 +567,12 @@ public class Gui {
564 } 567 }
565 } 568 }
566 569
567 public void navigateTo(Entry<?> entry) {
568 if (!this.controller.entryIsInJar(entry)) {
569 // entry is not in the jar. Ignore it
570 return;
571 }
572 if (reference != null) {
573 this.controller.savePreviousReference(reference);
574 }
575 this.controller.openDeclaration(entry);
576 }
577
578 private void navigateTo(EntryReference<Entry<?>, Entry<?>> reference) {
579 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) {
580 return;
581 }
582 if (this.reference != null) {
583 this.controller.savePreviousReference(this.reference);
584 }
585 this.controller.openReference(reference);
586 }
587
588 public void startRename() { 570 public void startRename() {
589 571
590 // init the text box 572 // init the text box
591 final JTextField text = new JTextField(); 573 final JTextField text = new JTextField();
592 574
593 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(reference); 575 EntryReference<Entry<?>, Entry<?>> translatedReference = controller.getDeobfuscator().deobfuscate(cursorReference);
594 text.setText(translatedReference.getNameableName()); 576 text.setText(translatedReference.getNameableName());
595 577
596 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); 578 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height));
@@ -631,7 +613,7 @@ public class Gui {
631 String newName = text.getText(); 613 String newName = text.getText();
632 if (saveName && newName != null && !newName.isEmpty()) { 614 if (saveName && newName != null && !newName.isEmpty()) {
633 try { 615 try {
634 this.controller.rename(reference, newName, true); 616 this.controller.rename(cursorReference, newName, true);
635 } catch (IllegalNameException ex) { 617 } catch (IllegalNameException ex) {
636 text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 618 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
637 text.setToolTipText(ex.getReason()); 619 text.setToolTipText(ex.getReason());
@@ -643,7 +625,7 @@ public class Gui {
643 // abort the rename 625 // abort the rename
644 JPanel panel = (JPanel) infoPanel.getComponent(0); 626 JPanel panel = (JPanel) infoPanel.getComponent(0);
645 panel.remove(panel.getComponentCount() - 1); 627 panel.remove(panel.getComponentCount() - 1);
646 panel.add(Utils.unboldLabel(new JLabel(reference.getNameableName(), JLabel.LEFT))); 628 panel.add(Utils.unboldLabel(new JLabel(cursorReference.getNameableName(), JLabel.LEFT)));
647 629
648 this.editor.grabFocus(); 630 this.editor.grabFocus();
649 631
@@ -652,24 +634,24 @@ public class Gui {
652 634
653 public void showInheritance() { 635 public void showInheritance() {
654 636
655 if (reference == null) { 637 if (cursorReference == null) {
656 return; 638 return;
657 } 639 }
658 640
659 inheritanceTree.setModel(null); 641 inheritanceTree.setModel(null);
660 642
661 if (reference.entry instanceof ClassEntry) { 643 if (cursorReference.entry instanceof ClassEntry) {
662 // get the class inheritance 644 // get the class inheritance
663 ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); 645 ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) cursorReference.entry);
664 646
665 // show the tree at the root 647 // show the tree at the root
666 TreePath path = getPathToRoot(classNode); 648 TreePath path = getPathToRoot(classNode);
667 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); 649 inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0)));
668 inheritanceTree.expandPath(path); 650 inheritanceTree.expandPath(path);
669 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); 651 inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path));
670 } else if (reference.entry instanceof MethodEntry) { 652 } else if (cursorReference.entry instanceof MethodEntry) {
671 // get the method inheritance 653 // get the method inheritance
672 MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); 654 MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) cursorReference.entry);
673 655
674 // show the tree at the root 656 // show the tree at the root
675 TreePath path = getPathToRoot(classNode); 657 TreePath path = getPathToRoot(classNode);
@@ -685,7 +667,7 @@ public class Gui {
685 667
686 public void showImplementations() { 668 public void showImplementations() {
687 669
688 if (reference == null) { 670 if (cursorReference == null) {
689 return; 671 return;
690 } 672 }
691 673
@@ -694,11 +676,11 @@ public class Gui {
694 DefaultMutableTreeNode node = null; 676 DefaultMutableTreeNode node = null;
695 677
696 // get the class implementations 678 // get the class implementations
697 if (reference.entry instanceof ClassEntry) 679 if (cursorReference.entry instanceof ClassEntry)
698 node = this.controller.getClassImplementations((ClassEntry) reference.entry); 680 node = this.controller.getClassImplementations((ClassEntry) cursorReference.entry);
699 else // get the method implementations 681 else // get the method implementations
700 if (reference.entry instanceof MethodEntry) 682 if (cursorReference.entry instanceof MethodEntry)
701 node = this.controller.getMethodImplementations((MethodEntry) reference.entry); 683 node = this.controller.getMethodImplementations((MethodEntry) cursorReference.entry);
702 684
703 if (node != null) { 685 if (node != null) {
704 // show the tree at the root 686 // show the tree at the root
@@ -714,18 +696,18 @@ public class Gui {
714 } 696 }
715 697
716 public void showCalls(boolean recurse) { 698 public void showCalls(boolean recurse) {
717 if (reference == null) { 699 if (cursorReference == null) {
718 return; 700 return;
719 } 701 }
720 702
721 if (reference.entry instanceof ClassEntry) { 703 if (cursorReference.entry instanceof ClassEntry) {
722 ClassReferenceTreeNode node = this.controller.getClassReferences((ClassEntry) reference.entry); 704 ClassReferenceTreeNode node = this.controller.getClassReferences((ClassEntry) cursorReference.entry);
723 callsTree.setModel(new DefaultTreeModel(node)); 705 callsTree.setModel(new DefaultTreeModel(node));
724 } else if (reference.entry instanceof FieldEntry) { 706 } else if (cursorReference.entry instanceof FieldEntry) {
725 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); 707 FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) cursorReference.entry);
726 callsTree.setModel(new DefaultTreeModel(node)); 708 callsTree.setModel(new DefaultTreeModel(node));
727 } else if (reference.entry instanceof MethodEntry) { 709 } else if (cursorReference.entry instanceof MethodEntry) {
728 MethodReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry, recurse); 710 MethodReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) cursorReference.entry, recurse);
729 callsTree.setModel(new DefaultTreeModel(node)); 711 callsTree.setModel(new DefaultTreeModel(node));
730 } 712 }
731 713
@@ -735,10 +717,10 @@ public class Gui {
735 } 717 }
736 718
737 public void toggleMapping() { 719 public void toggleMapping() {
738 if (this.controller.getDeobfuscator().isRemapped(reference.entry)) { 720 if (this.controller.getDeobfuscator().isRemapped(cursorReference.entry)) {
739 this.controller.removeMapping(reference); 721 this.controller.removeMapping(cursorReference);
740 } else { 722 } else {
741 this.controller.markAsDeobfuscated(reference); 723 this.controller.markAsDeobfuscated(cursorReference);
742 } 724 }
743 } 725 }
744 726
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 4155062..5610233 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.gui; 12package cuchaz.enigma.gui;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.google.common.collect.Queues;
16import com.google.common.util.concurrent.ThreadFactoryBuilder; 15import com.google.common.util.concurrent.ThreadFactoryBuilder;
17import com.strobel.decompiler.languages.java.ast.CompilationUnit; 16import com.strobel.decompiler.languages.java.ast.CompilationUnit;
18import cuchaz.enigma.Deobfuscator; 17import cuchaz.enigma.Deobfuscator;
@@ -20,6 +19,7 @@ import cuchaz.enigma.SourceProvider;
20import cuchaz.enigma.analysis.*; 19import cuchaz.enigma.analysis.*;
21import cuchaz.enigma.config.Config; 20import cuchaz.enigma.config.Config;
22import cuchaz.enigma.gui.dialog.ProgressDialog; 21import cuchaz.enigma.gui.dialog.ProgressDialog;
22import cuchaz.enigma.gui.util.History;
23import cuchaz.enigma.throwables.MappingParseException; 23import cuchaz.enigma.throwables.MappingParseException;
24import cuchaz.enigma.translation.Translator; 24import cuchaz.enigma.translation.Translator;
25import cuchaz.enigma.translation.mapping.*; 25import cuchaz.enigma.translation.mapping.*;
@@ -40,7 +40,6 @@ import java.io.PrintWriter;
40import java.io.StringWriter; 40import java.io.StringWriter;
41import java.nio.file.Path; 41import java.nio.file.Path;
42import java.util.Collection; 42import java.util.Collection;
43import java.util.Deque;
44import java.util.List; 43import java.util.List;
45import java.util.concurrent.ExecutorService; 44import java.util.concurrent.ExecutorService;
46import java.util.concurrent.Executors; 45import java.util.concurrent.Executors;
@@ -53,14 +52,13 @@ public class GuiController {
53 private final Gui gui; 52 private final Gui gui;
54 private Deobfuscator deobfuscator; 53 private Deobfuscator deobfuscator;
55 private DecompiledClassSource currentSource; 54 private DecompiledClassSource currentSource;
56 private Deque<EntryReference<Entry<?>, Entry<?>>> referenceStack; 55
57 56
58 private Path loadedMappingPath; 57 private Path loadedMappingPath;
59 private MappingFormat loadedMappingFormat; 58 private MappingFormat loadedMappingFormat;
60 59
61 public GuiController(Gui gui) { 60 public GuiController(Gui gui) {
62 this.gui = gui; 61 this.gui = gui;
63 this.referenceStack = Queues.newArrayDeque();
64 } 62 }
65 63
66 public boolean isDirty() { 64 public boolean isDirty() {
@@ -246,6 +244,10 @@ public class GuiController {
246 refreshCurrentClass(reference); 244 refreshCurrentClass(reference);
247 } 245 }
248 246
247 /**
248 * Navigates to the declaration with respect to navigation history
249 * @param entry the entry whose declaration will be navigated to
250 */
249 public void openDeclaration(Entry<?> entry) { 251 public void openDeclaration(Entry<?> entry) {
250 if (entry == null) { 252 if (entry == null) {
251 throw new IllegalArgumentException("Entry cannot be null!"); 253 throw new IllegalArgumentException("Entry cannot be null!");
@@ -253,11 +255,29 @@ public class GuiController {
253 openReference(new EntryReference<>(entry, entry.getName())); 255 openReference(new EntryReference<>(entry, entry.getName()));
254 } 256 }
255 257
258 /**
259 * Navigates to the reference with respect to navigation history
260 * @param reference the reference
261 */
256 public void openReference(EntryReference<Entry<?>, Entry<?>> reference) { 262 public void openReference(EntryReference<Entry<?>, Entry<?>> reference) {
257 if (reference == null) { 263 if (reference == null) {
258 throw new IllegalArgumentException("Reference cannot be null!"); 264 throw new IllegalArgumentException("Reference cannot be null!");
259 } 265 }
266 if (this.gui.referenceHistory == null) {
267 this.gui.referenceHistory = new History<>(reference);
268 } else {
269 if (!reference.equals(this.gui.referenceHistory.getCurrent())) {
270 this.gui.referenceHistory.push(reference);
271 }
272 }
273 setReference(reference);
274 }
260 275
276 /**
277 * Navigates to the reference without modifying history. If the class is not currently loaded, it will be loaded.
278 * @param reference the reference
279 */
280 private void setReference(EntryReference<Entry<?>, Entry<?>> reference) {
261 // get the reference target class 281 // get the reference target class
262 ClassEntry classEntry = reference.getLocationClassEntry(); 282 ClassEntry classEntry = reference.getLocationClassEntry();
263 if (!this.deobfuscator.isRenamable(classEntry)) { 283 if (!this.deobfuscator.isRenamable(classEntry)) {
@@ -272,6 +292,10 @@ public class GuiController {
272 } 292 }
273 } 293 }
274 294
295 /**
296 * Navigates to the reference without modifying history. Assumes the class is loaded.
297 * @param reference
298 */
275 private void showReference(EntryReference<Entry<?>, Entry<?>> reference) { 299 private void showReference(EntryReference<Entry<?>, Entry<?>> reference) {
276 EntryRemapper mapper = this.deobfuscator.getMapper(); 300 EntryRemapper mapper = this.deobfuscator.getMapper();
277 301
@@ -289,18 +313,39 @@ public class GuiController {
289 } 313 }
290 } 314 }
291 315
292 public void savePreviousReference(EntryReference<Entry<?>, Entry<?>> reference) { 316 public void openPreviousReference() {
293 this.referenceStack.push(reference); 317 if (hasPreviousReference()) {
318 setReference(gui.referenceHistory.goBack());
319 }
294 } 320 }
295 321
296 public void openPreviousReference() { 322 public boolean hasPreviousReference() {
297 if (hasPreviousLocation()) { 323 return gui.referenceHistory != null && gui.referenceHistory.canGoBack();
298 openReference(this.referenceStack.pop()); 324 }
325
326 public void openNextReference() {
327 if (hasNextReference()) {
328 setReference(gui.referenceHistory.goForward());
299 } 329 }
300 } 330 }
301 331
302 public boolean hasPreviousLocation() { 332 public boolean hasNextReference() {
303 return !this.referenceStack.isEmpty(); 333 return gui.referenceHistory != null && gui.referenceHistory.canGoForward();
334 }
335
336 public void navigateTo(Entry<?> entry) {
337 if (!entryIsInJar(entry)) {
338 // entry is not in the jar. Ignore it
339 return;
340 }
341 openDeclaration(entry);
342 }
343
344 public void navigateTo(EntryReference<Entry<?>, Entry<?>> reference) {
345 if (!entryIsInJar(reference.getLocationClassEntry())) {
346 return;
347 }
348 openReference(reference);
304 } 349 }
305 350
306 private void refreshClasses() { 351 private void refreshClasses() {
@@ -390,7 +435,7 @@ public class GuiController {
390 435
391 public void modifierChange(ItemEvent event) { 436 public void modifierChange(ItemEvent event) {
392 if (event.getStateChange() == ItemEvent.SELECTED) { 437 if (event.getStateChange() == ItemEvent.SELECTED) {
393 deobfuscator.changeModifier(gui.reference.entry, (AccessModifier) event.getItem()); 438 deobfuscator.changeModifier(gui.cursorReference.entry, (AccessModifier) event.getItem());
394 refreshCurrentClass(); 439 refreshCurrentClass();
395 } 440 }
396 } 441 }
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
index da09c52..a122bd8 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/SearchDialog.java
@@ -125,7 +125,7 @@ public class SearchDialog {
125 .filter(classEntry -> classEntry.getSimpleName().equals(classList.getSelectedValue())). 125 .filter(classEntry -> classEntry.getSimpleName().equals(classList.getSelectedValue())).
126 findFirst() 126 findFirst()
127 .ifPresent(classEntry -> { 127 .ifPresent(classEntry -> {
128 parent.navigateTo(classEntry); 128 parent.getController().navigateTo(classEntry);
129 parent.getDeobfPanel().deobfClasses.setSelectionClass(classEntry); 129 parent.getDeobfPanel().deobfClasses.setSelectionClass(classEntry);
130 }); 130 });
131 } 131 }
diff --git a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
index 32f9172..fbf39ac 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/PopupMenuBar.java
@@ -15,6 +15,7 @@ public class PopupMenuBar extends JPopupMenu {
15 public final JMenuItem showCallsSpecificMenu; 15 public final JMenuItem showCallsSpecificMenu;
16 public final JMenuItem openEntryMenu; 16 public final JMenuItem openEntryMenu;
17 public final JMenuItem openPreviousMenu; 17 public final JMenuItem openPreviousMenu;
18 public final JMenuItem openNextMenu;
18 public final JMenuItem toggleMappingMenu; 19 public final JMenuItem toggleMappingMenu;
19 20
20 public PopupMenuBar(Gui gui) { 21 public PopupMenuBar(Gui gui) {
@@ -60,14 +61,14 @@ public class PopupMenuBar extends JPopupMenu {
60 } 61 }
61 { 62 {
62 JMenuItem menu = new JMenuItem("Go to Declaration"); 63 JMenuItem menu = new JMenuItem("Go to Declaration");
63 menu.addActionListener(event -> gui.navigateTo(gui.reference.entry)); 64 menu.addActionListener(event -> gui.getController().navigateTo(gui.cursorReference.entry));
64 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0)); 65 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0));
65 menu.setEnabled(false); 66 menu.setEnabled(false);
66 this.add(menu); 67 this.add(menu);
67 this.openEntryMenu = menu; 68 this.openEntryMenu = menu;
68 } 69 }
69 { 70 {
70 JMenuItem menu = new JMenuItem("Go to previous"); 71 JMenuItem menu = new JMenuItem("Go back");
71 menu.addActionListener(event -> gui.getController().openPreviousReference()); 72 menu.addActionListener(event -> gui.getController().openPreviousReference());
72 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0)); 73 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0));
73 menu.setEnabled(false); 74 menu.setEnabled(false);
@@ -75,6 +76,14 @@ public class PopupMenuBar extends JPopupMenu {
75 this.openPreviousMenu = menu; 76 this.openPreviousMenu = menu;
76 } 77 }
77 { 78 {
79 JMenuItem menu = new JMenuItem("Go forward");
80 menu.addActionListener(event -> gui.getController().openNextReference());
81 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, 0));
82 menu.setEnabled(false);
83 this.add(menu);
84 this.openNextMenu = menu;
85 }
86 {
78 JMenuItem menu = new JMenuItem("Mark as deobfuscated"); 87 JMenuItem menu = new JMenuItem("Mark as deobfuscated");
79 menu.addActionListener(event -> gui.toggleMapping()); 88 menu.addActionListener(event -> gui.toggleMapping());
80 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0)); 89 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 0));
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
index 68cc8e1..2a4e2d7 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
@@ -15,7 +15,7 @@ public class PanelDeobf extends JPanel {
15 this.gui = gui; 15 this.gui = gui;
16 16
17 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true); 17 this.deobfClasses = new ClassSelector(gui, ClassSelector.DEOBF_CLASS_COMPARATOR, true);
18 this.deobfClasses.setSelectionListener(gui::navigateTo); 18 this.deobfClasses.setSelectionListener(gui.getController()::navigateTo);
19 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename); 19 this.deobfClasses.setRenameSelectionListener(gui::onPanelRename);
20 20
21 this.setLayout(new BorderLayout()); 21 this.setLayout(new BorderLayout());
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
index f766743..f19d98f 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java
@@ -21,8 +21,19 @@ public class PanelEditor extends JEditorPane {
21 this.addMouseListener(new MouseAdapter() { 21 this.addMouseListener(new MouseAdapter() {
22 @Override 22 @Override
23 public void mouseReleased(MouseEvent e) { 23 public void mouseReleased(MouseEvent e) {
24 if (e.getButton() == MouseEvent.BUTTON3) 24 switch (e.getButton()) {
25 self.setCaretPosition(self.viewToModel(e.getPoint())); 25 case MouseEvent.BUTTON3: // Right click
26 self.setCaretPosition(self.viewToModel(e.getPoint()));
27 break;
28
29 case 4: // Back navigation
30 gui.getController().openPreviousReference();
31 break;
32
33 case 5: // Forward navigation
34 gui.getController().openNextReference();
35 break;
36 }
26 } 37 }
27 }); 38 });
28 this.addKeyListener(new KeyAdapter() { 39 this.addKeyListener(new KeyAdapter() {
@@ -49,6 +60,10 @@ public class PanelEditor extends JEditorPane {
49 gui.popupMenu.openPreviousMenu.doClick(); 60 gui.popupMenu.openPreviousMenu.doClick();
50 break; 61 break;
51 62
63 case KeyEvent.VK_E:
64 gui.popupMenu.openNextMenu.doClick();
65 break;
66
52 case KeyEvent.VK_C: 67 case KeyEvent.VK_C:
53 gui.popupMenu.showCallsMenu.doClick(); 68 gui.popupMenu.showCallsMenu.doClick();
54 break; 69 break;
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index ccdc9f8..8ee564b 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -26,7 +26,7 @@ public class PanelObf extends JPanel {
26 }; 26 };
27 27
28 this.obfClasses = new ClassSelector(gui, obfClassComparator, false); 28 this.obfClasses = new ClassSelector(gui, obfClassComparator, false);
29 this.obfClasses.setSelectionListener(gui::navigateTo); 29 this.obfClasses.setSelectionListener(gui.getController()::navigateTo);
30 this.obfClasses.setRenameSelectionListener(gui::onPanelRename); 30 this.obfClasses.setRenameSelectionListener(gui::onPanelRename);
31 31
32 this.setLayout(new BorderLayout()); 32 this.setLayout(new BorderLayout());
diff --git a/src/main/java/cuchaz/enigma/gui/util/History.java b/src/main/java/cuchaz/enigma/gui/util/History.java
new file mode 100644
index 0000000..94f3105
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/gui/util/History.java
@@ -0,0 +1,49 @@
1package cuchaz.enigma.gui.util;
2
3import com.google.common.collect.Queues;
4
5import java.util.Deque;
6
7public class History<T> {
8 private final Deque<T> previous = Queues.newArrayDeque();
9 private final Deque<T> next = Queues.newArrayDeque();
10 private T current;
11
12 public History(T initial) {
13 current = initial;
14 }
15
16 public T getCurrent() {
17 return current;
18 }
19
20 public void push(T value) {
21 previous.addLast(current);
22 current = value;
23 next.clear();
24 }
25
26 public void replace(T value) {
27 current = value;
28 }
29
30 public boolean canGoBack() {
31 return !previous.isEmpty();
32 }
33
34 public T goBack() {
35 next.addFirst(current);
36 current = previous.removeLast();
37 return current;
38 }
39
40 public boolean canGoForward() {
41 return !next.isEmpty();
42 }
43
44 public T goForward() {
45 previous.addLast(current);
46 current = next.removeFirst();
47 return current;
48 }
49}