From e756324245cac09db300c967c30dcd17e9d78ef3 Mon Sep 17 00:00:00 2001 From: Cody <6558800+Bl3nd@users.noreply.github.com> Date: Mon, 16 Sep 2024 18:22:15 -0600 Subject: [PATCH] [Go-To] Move caret to requested field. - Also added enum constant declarations to the token parser. --- .../components/SearchableRSyntaxTextArea.java | 22 +- .../gui/components/actions/GoToAction.java | 97 ++- .../gui/resourceviewer/Workspace.java | 7 +- .../resourceviewer/viewer/ClassViewer.java | 617 +++++++++--------- .../gui/util/BytecodeViewPanelUpdater.java | 42 +- .../classcontainer/ClassFileContainer.java | 4 + .../classcontainer/parser/MyVoidVisitor.java | 27 + 7 files changed, 478 insertions(+), 338 deletions(-) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java index 813ffd66..f093b811 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/SearchableRSyntaxTextArea.java @@ -30,9 +30,7 @@ import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils; import javax.swing.*; import javax.swing.text.BadLocationException; import java.awt.*; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.MouseWheelEvent; +import java.awt.event.*; /** * Searching on an RSyntaxTextArea using swing highlighting @@ -87,6 +85,24 @@ public class SearchableRSyntaxTextArea extends RSyntaxTextArea GlobalHotKeys.keyPressed(keyEvent); })); + setCursor(new Cursor(Cursor.TEXT_CURSOR)); + getCaret().setBlinkRate(0); + getCaret().setVisible(true); + addFocusListener(new FocusAdapter() + { + @Override + public void focusGained(FocusEvent e) + { + getCaret().setVisible(true); + } + + @Override + public void focusLost(FocusEvent e) + { + getCaret().setVisible(true); + } + }); + final Font newFont = getFont().deriveFont((float) BytecodeViewer.viewer.getFontSize()); //set number-bar font diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java index 07cb9e33..8fafc209 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/components/actions/GoToAction.java @@ -1,17 +1,20 @@ package the.bytecode.club.bytecodeviewer.gui.components.actions; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; -import org.fife.ui.rsyntaxtextarea.Token; -import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel; +import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer; +import the.bytecode.club.bytecodeviewer.gui.util.BytecodeViewPanelUpdater; import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; import the.bytecode.club.bytecodeviewer.resources.classcontainer.locations.ClassFieldLocation; -import the.bytecode.club.bytecodeviewer.resources.classcontainer.parser.TokenUtil; import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; import javax.swing.text.Element; import java.awt.event.ActionEvent; +import java.util.HashMap; /** * Created by Bl3nd. @@ -36,20 +39,14 @@ public class GoToAction extends AbstractAction container.fieldMembers.values().forEach(fields -> fields.forEach(field -> { if (field.line == line && field.columnStart - 1 <= column && field.columnEnd >= column) { + Element root = textArea.getDocument().getDefaultRootElement(); // Open the class that is associated with the field's owner. if (!field.owner.equals(container.getName())) { - ResourceContainer resourceContainer = BytecodeViewer.getFileContainer(container.getParentContainer()); - if (resourceContainer != null) - { - String s = container.getImport(field.owner); - BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, s + ".class"); - } - + openFieldClass(field, textArea); return; } - Element root = textArea.getDocument().getDefaultRootElement(); ClassFieldLocation first = fields.get(0); int startOffset = root.getElement(first.line - 1).getStartOffset() + (first.columnStart - 1); textArea.setCaretPosition(startOffset); @@ -100,4 +97,82 @@ public class GoToAction extends AbstractAction } })); } + + private void openFieldClass(ClassFieldLocation field, RSyntaxTextArea textArea) + { + String token = textArea.modelToToken(textArea.getCaretPosition()).getLexeme(); + ResourceContainer resourceContainer = BytecodeViewer.getFileContainer(container.getParentContainer()); + if (resourceContainer != null) + { + String s = container.getImport(field.owner); + BytecodeViewer.viewer.workPane.addClassResource(resourceContainer, s + ".class"); + ClassViewer activeResource = (ClassViewer) BytecodeViewer.viewer.workPane.getActiveResource(); + HashMap classFiles = BytecodeViewer.viewer.workPane.classFiles; + Thread thread = new Thread(() -> { + try + { + BytecodeViewer.updateBusyStatus(true); + Thread.sleep(1000); + } catch (InterruptedException e) + { + throw new RuntimeException(e); + } finally + { + BytecodeViewer.updateBusyStatus(false); + } + + String s2 = activeResource.resource.workingName + "-" + this.container.getDecompiler(); + ClassFileContainer classFileContainer = classFiles.get(s2); + classFileContainer.fieldMembers.forEach((field1, field2) -> { + if (field1.equals(token)) + { + field2.forEach(classFieldLocation -> { + if (classFieldLocation.type.equals("declaration")) + { + for (int i = 0; i < 3; i++) + { + BytecodeViewPanel panel = activeResource.getPanel(i); + if (panel.textArea != null) + { + if (panel.decompiler.getDecompilerName().equals(this.container.getDecompiler())) + { + Element root = panel.textArea.getDocument().getDefaultRootElement(); + int startOffset = root.getElement(classFieldLocation.line - 1).getStartOffset() + (classFieldLocation.columnStart - 1); + panel.textArea.setCaretPosition(startOffset); + for (CaretListener caretListener : panel.textArea.getCaretListeners()) + { + if (caretListener instanceof BytecodeViewPanelUpdater.MarkerCaretListener) + { + BytecodeViewPanelUpdater.MarkerCaretListener markerCaretListener = (BytecodeViewPanelUpdater.MarkerCaretListener) caretListener; + markerCaretListener.caretUpdate(new CaretEvent(panel.textArea) + { + @Override + public int getDot() + { + return panel.textArea.getCaret().getDot(); + } + + @Override + public int getMark() + { + return 0; + } + }); + } + } + + panel.textArea.requestFocusInWindow(); + + break; + } + } + } + } + }); + } + }); + }); + thread.start(); + } + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/Workspace.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/Workspace.java index b25c3576..c513ab1b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/Workspace.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/Workspace.java @@ -24,6 +24,7 @@ import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer; import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.FileViewer; import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer; import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; +import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; import the.bytecode.club.bytecodeviewer.translation.TranslatedComponents; import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings; import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJButton; @@ -33,13 +34,10 @@ import the.bytecode.club.uikit.tabpopup.closer.PopupMenuTabsCloseConfiguration; import javax.swing.*; import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; +import java.util.HashMap; import java.util.HashSet; import java.util.Set; -import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU; - /** * This pane contains all the resources, as tabs. * @@ -54,6 +52,7 @@ public class Workspace extends TranslatedVisibleComponent { public final JPanel buttonPanel; public final JButton refreshClass; public final Set openedTabs = new HashSet<>(); + public HashMap classFiles = new HashMap<>(); public Workspace() { super("Workspace", TranslatedComponents.WORK_SPACE); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/viewer/ClassViewer.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/viewer/ClassViewer.java index 23c467d7..bd2d9115 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/viewer/ClassViewer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/viewer/ClassViewer.java @@ -27,7 +27,6 @@ import the.bytecode.club.bytecodeviewer.decompilers.Decompiler; import the.bytecode.club.bytecodeviewer.gui.resourceviewer.BytecodeViewPanel; import the.bytecode.club.bytecodeviewer.resources.Resource; import the.bytecode.club.bytecodeviewer.resources.ResourceContainer; -import the.bytecode.club.bytecodeviewer.resources.classcontainer.ClassFileContainer; import the.bytecode.club.bytecodeviewer.util.MethodParser; import javax.swing.*; @@ -38,7 +37,6 @@ import java.awt.event.ComponentEvent; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method; @@ -53,307 +51,316 @@ import static the.bytecode.club.bytecodeviewer.util.MethodParser.Method; public class ClassViewer extends ResourceViewer { - public JSplitPane sp; - public JSplitPane sp2; - public BytecodeViewPanel bytecodeViewPanel1 = new BytecodeViewPanel(0, this); - public BytecodeViewPanel bytecodeViewPanel2 = new BytecodeViewPanel(1, this); - public BytecodeViewPanel bytecodeViewPanel3 = new BytecodeViewPanel(2, this); - public List methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser()); - public HashMap classFiles = new HashMap<>(); - - public ClassViewer(ResourceContainer container, String name) - { - super(new Resource(name, container.getWorkingName(name), container)); - - this.setName(name); - this.setLayout(new BorderLayout()); - this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bytecodeViewPanel1, bytecodeViewPanel2); - this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, bytecodeViewPanel3); - this.add(sp2, BorderLayout.CENTER); - - this.addComponentListener(new ComponentAdapter() - { - @Override - public void componentResized(ComponentEvent e) - { - resetDivider(); - } - }); - } - - @Override - public void refresh(JButton button) - { - setPanes(); - refreshTitle(); - - bytecodeViewPanel1.createPane(this); - bytecodeViewPanel2.createPane(this); - bytecodeViewPanel3.createPane(this); - - byte[] classBytes = getResourceBytes(); - - //TODO remove this once all of the importers have been properly updated to use a FileContainerImporter - if (classBytes == null || classBytes.length == 0 || Configuration.forceResourceUpdateFromClassNode) - { - //TODO remove this error message when all of the importers have been updated - // only APK and DEX are left - if (!Configuration.forceResourceUpdateFromClassNode) - { - System.err.println("WARNING: Class Resource imported using the old importer!"); - System.err.println("TODO: Update it to use the FileContainerImporter"); - } - - classBytes = ASMUtil.nodeToBytes(resource.getResourceClassNode()); - } - - bytecodeViewPanel1.updatePane(this, classBytes, button, isPanel1Editable()); - bytecodeViewPanel2.updatePane(this, classBytes, button, isPanel2Editable()); - bytecodeViewPanel3.updatePane(this, classBytes, button, isPanel3Editable()); - - Thread dumpBuild = new Thread(() -> - { - BytecodeViewer.updateBusyStatus(true); - - while (Configuration.currentlyDumping) - { - //wait until it's not dumping - try - { - Thread.sleep(100); - } - catch (InterruptedException e) - { - e.printStackTrace(); - } - } - - BytecodeViewer.updateBusyStatus(false); - - if (bytecodeViewPanel1.decompiler != Decompiler.NONE) - bytecodeViewPanel1.updateThread.startNewThread(); - - if (bytecodeViewPanel2.decompiler != Decompiler.NONE) - bytecodeViewPanel2.updateThread.startNewThread(); - - if (bytecodeViewPanel3.decompiler != Decompiler.NONE) - bytecodeViewPanel3.updateThread.startNewThread(); - }, "ClassViewer Temp Dump"); - - dumpBuild.start(); - - if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable()) - { - if (Configuration.warnForEditing) - return; - - Configuration.warnForEditing = true; - - if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected() && !BytecodeViewer.viewer.compileOnSave.isSelected()) - { - BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to " + "test or export your changes.\nYou can set compile automatically on refresh or on save " + "in the settings menu."); - - SettingsSerializer.saveSettingsAsync(); - } - } - } - - public void setPanes() - { - bytecodeViewPanel1.decompiler = BytecodeViewer.viewer.viewPane1.getSelectedDecompiler(); - bytecodeViewPanel2.decompiler = BytecodeViewer.viewer.viewPane2.getSelectedDecompiler(); - bytecodeViewPanel3.decompiler = BytecodeViewer.viewer.viewPane3.getSelectedDecompiler(); - } - - public boolean isPanel1Editable() - { - setPanes(); - return BytecodeViewer.viewer.viewPane1.isPaneEditable(); - } - - public boolean isPanel2Editable() - { - setPanes(); - return BytecodeViewer.viewer.viewPane2.isPaneEditable(); - } - - public boolean isPanel3Editable() - { - setPanes(); - return BytecodeViewer.viewer.viewPane3.isPaneEditable(); - } - - - public static void selectMethod(RSyntaxTextArea area, int methodLine) - { - if (methodLine != area.getCaretLineNumber()) - { - setCaretLine(area, methodLine); - setViewLine(area, methodLine); - } - } - - public static void selectMethod(ClassViewer classViewer, int paneId, Method method) - { - RSyntaxTextArea area = null; - switch (paneId) - { - case 0: - area = classViewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; - break; - - case 1: - area = classViewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; - break; - - case 2: - area = classViewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; - break; - } - - if (area != null) - { - MethodParser methods = classViewer.methods.get(paneId); - if (methods != null) - { - int methodLine = methods.findMethod(method); - - if (methodLine != -1) - selectMethod(area, methodLine); - } - } - } - - public static int getMaxViewLine(RSyntaxTextArea area) - { - Container parent = area.getParent(); - - if (parent instanceof JViewport) - { - JViewport viewport = (JViewport) parent; - int y = viewport.getViewSize().height - viewport.getExtentSize().height; - int lineHeight = area.getLineHeight(); - return y >= lineHeight ? y / lineHeight : 0; - } - - return 0; - } - - public static int getViewLine(RSyntaxTextArea area) - { - Container parent = area.getParent(); - - if (parent instanceof JViewport) - { - JViewport viewport = (JViewport) parent; - Point point = viewport.getViewPosition(); - int lineHeight = area.getLineHeight(); - return point.y >= lineHeight ? point.y / lineHeight : 0; - } - - return 0; - } - - public static void setViewLine(RSyntaxTextArea area, int line) - { - Container parent = area.getParent(); - - if (parent instanceof JViewport) - { - JViewport viewport = (JViewport) parent; - int maxLine = ClassViewer.getMaxViewLine(area); - line = Math.min(line, maxLine); - viewport.setViewPosition(new Point(0, line * area.getLineHeight())); - } - } - - public static void setCaretLine(RSyntaxTextArea area, int line) - { - try - { - area.setCaretPosition(area.getLineStartOffset(line)); - } - catch (BadLocationException ignored) - { - } - } - - public void resetDivider() - { - SwingUtilities.invokeLater(() -> - { - sp.setResizeWeight(0.5); - - if (bytecodeViewPanel2.decompiler != Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) - setDividerLocation(sp, 0.5); - else if (bytecodeViewPanel1.decompiler != Decompiler.NONE) - setDividerLocation(sp, 1); - else if (bytecodeViewPanel2.decompiler != Decompiler.NONE) - { - sp.setResizeWeight(1); - setDividerLocation(sp, 0); - } - else - setDividerLocation(sp, 0); - - if (bytecodeViewPanel3.decompiler != Decompiler.NONE) - { - sp2.setResizeWeight(0.7); - setDividerLocation(sp2, 0.7); - if ((bytecodeViewPanel2.decompiler == Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) || (bytecodeViewPanel1.decompiler == Decompiler.NONE && bytecodeViewPanel2.decompiler != Decompiler.NONE)) - setDividerLocation(sp2, 0.5); - else if (bytecodeViewPanel1.decompiler == Decompiler.NONE) - setDividerLocation(sp2, 0); - } - else - { - sp.setResizeWeight(1); - sp2.setResizeWeight(0); - setDividerLocation(sp2, 1); - } - }); - } - - /** - * Whoever wrote this function, THANK YOU! - */ - public static JSplitPane setDividerLocation(JSplitPane splitter, double proportion) - { - if (splitter.isShowing()) - { - if (splitter.getWidth() > 0 && splitter.getHeight() > 0) - splitter.setDividerLocation(proportion); - else - { - splitter.addComponentListener(new ComponentAdapter() - { - @Override - public void componentResized(ComponentEvent ce) - { - splitter.removeComponentListener(this); - setDividerLocation(splitter, proportion); - } - }); - } - } - else - { - splitter.addHierarchyListener(new HierarchyListener() - { - @Override - public void hierarchyChanged(HierarchyEvent e) - { - if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && splitter.isShowing()) - { - splitter.removeHierarchyListener(this); - setDividerLocation(splitter, proportion); - } - } - }); - } - - return splitter; - } - - private static final long serialVersionUID = -8650495368920680024L; + public JSplitPane sp; + public JSplitPane sp2; + public BytecodeViewPanel bytecodeViewPanel1 = new BytecodeViewPanel(0, this); + public BytecodeViewPanel bytecodeViewPanel2 = new BytecodeViewPanel(1, this); + public BytecodeViewPanel bytecodeViewPanel3 = new BytecodeViewPanel(2, this); + public List methods = Arrays.asList(new MethodParser(), new MethodParser(), new MethodParser()); + + public ClassViewer(ResourceContainer container, String name) + { + super(new Resource(name, container.getWorkingName(name), container)); + + this.setName(name); + this.setLayout(new BorderLayout()); + this.sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, bytecodeViewPanel1, bytecodeViewPanel2); + this.sp2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, sp, bytecodeViewPanel3); + this.add(sp2, BorderLayout.CENTER); + + this.addComponentListener(new ComponentAdapter() + { + @Override + public void componentResized(ComponentEvent e) + { + resetDivider(); + } + }); + } + + @Override + public void refresh(JButton button) + { + setPanes(); + refreshTitle(); + + bytecodeViewPanel1.createPane(this); + bytecodeViewPanel2.createPane(this); + bytecodeViewPanel3.createPane(this); + + byte[] classBytes = getResourceBytes(); + + //TODO remove this once all of the importers have been properly updated to use a FileContainerImporter + if (classBytes == null || classBytes.length == 0 || Configuration.forceResourceUpdateFromClassNode) + { + //TODO remove this error message when all of the importers have been updated + // only APK and DEX are left + if (!Configuration.forceResourceUpdateFromClassNode) + { + System.err.println("WARNING: Class Resource imported using the old importer!"); + System.err.println("TODO: Update it to use the FileContainerImporter"); + } + + classBytes = ASMUtil.nodeToBytes(resource.getResourceClassNode()); + } + + bytecodeViewPanel1.updatePane(this, classBytes, button, isPanel1Editable()); + bytecodeViewPanel2.updatePane(this, classBytes, button, isPanel2Editable()); + bytecodeViewPanel3.updatePane(this, classBytes, button, isPanel3Editable()); + + Thread dumpBuild = new Thread(() -> + { + BytecodeViewer.updateBusyStatus(true); + + while (Configuration.currentlyDumping) + { + //wait until it's not dumping + try + { + Thread.sleep(100); + } catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + BytecodeViewer.updateBusyStatus(false); + + if (bytecodeViewPanel1.decompiler != Decompiler.NONE) + bytecodeViewPanel1.updateThread.startNewThread(); + + if (bytecodeViewPanel2.decompiler != Decompiler.NONE) + bytecodeViewPanel2.updateThread.startNewThread(); + + if (bytecodeViewPanel3.decompiler != Decompiler.NONE) + bytecodeViewPanel3.updateThread.startNewThread(); + }, "ClassViewer Temp Dump"); + + dumpBuild.start(); + + if (isPanel1Editable() || isPanel2Editable() || isPanel3Editable()) + { + if (Configuration.warnForEditing) + return; + + Configuration.warnForEditing = true; + + if (!BytecodeViewer.viewer.autoCompileOnRefresh.isSelected() && !BytecodeViewer.viewer.compileOnSave.isSelected()) + { + BytecodeViewer.showMessage("Make sure to compile (File>Compile or Ctrl + T) whenever you want to " + "test or export your changes.\nYou can set compile automatically on refresh or on save " + "in the settings menu."); + + SettingsSerializer.saveSettingsAsync(); + } + } + } + + public void setPanes() + { + bytecodeViewPanel1.decompiler = BytecodeViewer.viewer.viewPane1.getSelectedDecompiler(); + bytecodeViewPanel2.decompiler = BytecodeViewer.viewer.viewPane2.getSelectedDecompiler(); + bytecodeViewPanel3.decompiler = BytecodeViewer.viewer.viewPane3.getSelectedDecompiler(); + } + + public boolean isPanel1Editable() + { + setPanes(); + return BytecodeViewer.viewer.viewPane1.isPaneEditable(); + } + + public boolean isPanel2Editable() + { + setPanes(); + return BytecodeViewer.viewer.viewPane2.isPaneEditable(); + } + + public boolean isPanel3Editable() + { + setPanes(); + return BytecodeViewer.viewer.viewPane3.isPaneEditable(); + } + + public BytecodeViewPanel getPanel(int index) + { + switch (index) + { + case 0: + return bytecodeViewPanel1; + case 1: + return bytecodeViewPanel2; + case 2: + return bytecodeViewPanel3; + } + + return null; + } + + + public static void selectMethod(RSyntaxTextArea area, int methodLine) + { + if (methodLine != area.getCaretLineNumber()) + { + setCaretLine(area, methodLine); + setViewLine(area, methodLine); + } + } + + public static void selectMethod(ClassViewer classViewer, int paneId, Method method) + { + RSyntaxTextArea area = null; + switch (paneId) + { + case 0: + area = classViewer.bytecodeViewPanel1.updateThread.updateUpdaterTextArea; + break; + + case 1: + area = classViewer.bytecodeViewPanel2.updateThread.updateUpdaterTextArea; + break; + + case 2: + area = classViewer.bytecodeViewPanel3.updateThread.updateUpdaterTextArea; + break; + } + + if (area != null) + { + MethodParser methods = classViewer.methods.get(paneId); + if (methods != null) + { + int methodLine = methods.findMethod(method); + + if (methodLine != -1) + selectMethod(area, methodLine); + } + } + } + + public static int getMaxViewLine(RSyntaxTextArea area) + { + Container parent = area.getParent(); + + if (parent instanceof JViewport) + { + JViewport viewport = (JViewport) parent; + int y = viewport.getViewSize().height - viewport.getExtentSize().height; + int lineHeight = area.getLineHeight(); + return y >= lineHeight ? y / lineHeight : 0; + } + + return 0; + } + + public static int getViewLine(RSyntaxTextArea area) + { + Container parent = area.getParent(); + + if (parent instanceof JViewport) + { + JViewport viewport = (JViewport) parent; + Point point = viewport.getViewPosition(); + int lineHeight = area.getLineHeight(); + return point.y >= lineHeight ? point.y / lineHeight : 0; + } + + return 0; + } + + public static void setViewLine(RSyntaxTextArea area, int line) + { + Container parent = area.getParent(); + + if (parent instanceof JViewport) + { + JViewport viewport = (JViewport) parent; + int maxLine = ClassViewer.getMaxViewLine(area); + line = Math.min(line, maxLine); + viewport.setViewPosition(new Point(0, line * area.getLineHeight())); + } + } + + public static void setCaretLine(RSyntaxTextArea area, int line) + { + try + { + area.setCaretPosition(area.getLineStartOffset(line)); + } catch (BadLocationException ignored) + { + } + } + + public void resetDivider() + { + SwingUtilities.invokeLater(() -> + { + sp.setResizeWeight(0.5); + + if (bytecodeViewPanel2.decompiler != Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) + setDividerLocation(sp, 0.5); + else if (bytecodeViewPanel1.decompiler != Decompiler.NONE) + setDividerLocation(sp, 1); + else if (bytecodeViewPanel2.decompiler != Decompiler.NONE) + { + sp.setResizeWeight(1); + setDividerLocation(sp, 0); + } else + setDividerLocation(sp, 0); + + if (bytecodeViewPanel3.decompiler != Decompiler.NONE) + { + sp2.setResizeWeight(0.7); + setDividerLocation(sp2, 0.7); + if ((bytecodeViewPanel2.decompiler == Decompiler.NONE && bytecodeViewPanel1.decompiler != Decompiler.NONE) || (bytecodeViewPanel1.decompiler == Decompiler.NONE && bytecodeViewPanel2.decompiler != Decompiler.NONE)) + setDividerLocation(sp2, 0.5); + else if (bytecodeViewPanel1.decompiler == Decompiler.NONE) + setDividerLocation(sp2, 0); + } else + { + sp.setResizeWeight(1); + sp2.setResizeWeight(0); + setDividerLocation(sp2, 1); + } + }); + } + + /** + * Whoever wrote this function, THANK YOU! + */ + public static JSplitPane setDividerLocation(JSplitPane splitter, double proportion) + { + if (splitter.isShowing()) + { + if (splitter.getWidth() > 0 && splitter.getHeight() > 0) + splitter.setDividerLocation(proportion); + else + { + splitter.addComponentListener(new ComponentAdapter() + { + @Override + public void componentResized(ComponentEvent ce) + { + splitter.removeComponentListener(this); + setDividerLocation(splitter, proportion); + } + }); + } + } else + { + splitter.addHierarchyListener(new HierarchyListener() + { + @Override + public void hierarchyChanged(HierarchyEvent e) + { + if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && splitter.isShowing()) + { + splitter.removeHierarchyListener(this); + setDividerLocation(splitter, proportion); + } + } + }); + } + + return splitter; + } + + private static final long serialVersionUID = -8650495368920680024L; } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java index b22fd95b..2aaa80e9 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/util/BytecodeViewPanelUpdater.java @@ -72,6 +72,8 @@ public class BytecodeViewPanelUpdater implements Runnable private final JButton button; private final byte[] classBytes; + public MarkerCaretListener markerCaretListener; + private MyErrorStripe errorStripe; public SearchableRSyntaxTextArea updateUpdaterTextArea; public JComboBox methodsList; public boolean isPanelEditable; @@ -118,7 +120,7 @@ public class BytecodeViewPanelUpdater implements Runnable if (!container.hasBeenParsed) { container.parse(); - viewer.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container); + BytecodeViewer.viewer.workPane.classFiles.put(viewer.resource.workingName + "-" + decompiler.getDecompilerName(), container); container.hasBeenParsed = true; } @@ -399,9 +401,6 @@ public class BytecodeViewPanelUpdater implements Runnable bytecodeViewPanel.textArea = updateUpdaterTextArea; bytecodeViewPanel.textArea.setMarkOccurrencesColor(Color.ORANGE); - bytecodeViewPanel.textArea.setCursor(new Cursor(Cursor.TEXT_CURSOR)); - bytecodeViewPanel.textArea.getCaret().setVisible(true); - bytecodeViewPanel.textArea.getCaret().setBlinkRate(0); bytecodeViewPanel.textArea.setHighlighter(new RSyntaxTextAreaHighlighterEx()); if (bytecodeViewPanel.decompiler != Decompiler.BYTECODE_DISASSEMBLER) @@ -426,26 +425,19 @@ public class BytecodeViewPanelUpdater implements Runnable String editable = isPanelEditable ? " - " + EDITABLE : ""; bytecodeViewPanel.textArea.getTextAreaSearchPanel().getTitleHeader().setText(decompiler.getDecompilerName() + editable); - MyErrorStripe errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea); + errorStripe = new MyErrorStripe(bytecodeViewPanel.textArea); bytecodeViewPanel.add(errorStripe, BorderLayout.LINE_END); bytecodeViewPanel.revalidate(); bytecodeViewPanel.repaint(); String classContainerName = viewer.resource.workingName + "-" + decompiler.getDecompilerName(); - ClassFileContainer classFileContainer = viewer.classFiles.get(classContainerName); + ClassFileContainer classFileContainer = BytecodeViewer.viewer.workPane.classFiles.get(classContainerName); bytecodeViewPanel.textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_DOWN_MASK), "goToAction"); bytecodeViewPanel.textArea.getActionMap().put("goToAction", new GoToAction(classFileContainer)); - bytecodeViewPanel.textArea.addCaretListener(e -> { - if (bytecodeViewPanel.textArea.isFocusOwner()) - { - RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter(); - highlighterEx.clearMarkOccurrencesHighlights(); - RSyntaxTextArea textArea = (RSyntaxTextArea) e.getSource(); - markOccurrences(textArea, viewer.classFiles.get(classContainerName), errorStripe); - } - }); + markerCaretListener = new MarkerCaretListener(classContainerName); + bytecodeViewPanel.textArea.addCaretListener(markerCaretListener); } private void markOccurrences(RSyntaxTextArea textArea, ClassFileContainer classFileContainer, MyErrorStripe errorStripe) @@ -627,4 +619,24 @@ public class BytecodeViewPanelUpdater implements Runnable } })); } + + public class MarkerCaretListener implements CaretListener + { + + private final String classContainerName; + + public MarkerCaretListener(String classContainerName) + { + this.classContainerName = classContainerName; + } + + @Override + public void caretUpdate(CaretEvent e) + { + SearchableRSyntaxTextArea textArea = (SearchableRSyntaxTextArea) e.getSource(); + RSyntaxTextAreaHighlighterEx highlighterEx = (RSyntaxTextAreaHighlighterEx) bytecodeViewPanel.textArea.getHighlighter(); + highlighterEx.clearMarkOccurrencesHighlights(); + markOccurrences(textArea, BytecodeViewer.viewer.workPane.classFiles.get(classContainerName), errorStripe); + } + } } diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java index 6a9d338b..7413dbd9 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/ClassFileContainer.java @@ -57,6 +57,10 @@ public class ClassFileContainer return this.className.substring(this.className.lastIndexOf('.') + 1); } + public String getDecompiler() { + return getName().substring(6); + } + public String getParentContainer() { return this.parentContainer; diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java index 2bc3cfee..04ccfb5e 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/resources/classcontainer/parser/MyVoidVisitor.java @@ -1312,6 +1312,12 @@ public class MyVoidVisitor extends VoidVisitorAdapter } } + /** + * Visit all {@link LambdaExpr}'s. + * + * @param n The current {@code LambdaExpr} + * @param arg Don't worry about it. + */ @Override public void visit(LambdaExpr n, Object arg) { @@ -1337,6 +1343,27 @@ public class MyVoidVisitor extends VoidVisitorAdapter } } + /** + * Visit all {@link EnumDeclaration}'s. + * + * @param n The current {@code EnumDeclaration} + * @param arg Don't worry about it. + */ + @Override + public void visit(EnumDeclaration n, Object arg) + { + super.visit(n, arg); + n.getEntries().forEach(entry -> { + SimpleName simpleName = entry.getName(); + String name = simpleName.getIdentifier(); + Range range = simpleName.getRange().get(); + int line = range.begin.line; + int columnStart = range.begin.column; + int columnEnd = range.end.column; + this.classFileContainer.putField(name, new ClassFieldLocation(getOwner(), "declaration", line, columnStart, columnEnd + 1)); + }); + } + /** * Look through the {@link CompilationUnit} for the specific statement within its methods. *