From 6e464ea251cab63c776ece0b2a356f1498ffa294 Mon Sep 17 00:00:00 2001 From: Thog Date: Wed, 8 Mar 2017 08:17:04 +0100 Subject: Follow Fabric guidelines --- src/main/java/cuchaz/enigma/gui/Gui.java | 1594 +++++++++++++++--------------- 1 file changed, 787 insertions(+), 807 deletions(-) (limited to 'src/main/java/cuchaz/enigma/gui/Gui.java') diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index 7cb494f..9f8d6fc 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java @@ -8,6 +8,7 @@ * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ + package cuchaz.enigma.gui; import com.google.common.collect.Lists; @@ -54,811 +55,790 @@ import java.util.function.Function; public class Gui { - private GuiController controller; - - private final PanelObf obfPanel; - private final PanelDeobf deobfPanel; - - private final MenuBar menuBar; - public final PopupMenuBar popupMenu; - - private JFrame frame; - private PanelEditor editor; - private JPanel classesPanel; - private JSplitPane splitClasses; - private PanelIdentifier infoPanel; - private ObfuscatedHighlightPainter obfuscatedHighlightPainter; - private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; - private OtherHighlightPainter otherHighlightPainter; - private SelectionHighlightPainter selectionHighlightPainter; - private JTree inheritanceTree; - private JTree implementationsTree; - private JTree callsTree; - private JList tokens; - private JTabbedPane tabs; - - // state - public EntryReference reference; - - public JFileChooser jarFileChooser; - public JFileChooser enigmaMappingsFileChooser; - - public JFileChooser exportSourceFileChooser; - public JFileChooser exportJarFileChooser; - - public Gui() { - - // init frame - this.frame = new JFrame(Constants.NAME); - final Container pane = this.frame.getContentPane(); - pane.setLayout(new BorderLayout()); - - if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { - // install a global exception handler to the event thread - CrashDialog.init(this.frame); - Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { - t.printStackTrace(System.err); - if (!ExceptionIgnorer.shouldIgnore(t)) { - CrashDialog.show(t); - } - }); - } - - this.controller = new GuiController(this); - - // init file choosers - this.jarFileChooser = new FileChooserFile(); - - - this.enigmaMappingsFileChooser = new FileChooserAny(); - this.exportSourceFileChooser = new FileChooserFolder(); - this.exportJarFileChooser = new FileChooserFile(); - - this.obfPanel = new PanelObf(this); - this.deobfPanel = new PanelDeobf(this); - - // set up classes panel (don't add the splitter yet) - splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); - splitClasses.setResizeWeight(0.3); - this.classesPanel = new JPanel(); - this.classesPanel.setLayout(new BorderLayout()); - this.classesPanel.setPreferredSize(new Dimension(250, 0)); - - // init info panel - infoPanel = new PanelIdentifier(this); - infoPanel.clearReference(); - - // init editor - DefaultSyntaxKit.initKit(); - obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); - deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); - otherHighlightPainter = new OtherHighlightPainter(); - selectionHighlightPainter = new SelectionHighlightPainter(); - this.editor = new PanelEditor(this); - JScrollPane sourceScroller = new JScrollPane(this.editor); - this.editor.setContentType("text/java"); - DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); - kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); - - // init editor popup menu - this.popupMenu = new PopupMenuBar(this); - this.editor.setComponentPopupMenu(this.popupMenu); - - // init inheritance panel - inheritanceTree = new JTree(); - inheritanceTree.setModel(null); - inheritanceTree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent event) { - if (event.getClickCount() == 2) { - // get the selected node - TreePath path = inheritanceTree.getSelectionPath(); - if (path == null) { - return; - } - - Object node = path.getLastPathComponent(); - if (node instanceof ClassInheritanceTreeNode) { - ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; - navigateTo(new ClassEntry(classNode.getObfClassName())); - } else if (node instanceof MethodInheritanceTreeNode) { - MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; - if (methodNode.isImplemented()) { - navigateTo(methodNode.getMethodEntry()); - } - } - } - } - }); - JPanel inheritancePanel = new JPanel(); - inheritancePanel.setLayout(new BorderLayout()); - inheritancePanel.add(new JScrollPane(inheritanceTree)); - - // init implementations panel - implementationsTree = new JTree(); - implementationsTree.setModel(null); - implementationsTree.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent event) { - if (event.getClickCount() == 2) { - // get the selected node - TreePath path = implementationsTree.getSelectionPath(); - if (path == null) { - return; - } - - Object node = path.getLastPathComponent(); - if (node instanceof ClassImplementationsTreeNode) { - ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; - navigateTo(classNode.getClassEntry()); - } else if (node instanceof MethodImplementationsTreeNode) { - MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; - navigateTo(methodNode.getMethodEntry()); - } - } - } - }); - JPanel implementationsPanel = new JPanel(); - implementationsPanel.setLayout(new BorderLayout()); - implementationsPanel.add(new JScrollPane(implementationsTree)); - - // init call panel - callsTree = new JTree(); - callsTree.setModel(null); - callsTree.addMouseListener(new MouseAdapter() { - @SuppressWarnings("unchecked") - @Override - public void mouseClicked(MouseEvent event) { - if (event.getClickCount() == 2) { - // get the selected node - TreePath path = callsTree.getSelectionPath(); - if (path == null) { - return; - } - - Object node = path.getLastPathComponent(); - if (node instanceof ReferenceTreeNode) { - ReferenceTreeNode referenceNode = ((ReferenceTreeNode) node); - if (referenceNode.getReference() != null) { - navigateTo(referenceNode.getReference()); - } else { - navigateTo(referenceNode.getEntry()); - } - } - } - } - }); - tokens = new JList<>(); - tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); - tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - tokens.setLayoutOrientation(JList.VERTICAL); - tokens.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent event) { - if (event.getClickCount() == 2) { - Token selected = tokens.getSelectedValue(); - if (selected != null) { - showToken(selected); - } - } - } - }); - tokens.setPreferredSize(new Dimension(0, 200)); - tokens.setMinimumSize(new Dimension(0, 200)); - JSplitPane callPanel = new JSplitPane( - JSplitPane.VERTICAL_SPLIT, - true, - new JScrollPane(callsTree), - new JScrollPane(tokens) - ); - callPanel.setResizeWeight(1); // let the top side take all the slack - callPanel.resetToPreferredSizes(); - - // layout controls - JPanel centerPanel = new JPanel(); - centerPanel.setLayout(new BorderLayout()); - centerPanel.add(infoPanel, BorderLayout.NORTH); - centerPanel.add(sourceScroller, BorderLayout.CENTER); - tabs = new JTabbedPane(); - tabs.setPreferredSize(new Dimension(250, 0)); - tabs.addTab("Inheritance", inheritancePanel); - tabs.addTab("Implementations", implementationsPanel); - tabs.addTab("Call Graph", callPanel); - JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); - splitRight.setResizeWeight(1); // let the left side take all the slack - splitRight.resetToPreferredSizes(); - JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); - splitCenter.setResizeWeight(0); // let the right side take all the slack - pane.add(splitCenter, BorderLayout.CENTER); - - // init menus - this.menuBar = new MenuBar(this); - this.frame.setJMenuBar(this.menuBar); - - // init state - onCloseJar(); - - this.frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing(WindowEvent event) { - close(); - } - }); - - // show the frame - pane.doLayout(); - this.frame.setSize(1024, 576); - this.frame.setMinimumSize(new Dimension(640, 480)); - this.frame.setVisible(true); - this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - } - - public JFrame getFrame() { - return this.frame; - } - - public GuiController getController() { - return this.controller; - } - - public void onStartOpenJar() { - this.classesPanel.removeAll(); - JPanel panel = new JPanel(); - panel.setLayout(new FlowLayout()); - panel.add(new JLabel("Loading...")); - this.classesPanel.add(panel); - redraw(); - } - - public void onFinishOpenJar(String jarName) { - // update gui - this.frame.setTitle(Constants.NAME + " - " + jarName); - this.classesPanel.removeAll(); - this.classesPanel.add(splitClasses); - setSource(null); - - // update menu - this.menuBar.closeJarMenu.setEnabled(true); - this.menuBar.openEnigmaMappingsMenu.setEnabled(true); - this.menuBar.saveMappingsMenu.setEnabled(false); - this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); - this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); - this.menuBar.saveMappingsSrgMenu.setEnabled(true); - this.menuBar.closeMappingsMenu.setEnabled(true); - this.menuBar.exportSourceMenu.setEnabled(true); - this.menuBar.exportJarMenu.setEnabled(true); - - redraw(); - } - - public void onCloseJar() { - // update gui - this.frame.setTitle(Constants.NAME); - setObfClasses(null); - setDeobfClasses(null); - setSource(null); - this.classesPanel.removeAll(); - - // update menu - this.menuBar.closeJarMenu.setEnabled(false); - this.menuBar.openEnigmaMappingsMenu.setEnabled(false); - this.menuBar.saveMappingsMenu.setEnabled(false); - this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); - this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); - this.menuBar.saveMappingsSrgMenu.setEnabled(false); - this.menuBar.closeMappingsMenu.setEnabled(false); - this.menuBar.exportSourceMenu.setEnabled(false); - this.menuBar.exportJarMenu.setEnabled(false); - - redraw(); - } - - public void setObfClasses(Collection obfClasses) { - this.obfPanel.obfClasses.setClasses(obfClasses); - } - - public void setDeobfClasses(Collection deobfClasses) { - this.deobfPanel.deobfClasses.setClasses(deobfClasses); - } - - public void setMappingsFile(File file) { - this.enigmaMappingsFileChooser.setSelectedFile(file); - this.menuBar.saveMappingsMenu.setEnabled(file != null); - } - - public void setSource(String source) { - this.editor.getHighlighter().removeAllHighlights(); - this.editor.setText(source); - } - - public void showToken(final Token token) { - if (token == null) { - throw new IllegalArgumentException("Token cannot be null!"); - } - CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); - redraw(); - } - - public void showTokens(Collection tokens) { - Vector sortedTokens = new Vector<>(tokens); - Collections.sort(sortedTokens); - if (sortedTokens.size() > 1) { - // sort the tokens and update the tokens panel - this.tokens.setListData(sortedTokens); - this.tokens.setSelectedIndex(0); - } else { - this.tokens.setListData(new Vector<>()); - } - - // show the first token - showToken(sortedTokens.get(0)); - } - - public void setHighlightedTokens(Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens) { - - // remove any old highlighters - this.editor.getHighlighter().removeAllHighlights(); - - // color things based on the index - if (obfuscatedTokens != null) { - setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); - } - if (deobfuscatedTokens != null) { - setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); - } - if (otherTokens != null) { - setHighlightedTokens(otherTokens, otherHighlightPainter); - } - - redraw(); - } - - private void setHighlightedTokens(Iterable tokens, Highlighter.HighlightPainter painter) { - for (Token token : tokens) { - try { - this.editor.getHighlighter().addHighlight(token.start, token.end, painter); - } catch (BadLocationException ex) { - throw new IllegalArgumentException(ex); - } - } - } - - private void showReference(EntryReference reference) { - if (reference == null) { - infoPanel.clearReference(); - return; - } - - this.reference = reference; - - infoPanel.removeAll(); - if (reference.entry instanceof ClassEntry) { - showClassEntry((ClassEntry) this.reference.entry); - } else if (this.reference.entry instanceof FieldEntry) { - showFieldEntry((FieldEntry) this.reference.entry); - } else if (this.reference.entry instanceof MethodEntry) { - showMethodEntry((MethodEntry) this.reference.entry); - } else if (this.reference.entry instanceof ConstructorEntry) { - showConstructorEntry((ConstructorEntry) this.reference.entry); - } else if (this.reference.entry instanceof ArgumentEntry) { - showArgumentEntry((ArgumentEntry) this.reference.entry); - } else if (this.reference.entry instanceof LocalVariableEntry) { - showLocalVariableEntry((LocalVariableEntry) this.reference.entry); - } else { - throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); - } - - redraw(); - } - - private void showLocalVariableEntry(LocalVariableEntry entry) { - addNameValue(infoPanel, "Variable", entry.getName()); - addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); - addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); - addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); - addNameValue(infoPanel, "Type", entry.getType().toString()); - } - - private void showClassEntry(ClassEntry entry) { - addNameValue(infoPanel, "Class", entry.getName()); - addModifierComboBox(infoPanel, "Modifier", entry); - } - - private void showFieldEntry(FieldEntry entry) { - addNameValue(infoPanel, "Field", entry.getName()); - addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); - addNameValue(infoPanel, "Type", entry.getType().toString()); - addModifierComboBox(infoPanel, "Modifier", entry); - } - - private void showMethodEntry(MethodEntry entry) { - addNameValue(infoPanel, "Method", entry.getName()); - addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); - addNameValue(infoPanel, "Signature", entry.getSignature().toString()); - addModifierComboBox(infoPanel, "Modifier", entry); - - } - - private void showConstructorEntry(ConstructorEntry entry) { - addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); - if (!entry.isStatic()) { - addNameValue(infoPanel, "Signature", entry.getSignature().toString()); - addModifierComboBox(infoPanel, "Modifier", entry); - } - } - - private void showArgumentEntry(ArgumentEntry entry) { - addNameValue(infoPanel, "Argument", entry.getName()); - addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); - addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); - addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); - } - - private void addNameValue(JPanel container, String name, String value) { - JPanel panel = new JPanel(); - panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); - container.add(panel); - - JLabel label = new JLabel(name + ":", JLabel.RIGHT); - label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); - panel.add(label); - - panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); - } - - private JComboBox addModifierComboBox(JPanel container, String name, Entry entry) - { - if (!getController().entryIsInJar(entry)) - return null; - JPanel panel = new JPanel(); - panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); - container.add(panel); - JLabel label = new JLabel(name + ":", JLabel.RIGHT); - label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); - panel.add(label); - JComboBox combo = new JComboBox<>(Mappings.EntryModifier.values()); - ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); - combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); - combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); - combo.addItemListener(getController()::modifierChange); - panel.add(combo); - return combo; - } - - public void onCaretMove(int pos) { - - Token token = this.controller.getToken(pos); - boolean isToken = token != null; - - reference = this.controller.getDeobfReference(token); - boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; - boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; - boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; - boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; - boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); - boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); - - if (isToken) { - showReference(reference); - } else { - infoPanel.clearReference(); - } - - this.popupMenu.renameMenu.setEnabled(isRenameable); - this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); - this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); - this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); - this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); - this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); - this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); - - if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { - this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); - } else { - this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); - } - } - - public void navigateTo(Entry entry) { - if (!this.controller.entryIsInJar(entry)) { - // entry is not in the jar. Ignore it - return; - } - if (reference != null) { - this.controller.savePreviousReference(reference); - } - this.controller.openDeclaration(entry); - } - - private void navigateTo(EntryReference reference) { - if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { - return; - } - if (this.reference != null) { - this.controller.savePreviousReference(this.reference); - } - this.controller.openReference(reference); - } - - public void startRename() { - - // init the text box - final JTextField text = new JTextField(); - text.setText(reference.getNamableName()); - text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); - text.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent event) { - switch (event.getKeyCode()) { - case KeyEvent.VK_ENTER: - finishRename(text, true); - break; - - case KeyEvent.VK_ESCAPE: - finishRename(text, false); - break; - default: - break; - } - } - }); - - // find the label with the name and replace it with the text box - JPanel panel = (JPanel) infoPanel.getComponent(0); - panel.remove(panel.getComponentCount() - 1); - panel.add(text); - text.grabFocus(); - - int offset = text.getText().lastIndexOf('/') + 1; - // If it's a class and isn't in the default package, assume that it's deobfuscated. - if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) - text.select(offset, text.getText().length()); - else - text.selectAll(); - - redraw(); - } - - private void finishRename(JTextField text, boolean saveName) { - String newName = text.getText(); - if (saveName && newName != null && newName.length() > 0) { - try { - this.controller.rename(reference, newName); - } catch (IllegalNameException ex) { - text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); - text.setToolTipText(ex.getReason()); - Utils.showToolTipNow(text); - } - return; - } - - // abort the rename - JPanel panel = (JPanel) infoPanel.getComponent(0); - panel.remove(panel.getComponentCount() - 1); - panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); - - this.editor.grabFocus(); - - redraw(); - } - - public void showInheritance() { - - if (reference == null) { - return; - } - - inheritanceTree.setModel(null); - - if (reference.entry instanceof ClassEntry) { - // get the class inheritance - ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); - - // show the tree at the root - TreePath path = getPathToRoot(classNode); - inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); - inheritanceTree.expandPath(path); - inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); - } else if (reference.entry instanceof MethodEntry) { - // get the method inheritance - MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); - - // show the tree at the root - TreePath path = getPathToRoot(classNode); - inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); - inheritanceTree.expandPath(path); - inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); - } - - tabs.setSelectedIndex(0); - redraw(); - } - - public void showImplementations() { - - if (reference == null) { - return; - } - - implementationsTree.setModel(null); - - DefaultMutableTreeNode node = null; - - // get the class implementations - if (reference.entry instanceof ClassEntry) - node = this.controller.getClassImplementations((ClassEntry) reference.entry); - else // get the method implementations - if (reference.entry instanceof MethodEntry) - node = this.controller.getMethodImplementations((MethodEntry) reference.entry); - - if (node != null) { - // show the tree at the root - TreePath path = getPathToRoot(node); - implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); - implementationsTree.expandPath(path); - implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); - } - - tabs.setSelectedIndex(1); - redraw(); - } - - public void showCalls() { - if (reference == null) { - return; - } - - if (reference.entry instanceof ClassEntry) { - // look for calls to the default constructor - // TODO: get a list of all the constructors and find calls to all of them - BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); - callsTree.setModel(new DefaultTreeModel(node)); - } else if (reference.entry instanceof FieldEntry) { - FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); - callsTree.setModel(new DefaultTreeModel(node)); - } else if (reference.entry instanceof MethodEntry) { - BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); - callsTree.setModel(new DefaultTreeModel(node)); - } else if (reference.entry instanceof ConstructorEntry) { - BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); - callsTree.setModel(new DefaultTreeModel(node)); - } - - tabs.setSelectedIndex(2); - redraw(); - } - - public void toggleMapping() { - if (this.controller.entryHasDeobfuscatedName(reference.entry)) { - this.controller.removeMapping(reference); - } else { - this.controller.markAsDeobfuscated(reference); - } - } - - private TreePath getPathToRoot(TreeNode node) { - List nodes = Lists.newArrayList(); - TreeNode n = node; - do { - nodes.add(n); - n = n.getParent(); - } while (n != null); - Collections.reverse(nodes); - return new TreePath(nodes.toArray()); - } - - public void showDiscardDiag(Function callback, String... options) - { - int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, null, options, options[2]); - callback.apply(response); - } - - public void saveMapping() throws IOException - { - if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) - this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); - } - - public void close() { - if (!this.controller.isDirty()) { - // everything is saved, we can exit safely - this.frame.dispose(); - System.exit(0); - } else { - // ask to save before closing - showDiscardDiag((response) -> { - if (response == JOptionPane.YES_OPTION) - { - try { - this.saveMapping(); - this.frame.dispose(); - - } catch (IOException ex) { - throw new Error(ex); - } - } - else if (response == JOptionPane.NO_OPTION) - this.frame.dispose(); - - return null; - }, "Save and exit", "Discard changes", "Cancel"); - } - } - - public void redraw() { - this.frame.validate(); - this.frame.repaint(); - } - - public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException - { - // package rename - if (data instanceof String) - { - for (int i = 0; i < node.getChildCount(); i++) - { - data = Descriptor.toJvmName((String) data); - DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); - ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); - ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); - this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); - childNode.setUserObject(dataChild); - } - node.setUserObject(data); - // Ob package will never be modified, just reload deob view - this.deobfPanel.deobfClasses.reload(); - } - // class rename - else if (data instanceof ClassEntry) - this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); - } - - public void moveClassTree(EntryReference deobfReference, String newName) - { - String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); - String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); - moveClassTree(deobfReference, newName, oldEntry == null, - newEntry == null); - } - - public void moveClassTree(EntryReference deobfReference, String newName, boolean isOldOb, boolean isNewOb) - { - ClassEntry oldEntry = deobfReference.entry.getClassEntry(); - ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); - - // Ob -> deob - if (isOldOb && !isNewOb) - { - this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); - ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); - this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); - this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); - this.deobfPanel.deobfClasses.reload(); - this.obfPanel.obfClasses.reload(); - } - // Deob -> ob - else if (isNewOb && !isOldOb) - { - this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); - ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); - this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); - this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); - this.deobfPanel.deobfClasses.reload(); - this.obfPanel.obfClasses.reload(); - } - // Local move - else if (isOldOb) - { - this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); - this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); - this.obfPanel.obfClasses.reload(); - } - else - { - this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); - this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); - this.deobfPanel.deobfClasses.reload(); - } - } + public final PopupMenuBar popupMenu; + private final PanelObf obfPanel; + private final PanelDeobf deobfPanel; + + private final MenuBar menuBar; + // state + public EntryReference reference; + public JFileChooser jarFileChooser; + public JFileChooser enigmaMappingsFileChooser; + public JFileChooser exportSourceFileChooser; + public JFileChooser exportJarFileChooser; + private GuiController controller; + private JFrame frame; + private PanelEditor editor; + private JPanel classesPanel; + private JSplitPane splitClasses; + private PanelIdentifier infoPanel; + private ObfuscatedHighlightPainter obfuscatedHighlightPainter; + private DeobfuscatedHighlightPainter deobfuscatedHighlightPainter; + private OtherHighlightPainter otherHighlightPainter; + private SelectionHighlightPainter selectionHighlightPainter; + private JTree inheritanceTree; + private JTree implementationsTree; + private JTree callsTree; + private JList tokens; + private JTabbedPane tabs; + + public Gui() { + + // init frame + this.frame = new JFrame(Constants.NAME); + final Container pane = this.frame.getContentPane(); + pane.setLayout(new BorderLayout()); + + if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) { + // install a global exception handler to the event thread + CrashDialog.init(this.frame); + Thread.setDefaultUncaughtExceptionHandler((thread, t) -> { + t.printStackTrace(System.err); + if (!ExceptionIgnorer.shouldIgnore(t)) { + CrashDialog.show(t); + } + }); + } + + this.controller = new GuiController(this); + + // init file choosers + this.jarFileChooser = new FileChooserFile(); + + this.enigmaMappingsFileChooser = new FileChooserAny(); + this.exportSourceFileChooser = new FileChooserFolder(); + this.exportJarFileChooser = new FileChooserFile(); + + this.obfPanel = new PanelObf(this); + this.deobfPanel = new PanelDeobf(this); + + // set up classes panel (don't add the splitter yet) + splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, this.obfPanel, this.deobfPanel); + splitClasses.setResizeWeight(0.3); + this.classesPanel = new JPanel(); + this.classesPanel.setLayout(new BorderLayout()); + this.classesPanel.setPreferredSize(new Dimension(250, 0)); + + // init info panel + infoPanel = new PanelIdentifier(this); + infoPanel.clearReference(); + + // init editor + DefaultSyntaxKit.initKit(); + obfuscatedHighlightPainter = new ObfuscatedHighlightPainter(); + deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter(); + otherHighlightPainter = new OtherHighlightPainter(); + selectionHighlightPainter = new SelectionHighlightPainter(); + this.editor = new PanelEditor(this); + JScrollPane sourceScroller = new JScrollPane(this.editor); + this.editor.setContentType("text/java"); + DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); + kit.toggleComponent(this.editor, "de.sciss.syntaxpane.components.TokenMarker"); + + // init editor popup menu + this.popupMenu = new PopupMenuBar(this); + this.editor.setComponentPopupMenu(this.popupMenu); + + // init inheritance panel + inheritanceTree = new JTree(); + inheritanceTree.setModel(null); + inheritanceTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = inheritanceTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassInheritanceTreeNode) { + ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode) node; + navigateTo(new ClassEntry(classNode.getObfClassName())); + } else if (node instanceof MethodInheritanceTreeNode) { + MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode) node; + if (methodNode.isImplemented()) { + navigateTo(methodNode.getMethodEntry()); + } + } + } + } + }); + JPanel inheritancePanel = new JPanel(); + inheritancePanel.setLayout(new BorderLayout()); + inheritancePanel.add(new JScrollPane(inheritanceTree)); + + // init implementations panel + implementationsTree = new JTree(); + implementationsTree.setModel(null); + implementationsTree.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = implementationsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ClassImplementationsTreeNode) { + ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode) node; + navigateTo(classNode.getClassEntry()); + } else if (node instanceof MethodImplementationsTreeNode) { + MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode) node; + navigateTo(methodNode.getMethodEntry()); + } + } + } + }); + JPanel implementationsPanel = new JPanel(); + implementationsPanel.setLayout(new BorderLayout()); + implementationsPanel.add(new JScrollPane(implementationsTree)); + + // init call panel + callsTree = new JTree(); + callsTree.setModel(null); + callsTree.addMouseListener(new MouseAdapter() { + @SuppressWarnings("unchecked") + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + // get the selected node + TreePath path = callsTree.getSelectionPath(); + if (path == null) { + return; + } + + Object node = path.getLastPathComponent(); + if (node instanceof ReferenceTreeNode) { + ReferenceTreeNode referenceNode = ((ReferenceTreeNode) node); + if (referenceNode.getReference() != null) { + navigateTo(referenceNode.getReference()); + } else { + navigateTo(referenceNode.getEntry()); + } + } + } + } + }); + tokens = new JList<>(); + tokens.setCellRenderer(new TokenListCellRenderer(this.controller)); + tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + tokens.setLayoutOrientation(JList.VERTICAL); + tokens.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) { + if (event.getClickCount() == 2) { + Token selected = tokens.getSelectedValue(); + if (selected != null) { + showToken(selected); + } + } + } + }); + tokens.setPreferredSize(new Dimension(0, 200)); + tokens.setMinimumSize(new Dimension(0, 200)); + JSplitPane callPanel = new JSplitPane( + JSplitPane.VERTICAL_SPLIT, + true, + new JScrollPane(callsTree), + new JScrollPane(tokens) + ); + callPanel.setResizeWeight(1); // let the top side take all the slack + callPanel.resetToPreferredSizes(); + + // layout controls + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BorderLayout()); + centerPanel.add(infoPanel, BorderLayout.NORTH); + centerPanel.add(sourceScroller, BorderLayout.CENTER); + tabs = new JTabbedPane(); + tabs.setPreferredSize(new Dimension(250, 0)); + tabs.addTab("Inheritance", inheritancePanel); + tabs.addTab("Implementations", implementationsPanel); + tabs.addTab("Call Graph", callPanel); + JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); + splitRight.setResizeWeight(1); // let the left side take all the slack + splitRight.resetToPreferredSizes(); + JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); + splitCenter.setResizeWeight(0); // let the right side take all the slack + pane.add(splitCenter, BorderLayout.CENTER); + + // init menus + this.menuBar = new MenuBar(this); + this.frame.setJMenuBar(this.menuBar); + + // init state + onCloseJar(); + + this.frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent event) { + close(); + } + }); + + // show the frame + pane.doLayout(); + this.frame.setSize(1024, 576); + this.frame.setMinimumSize(new Dimension(640, 480)); + this.frame.setVisible(true); + this.frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + } + + public JFrame getFrame() { + return this.frame; + } + + public GuiController getController() { + return this.controller; + } + + public void onStartOpenJar() { + this.classesPanel.removeAll(); + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout()); + panel.add(new JLabel("Loading...")); + this.classesPanel.add(panel); + redraw(); + } + + public void onFinishOpenJar(String jarName) { + // update gui + this.frame.setTitle(Constants.NAME + " - " + jarName); + this.classesPanel.removeAll(); + this.classesPanel.add(splitClasses); + setSource(null); + + // update menu + this.menuBar.closeJarMenu.setEnabled(true); + this.menuBar.openEnigmaMappingsMenu.setEnabled(true); + this.menuBar.saveMappingsMenu.setEnabled(false); + this.menuBar.saveMappingEnigmaFileMenu.setEnabled(true); + this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(true); + this.menuBar.saveMappingsSrgMenu.setEnabled(true); + this.menuBar.closeMappingsMenu.setEnabled(true); + this.menuBar.exportSourceMenu.setEnabled(true); + this.menuBar.exportJarMenu.setEnabled(true); + + redraw(); + } + + public void onCloseJar() { + // update gui + this.frame.setTitle(Constants.NAME); + setObfClasses(null); + setDeobfClasses(null); + setSource(null); + this.classesPanel.removeAll(); + + // update menu + this.menuBar.closeJarMenu.setEnabled(false); + this.menuBar.openEnigmaMappingsMenu.setEnabled(false); + this.menuBar.saveMappingsMenu.setEnabled(false); + this.menuBar.saveMappingEnigmaFileMenu.setEnabled(false); + this.menuBar.saveMappingEnigmaDirectoryMenu.setEnabled(false); + this.menuBar.saveMappingsSrgMenu.setEnabled(false); + this.menuBar.closeMappingsMenu.setEnabled(false); + this.menuBar.exportSourceMenu.setEnabled(false); + this.menuBar.exportJarMenu.setEnabled(false); + + redraw(); + } + + public void setObfClasses(Collection obfClasses) { + this.obfPanel.obfClasses.setClasses(obfClasses); + } + + public void setDeobfClasses(Collection deobfClasses) { + this.deobfPanel.deobfClasses.setClasses(deobfClasses); + } + + public void setMappingsFile(File file) { + this.enigmaMappingsFileChooser.setSelectedFile(file); + this.menuBar.saveMappingsMenu.setEnabled(file != null); + } + + public void setSource(String source) { + this.editor.getHighlighter().removeAllHighlights(); + this.editor.setText(source); + } + + public void showToken(final Token token) { + if (token == null) { + throw new IllegalArgumentException("Token cannot be null!"); + } + CodeReader.navigateToToken(this.editor, token, selectionHighlightPainter); + redraw(); + } + + public void showTokens(Collection tokens) { + Vector sortedTokens = new Vector<>(tokens); + Collections.sort(sortedTokens); + if (sortedTokens.size() > 1) { + // sort the tokens and update the tokens panel + this.tokens.setListData(sortedTokens); + this.tokens.setSelectedIndex(0); + } else { + this.tokens.setListData(new Vector<>()); + } + + // show the first token + showToken(sortedTokens.get(0)); + } + + public void setHighlightedTokens(Iterable obfuscatedTokens, Iterable deobfuscatedTokens, Iterable otherTokens) { + + // remove any old highlighters + this.editor.getHighlighter().removeAllHighlights(); + + // color things based on the index + if (obfuscatedTokens != null) { + setHighlightedTokens(obfuscatedTokens, obfuscatedHighlightPainter); + } + if (deobfuscatedTokens != null) { + setHighlightedTokens(deobfuscatedTokens, deobfuscatedHighlightPainter); + } + if (otherTokens != null) { + setHighlightedTokens(otherTokens, otherHighlightPainter); + } + + redraw(); + } + + private void setHighlightedTokens(Iterable tokens, Highlighter.HighlightPainter painter) { + for (Token token : tokens) { + try { + this.editor.getHighlighter().addHighlight(token.start, token.end, painter); + } catch (BadLocationException ex) { + throw new IllegalArgumentException(ex); + } + } + } + + private void showReference(EntryReference reference) { + if (reference == null) { + infoPanel.clearReference(); + return; + } + + this.reference = reference; + + infoPanel.removeAll(); + if (reference.entry instanceof ClassEntry) { + showClassEntry((ClassEntry) this.reference.entry); + } else if (this.reference.entry instanceof FieldEntry) { + showFieldEntry((FieldEntry) this.reference.entry); + } else if (this.reference.entry instanceof MethodEntry) { + showMethodEntry((MethodEntry) this.reference.entry); + } else if (this.reference.entry instanceof ConstructorEntry) { + showConstructorEntry((ConstructorEntry) this.reference.entry); + } else if (this.reference.entry instanceof ArgumentEntry) { + showArgumentEntry((ArgumentEntry) this.reference.entry); + } else if (this.reference.entry instanceof LocalVariableEntry) { + showLocalVariableEntry((LocalVariableEntry) this.reference.entry); + } else { + throw new Error("Unknown entry type: " + this.reference.entry.getClass().getName()); + } + + redraw(); + } + + private void showLocalVariableEntry(LocalVariableEntry entry) { + addNameValue(infoPanel, "Variable", entry.getName()); + addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); + addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); + addNameValue(infoPanel, "Type", entry.getType().toString()); + } + + private void showClassEntry(ClassEntry entry) { + addNameValue(infoPanel, "Class", entry.getName()); + addModifierComboBox(infoPanel, "Modifier", entry); + } + + private void showFieldEntry(FieldEntry entry) { + addNameValue(infoPanel, "Field", entry.getName()); + addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(infoPanel, "Type", entry.getType().toString()); + addModifierComboBox(infoPanel, "Modifier", entry); + } + + private void showMethodEntry(MethodEntry entry) { + addNameValue(infoPanel, "Method", entry.getName()); + addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(infoPanel, "Signature", entry.getSignature().toString()); + addModifierComboBox(infoPanel, "Modifier", entry); + + } + + private void showConstructorEntry(ConstructorEntry entry) { + addNameValue(infoPanel, "Constructor", entry.getClassEntry().getName()); + if (!entry.isStatic()) { + addNameValue(infoPanel, "Signature", entry.getSignature().toString()); + addModifierComboBox(infoPanel, "Modifier", entry); + } + } + + private void showArgumentEntry(ArgumentEntry entry) { + addNameValue(infoPanel, "Argument", entry.getName()); + addNameValue(infoPanel, "Class", entry.getClassEntry().getName()); + addNameValue(infoPanel, "Method", entry.getBehaviorEntry().getName()); + addNameValue(infoPanel, "Index", Integer.toString(entry.getIndex())); + } + + private void addNameValue(JPanel container, String name, String value) { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); + + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); + + panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); + } + + private JComboBox addModifierComboBox(JPanel container, String name, Entry entry) { + if (!getController().entryIsInJar(entry)) + return null; + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); + JComboBox combo = new JComboBox<>(Mappings.EntryModifier.values()); + ((JLabel) combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); + combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); + combo.addItemListener(getController()::modifierChange); + panel.add(combo); + return combo; + } + + public void onCaretMove(int pos) { + + Token token = this.controller.getToken(pos); + boolean isToken = token != null; + + reference = this.controller.getDeobfReference(token); + boolean isClassEntry = isToken && reference.entry instanceof ClassEntry; + boolean isFieldEntry = isToken && reference.entry instanceof FieldEntry; + boolean isMethodEntry = isToken && reference.entry instanceof MethodEntry; + boolean isConstructorEntry = isToken && reference.entry instanceof ConstructorEntry; + boolean isInJar = isToken && this.controller.entryIsInJar(reference.entry); + boolean isRenameable = isToken && this.controller.referenceIsRenameable(reference); + + if (isToken) { + showReference(reference); + } else { + infoPanel.clearReference(); + } + + this.popupMenu.renameMenu.setEnabled(isRenameable); + this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); + this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); + this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); + this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); + this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); + this.popupMenu.toggleMappingMenu.setEnabled(isRenameable); + + if (isToken && this.controller.entryHasDeobfuscatedName(reference.entry)) { + this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); + } else { + this.popupMenu.toggleMappingMenu.setText("Mark as deobfuscated"); + } + } + + public void navigateTo(Entry entry) { + if (!this.controller.entryIsInJar(entry)) { + // entry is not in the jar. Ignore it + return; + } + if (reference != null) { + this.controller.savePreviousReference(reference); + } + this.controller.openDeclaration(entry); + } + + private void navigateTo(EntryReference reference) { + if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { + return; + } + if (this.reference != null) { + this.controller.savePreviousReference(this.reference); + } + this.controller.openReference(reference); + } + + public void startRename() { + + // init the text box + final JTextField text = new JTextField(); + text.setText(reference.getNamableName()); + text.setPreferredSize(new Dimension(360, text.getPreferredSize().height)); + text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent event) { + switch (event.getKeyCode()) { + case KeyEvent.VK_ENTER: + finishRename(text, true); + break; + + case KeyEvent.VK_ESCAPE: + finishRename(text, false); + break; + default: + break; + } + } + }); + + // find the label with the name and replace it with the text box + JPanel panel = (JPanel) infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(text); + text.grabFocus(); + + int offset = text.getText().lastIndexOf('/') + 1; + // If it's a class and isn't in the default package, assume that it's deobfuscated. + if (reference.getNameableEntry() instanceof ClassEntry && text.getText().contains("/") && offset != 0) + text.select(offset, text.getText().length()); + else + text.selectAll(); + + redraw(); + } + + private void finishRename(JTextField text, boolean saveName) { + String newName = text.getText(); + if (saveName && newName != null && !newName.isEmpty()) { + try { + this.controller.rename(reference, newName); + } catch (IllegalNameException ex) { + text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); + text.setToolTipText(ex.getReason()); + Utils.showToolTipNow(text); + } + return; + } + + // abort the rename + JPanel panel = (JPanel) infoPanel.getComponent(0); + panel.remove(panel.getComponentCount() - 1); + panel.add(Utils.unboldLabel(new JLabel(reference.getNamableName(), JLabel.LEFT))); + + this.editor.grabFocus(); + + redraw(); + } + + public void showInheritance() { + + if (reference == null) { + return; + } + + inheritanceTree.setModel(null); + + if (reference.entry instanceof ClassEntry) { + // get the class inheritance + ClassInheritanceTreeNode classNode = this.controller.getClassInheritance((ClassEntry) reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); + inheritanceTree.expandPath(path); + inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); + } else if (reference.entry instanceof MethodEntry) { + // get the method inheritance + MethodInheritanceTreeNode classNode = this.controller.getMethodInheritance((MethodEntry) reference.entry); + + // show the tree at the root + TreePath path = getPathToRoot(classNode); + inheritanceTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); + inheritanceTree.expandPath(path); + inheritanceTree.setSelectionRow(inheritanceTree.getRowForPath(path)); + } + + tabs.setSelectedIndex(0); + redraw(); + } + + public void showImplementations() { + + if (reference == null) { + return; + } + + implementationsTree.setModel(null); + + DefaultMutableTreeNode node = null; + + // get the class implementations + if (reference.entry instanceof ClassEntry) + node = this.controller.getClassImplementations((ClassEntry) reference.entry); + else // get the method implementations + if (reference.entry instanceof MethodEntry) + node = this.controller.getMethodImplementations((MethodEntry) reference.entry); + + if (node != null) { + // show the tree at the root + TreePath path = getPathToRoot(node); + implementationsTree.setModel(new DefaultTreeModel((TreeNode) path.getPathComponent(0))); + implementationsTree.expandPath(path); + implementationsTree.setSelectionRow(implementationsTree.getRowForPath(path)); + } + + tabs.setSelectedIndex(1); + redraw(); + } + + public void showCalls() { + if (reference == null) { + return; + } + + if (reference.entry instanceof ClassEntry) { + // look for calls to the default constructor + // TODO: get a list of all the constructors and find calls to all of them + BehaviorReferenceTreeNode node = this.controller.getMethodReferences(new ConstructorEntry((ClassEntry) reference.entry, new Signature("()V"))); + callsTree.setModel(new DefaultTreeModel(node)); + } else if (reference.entry instanceof FieldEntry) { + FieldReferenceTreeNode node = this.controller.getFieldReferences((FieldEntry) reference.entry); + callsTree.setModel(new DefaultTreeModel(node)); + } else if (reference.entry instanceof MethodEntry) { + BehaviorReferenceTreeNode node = this.controller.getMethodReferences((MethodEntry) reference.entry); + callsTree.setModel(new DefaultTreeModel(node)); + } else if (reference.entry instanceof ConstructorEntry) { + BehaviorReferenceTreeNode node = this.controller.getMethodReferences((ConstructorEntry) reference.entry); + callsTree.setModel(new DefaultTreeModel(node)); + } + + tabs.setSelectedIndex(2); + redraw(); + } + + public void toggleMapping() { + if (this.controller.entryHasDeobfuscatedName(reference.entry)) { + this.controller.removeMapping(reference); + } else { + this.controller.markAsDeobfuscated(reference); + } + } + + private TreePath getPathToRoot(TreeNode node) { + List nodes = Lists.newArrayList(); + TreeNode n = node; + do { + nodes.add(n); + n = n.getParent(); + } while (n != null); + Collections.reverse(nodes); + return new TreePath(nodes.toArray()); + } + + public void showDiscardDiag(Function callback, String... options) { + int response = JOptionPane.showOptionDialog(this.frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, options[2]); + callback.apply(response); + } + + public void saveMapping() throws IOException { + if (this.enigmaMappingsFileChooser.getSelectedFile() != null || this.enigmaMappingsFileChooser.showSaveDialog(this.frame) == JFileChooser.APPROVE_OPTION) + this.controller.saveMappings(this.enigmaMappingsFileChooser.getSelectedFile()); + } + + public void close() { + if (!this.controller.isDirty()) { + // everything is saved, we can exit safely + this.frame.dispose(); + System.exit(0); + } else { + // ask to save before closing + showDiscardDiag((response) -> { + if (response == JOptionPane.YES_OPTION) { + try { + this.saveMapping(); + this.frame.dispose(); + + } catch (IOException ex) { + throw new Error(ex); + } + } else if (response == JOptionPane.NO_OPTION) + this.frame.dispose(); + + return null; + }, "Save and exit", "Discard changes", "Cancel"); + } + } + + public void redraw() { + this.frame.validate(); + this.frame.repaint(); + } + + public void onPanelRename(Object prevData, Object data, DefaultMutableTreeNode node) throws IllegalNameException { + // package rename + if (data instanceof String) { + for (int i = 0; i < node.getChildCount(); i++) { + data = Descriptor.toJvmName((String) data); + DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) node.getChildAt(i); + ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); + ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); + this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getName()), dataChild.getName(), false, i + 1 == node.getChildCount()); + childNode.setUserObject(dataChild); + } + node.setUserObject(data); + // Ob package will never be modified, just reload deob view + this.deobfPanel.deobfClasses.reload(); + } + // class rename + else if (data instanceof ClassEntry) + this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getName()), ((ClassEntry) data).getName(), false, true); + } + + public void moveClassTree(EntryReference deobfReference, String newName) { + String oldEntry = deobfReference.entry.getClassEntry().getPackageName(); + String newEntry = new ClassEntry(Descriptor.toJvmName(newName)).getPackageName(); + moveClassTree(deobfReference, newName, oldEntry == null, + newEntry == null); + } + + public void moveClassTree(EntryReference deobfReference, String newName, boolean isOldOb, boolean isNewOb) { + ClassEntry oldEntry = deobfReference.entry.getClassEntry(); + ClassEntry newEntry = new ClassEntry(Descriptor.toJvmName(newName)); + + // Ob -> deob + if (isOldOb && !isNewOb) { + this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, obfPanel.obfClasses); + ClassSelectorPackageNode packageNode = this.obfPanel.obfClasses.getPackageNode(oldEntry); + this.obfPanel.obfClasses.removeNode(packageNode, oldEntry); + this.obfPanel.obfClasses.removeNodeIfEmpty(packageNode); + this.deobfPanel.deobfClasses.reload(); + this.obfPanel.obfClasses.reload(); + } + // Deob -> ob + else if (isNewOb && !isOldOb) { + this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, deobfPanel.deobfClasses); + ClassSelectorPackageNode packageNode = this.deobfPanel.deobfClasses.getPackageNode(oldEntry); + this.deobfPanel.deobfClasses.removeNode(packageNode, oldEntry); + this.deobfPanel.deobfClasses.removeNodeIfEmpty(packageNode); + this.deobfPanel.deobfClasses.reload(); + this.obfPanel.obfClasses.reload(); + } + // Local move + else if (isOldOb) { + this.obfPanel.obfClasses.moveClassTree(oldEntry, newEntry, null); + this.obfPanel.obfClasses.removeNodeIfEmpty(this.obfPanel.obfClasses.getPackageNode(oldEntry)); + this.obfPanel.obfClasses.reload(); + } else { + this.deobfPanel.deobfClasses.moveClassTree(oldEntry, newEntry, null); + this.deobfPanel.deobfClasses.removeNodeIfEmpty(this.deobfPanel.deobfClasses.getPackageNode(oldEntry)); + this.deobfPanel.deobfClasses.reload(); + } + } } -- cgit v1.2.3