diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java b/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java index d904c3fd..acb00f73 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/BytecodeViewer.java @@ -24,7 +24,7 @@ import the.bytecode.club.bootloader.Boot; import the.bytecode.club.bytecodeviewer.api.ClassNodeLoader; import the.bytecode.club.bytecodeviewer.compilers.Compilers; import the.bytecode.club.bytecodeviewer.gui.ClassViewer; -import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane; +import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI; import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions; import the.bytecode.club.bytecodeviewer.gui.SearchBoxPane; @@ -104,7 +104,7 @@ public class BytecodeViewer public static Refactorer refactorer = new Refactorer(); public static List files = new ArrayList<>(); //all of BCV's loaded files/classes/etc public static List createdProcesses = new ArrayList<>(); - public static final boolean EXPERIMENTAL_DRAG_TABS = false; + public static final boolean EXPERIMENTAL_TAB_CODE = false; /** * The version checker thread diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java index 0828e3d3..b2112abf 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/MainViewerGUI.java @@ -32,7 +32,7 @@ import the.bytecode.club.bytecodeviewer.gui.extras.AboutWindow; import the.bytecode.club.bytecodeviewer.gui.extras.RunOptions; import the.bytecode.club.bytecodeviewer.gui.plugins.MaliciousCodeScannerOptions; import the.bytecode.club.bytecodeviewer.gui.plugins.ReplaceStringsOptions; -import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane; +import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameClasses; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameFields; import the.bytecode.club.bytecodeviewer.obfuscators.rename.RenameMethods; @@ -177,7 +177,7 @@ public class MainViewerGUI extends JFrame { public JCheckBoxMenuItem asc = new JCheckBoxMenuItem("Allow only ASCII characters in strings"); public JCheckBoxMenuItem ren = new JCheckBoxMenuItem("Rename ambiguous classes and class elements"); //Proycon - public final JMenu proyconSettingsSecondaryMenu = new JMenu("Procyon"); + public final JMenu procyonSettingsSecondaryMenu = new JMenu("Procyon"); public final JCheckBoxMenuItem alwaysGenerateExceptionVars = new JCheckBoxMenuItem("Always Generate Exception Variable For Catch Blocks"); public final JCheckBoxMenuItem excludeNestedTypes = new JCheckBoxMenuItem("Exclude Nested Types"); public final JCheckBoxMenuItem showDebugLineNumbers = new JCheckBoxMenuItem("Show Debug Line Numbers"); @@ -426,21 +426,21 @@ public class MainViewerGUI extends JFrame { visualSettings.add(showClassMethods); //PROCYON SETTINGS - settingsMainMenu.add(proyconSettingsSecondaryMenu); - proyconSettingsSecondaryMenu.add(alwaysGenerateExceptionVars); - proyconSettingsSecondaryMenu.add(excludeNestedTypes); - proyconSettingsSecondaryMenu.add(showDebugLineNumbers); - proyconSettingsSecondaryMenu.add(includeLineNumbersInBytecode); - proyconSettingsSecondaryMenu.add(includeErrorDiagnostics); - proyconSettingsSecondaryMenu.add(showSyntheticMembers); - proyconSettingsSecondaryMenu.add(simplifyMemberReferences); - proyconSettingsSecondaryMenu.add(mergeVariables); - proyconSettingsSecondaryMenu.add(forceExplicitTypeArguments); - proyconSettingsSecondaryMenu.add(forceExplicitImports); - proyconSettingsSecondaryMenu.add(flattenSwitchBlocks); - proyconSettingsSecondaryMenu.add(retainPointlessSwitches); - proyconSettingsSecondaryMenu.add(retainRedunantCasts); - proyconSettingsSecondaryMenu.add(unicodeOutputEnabled); + settingsMainMenu.add(procyonSettingsSecondaryMenu); + procyonSettingsSecondaryMenu.add(alwaysGenerateExceptionVars); + procyonSettingsSecondaryMenu.add(excludeNestedTypes); + procyonSettingsSecondaryMenu.add(showDebugLineNumbers); + procyonSettingsSecondaryMenu.add(includeLineNumbersInBytecode); + procyonSettingsSecondaryMenu.add(includeErrorDiagnostics); + procyonSettingsSecondaryMenu.add(showSyntheticMembers); + procyonSettingsSecondaryMenu.add(simplifyMemberReferences); + procyonSettingsSecondaryMenu.add(mergeVariables); + procyonSettingsSecondaryMenu.add(forceExplicitTypeArguments); + procyonSettingsSecondaryMenu.add(forceExplicitImports); + procyonSettingsSecondaryMenu.add(flattenSwitchBlocks); + procyonSettingsSecondaryMenu.add(retainPointlessSwitches); + procyonSettingsSecondaryMenu.add(retainRedunantCasts); + procyonSettingsSecondaryMenu.add(unicodeOutputEnabled); //CFR SETTINGS settingsMainMenu.add(cfrSettingsSecondaryMenu); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/SearchBoxPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/SearchBoxPane.java index 3b6b3b69..62a33cb9 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/SearchBoxPane.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/SearchBoxPane.java @@ -18,7 +18,7 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreePath; import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; -import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane; +import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.searching.BackgroundSearchThread; import the.bytecode.club.bytecodeviewer.searching.FieldCallSearch; import the.bytecode.club.bytecodeviewer.searching.LDCSearch; @@ -61,13 +61,13 @@ public class SearchBoxPane extends VisibleComponent { public static final SearchRadius[] SEARCH_RADII = SearchRadius.values(); public static final SearchType[] SEARCH_TYPES = SearchType.values(); - JCheckBox exact = new JCheckBox("Exact"); - DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results"); - JTree tree; - JComboBox typeBox; - - SearchType searchType = null; - JComboBox searchRadiusBox; + public final JCheckBox exact = new JCheckBox("Exact"); + public final DefaultMutableTreeNode treeRoot = new DefaultMutableTreeNode("Results"); + public final JTree tree; + public final JComboBox typeBox; + + public SearchType searchType = null; + public final JComboBox searchRadiusBox; public JButton search = new JButton("Search"); BackgroundSearchThread t = new BackgroundSearchThread(true) { @@ -75,7 +75,6 @@ public class SearchBoxPane extends VisibleComponent { public void doSearch() { // empty } - }; @SuppressWarnings("unchecked") @@ -83,9 +82,7 @@ public class SearchBoxPane extends VisibleComponent { super("Search"); final JPanel optionPanel = new JPanel(new BorderLayout()); - final JPanel searchRadiusOpt = new JPanel(new BorderLayout()); - final JPanel searchOpts = new JPanel(new GridLayout(2, 1)); searchRadiusOpt.add(new JLabel("Search from "), BorderLayout.WEST); @@ -96,15 +93,12 @@ public class SearchBoxPane extends VisibleComponent { } searchRadiusBox = new JComboBox(model); - searchRadiusOpt.add(searchRadiusBox, BorderLayout.CENTER); - searchOpts.add(searchRadiusOpt); model = new DefaultComboBoxModel(); - for (final SearchType st : SEARCH_TYPES) { + for (final SearchType st : SEARCH_TYPES) model.addElement(st); - } typeBox = new JComboBox(model); final JPanel searchOptPanel = new JPanel(); @@ -160,8 +154,7 @@ public class SearchBoxPane extends VisibleComponent { final ClassNode fN = Objects.requireNonNull(container).getClassNode(className); if (fN != null) { - Objects.requireNonNull(MainViewerGUI.getComponent(ResourceListPane.class)) - .openClassFileToWorkSpace(container, className + ".class", fN); + BytecodeViewer.viewer.openClassFile(container, className + ".class", fN); } }); diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java index 1234cfb7..b15a9b4b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/TabbedPane.java @@ -89,18 +89,6 @@ public class TabbedPane extends JPanel { } }; - this.addMouseListener(new MouseAdapter() { - @Override - public void mouseReleased(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON2) { - final int i = pane.indexOfTabComponent(TabbedPane.this); - if (i != -1) { - pane.remove(i); - } - } - } - }); - this.add(label); // add more space between the label and the button label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5)); @@ -167,7 +155,8 @@ public class TabbedPane extends JPanel { } }); - if(BytecodeViewer.EXPERIMENTAL_DRAG_TABS) + //tab dragging + if(BytecodeViewer.EXPERIMENTAL_TAB_CODE) { /*label.addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) {} @@ -197,6 +186,22 @@ public class TabbedPane extends JPanel { } }); } + + //middle click close + if(BytecodeViewer.EXPERIMENTAL_TAB_CODE) + { + this.addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON2) { + final int i = pane.indexOfTabComponent(TabbedPane.this); + if (i != -1) { + pane.remove(i); + } + } + } + }); + } } private void stopDragging(int mouseX, int mouseY) { diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/extras/StringMetricsUtil.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/extras/StringMetricsUtil.java new file mode 100644 index 00000000..dd642aae --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/extras/StringMetricsUtil.java @@ -0,0 +1,38 @@ +package the.bytecode.club.bytecodeviewer.gui.extras; + +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.geom.Rectangle2D; + +/** + * @author http://stackoverflow.com/a/18450804 + */ +public class StringMetricsUtil +{ + Font font; + FontRenderContext context; + + public StringMetricsUtil(Graphics2D g2) + { + font = g2.getFont(); + context = g2.getFontRenderContext(); + } + + public Rectangle2D getBounds(String message) + { + return font.getStringBounds(message, context); + } + + public double getWidth(String message) + { + Rectangle2D bounds = getBounds(message); + return bounds.getWidth(); + } + + public double getHeight(String message) + { + Rectangle2D bounds = getBounds(message); + return bounds.getHeight(); + } + +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ImageRenderer.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ImageRenderer.java similarity index 94% rename from src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ImageRenderer.java rename to src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ImageRenderer.java index 35c87e2e..c9c18fc0 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ImageRenderer.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ImageRenderer.java @@ -1,4 +1,4 @@ -package the.bytecode.club.bytecodeviewer.gui.resourceviewer; +package the.bytecode.club.bytecodeviewer.gui.resourcelist; import the.bytecode.club.bytecodeviewer.Resources; @@ -24,9 +24,9 @@ public class ImageRenderer extends DefaultTreeCellRenderer Component ret = super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); - if (value instanceof ResourceListPane.MyTreeNode) + if (value instanceof ResourceTreeNode) { - ResourceListPane.MyTreeNode node = (ResourceListPane.MyTreeNode) value; + ResourceTreeNode node = (ResourceTreeNode) value; String name = node.toString().toLowerCase(); if (name.endsWith(".jar") || name.endsWith(".war")) diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java new file mode 100644 index 00000000..b4f41128 --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListPane.java @@ -0,0 +1,435 @@ +package the.bytecode.club.bytecodeviewer.gui.resourcelist; + +//TODO fix for Java 9+ +import com.sun.java.swing.plaf.windows.WindowsTreeUI; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.Enumeration; +import java.util.Map.Entry; +import java.util.Objects; +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import org.objectweb.asm.tree.ClassNode; +import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.gui.VisibleComponent; +import the.bytecode.club.bytecodeviewer.util.FileContainer; +import the.bytecode.club.bytecodeviewer.util.FileDrop; +import the.bytecode.club.bytecodeviewer.util.LazyNameUtil; + +/*************************************************************************** + * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * + * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ***************************************************************************/ + +/** + * The file navigation pane. + * + * @author Konloch + * @author WaterWolf + * @author afffsdd + */ + +public class ResourceListPane extends VisibleComponent implements FileDrop.Listener +{ + public final JPopupMenu rightClickMenu = new JPopupMenu(); + public final JCheckBox exact = new JCheckBox("Exact"); + public final JButton open = new JButton("+"); + public final JButton close = new JButton("-"); + public final ResourceTreeNode treeRoot = new ResourceTreeNode("Loaded Files:"); + public final ResourceTree tree = new ResourceTree(treeRoot); + public final String quickSearchText = "Quick file search (no file extension)"; + public final JTextField quickSearch = new JTextField(quickSearchText); + public final FileDrop fileDrop; + public boolean cancel = false; + + public final KeyAdapter search = new SearchKeyAdapter(this); + + private void showPopMenu(ResourceTree tree, TreePath selPath, int x, int y) { + if (selPath == null) + return; + + rightClickMenu.removeAll(); + + rightClickMenu.add(new ResourceListRightClickRemove(this, x, y, tree)); + + //TODO fix for Java 9+ + // You can replace this with any Icon loaded from disk, + // they could be re-used for the plus and minus symbols on the resource list too + rightClickMenu.add(new AbstractAction("Expand", WindowsTreeUI.ExpandedIcon.createExpandedIcon()) { + @Override + public void actionPerformed(ActionEvent e) { + TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y); + expandAll(tree, Objects.requireNonNull(selPath), true); + } + }); + rightClickMenu.add(new AbstractAction("Collapse", WindowsTreeUI.CollapsedIcon.createCollapsedIcon()) { + @Override + public void actionPerformed(ActionEvent e) { + TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y); + expandAll(tree, Objects.requireNonNull(selPath), false); + } + }); + + rightClickMenu.show(this.tree, x, y); + } + + //used to remove resources from the resource list + public void removeFile(FileContainer fileContainer) + { + BytecodeViewer.files.remove(fileContainer); + LazyNameUtil.removeName(fileContainer.name); + } + + public ResourceListPane() + { + super("ClassNavigation"); + tree.setRootVisible(false); + tree.setShowsRootHandles(true); + quickSearch.setForeground(Color.gray); + setTitle("Files"); + + attachTreeListeners(); + attachQuickSearchListeners(); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER); + + JPanel exactPanel = new JPanel(new BorderLayout()); + JPanel quickSearchPanel = new JPanel(); + JPanel buttonPanel = new JPanel(new BorderLayout()); + + quickSearchPanel.setLayout(new BorderLayout()); + quickSearchPanel.add(quickSearch, BorderLayout.NORTH); + exactPanel.add(exact, BorderLayout.WEST); + buttonPanel.add(open, BorderLayout.EAST); + buttonPanel.add(close, BorderLayout.WEST); + exactPanel.add(buttonPanel, BorderLayout.EAST); + quickSearchPanel.add(exactPanel, BorderLayout.SOUTH); + + getContentPane().add(quickSearchPanel, BorderLayout.SOUTH); + + this.setVisible(true); + fileDrop = new FileDrop(this, this); + } + + @Override + public void filesDropped(final File[] files) + { + if (files.length < 1) + return; + + BytecodeViewer.openFiles(files, true); + } + + public void updateTree() + { + try + { + treeRoot.removeAllChildren(); + for (FileContainer container : BytecodeViewer.files) + { + ResourceTreeNode root = new ResourceTreeNode(container.name); + treeRoot.add(root); + + ImageRenderer renderer = new ImageRenderer(); + tree.setCellRenderer(renderer); + + if (!container.classes.isEmpty()) + { + for (ClassNode c : container.classes) + { + String name = c.name; + final String[] spl = name.split("/"); + if (spl.length < 2) + { + root.add(new ResourceTreeNode(name + ".class")); + } + else + { + ResourceTreeNode parent = root; + for (int i1 = 0; i1 < spl.length; i1++) + { + String s = spl[i1]; + + if (i1 == spl.length - 1) + s += ".class"; + + ResourceTreeNode child = null; + for (int i = 0; i < parent.getChildCount(); i++) + { + if (((ResourceTreeNode) parent.getChildAt(i)).getUserObject().equals(s)) + { + child = (ResourceTreeNode) parent.getChildAt(i); + break; + } + } + + if (child == null) + { + child = new ResourceTreeNode(s); + parent.add(child); + } + + parent = child; + } + } + } + } + + if (!container.files.isEmpty()) + { + for (final Entry entry : container.files.entrySet()) + { + String name = entry.getKey(); + final String[] spl = name.split("/"); + if (spl.length < 2) + { + root.add(new ResourceTreeNode(name)); + } + else + { + ResourceTreeNode parent = root; + for (final String s : spl) + { + ResourceTreeNode child = null; + for (int i = 0; i < parent.getChildCount(); i++) + { + if (((ResourceTreeNode) parent.getChildAt(i)).getUserObject().equals(s)) + { + child = (ResourceTreeNode) parent.getChildAt(i); + break; + } + } + if (child == null) + { + child = new ResourceTreeNode(s); + parent.add(child); + } + parent = child; + } + } + } + } + } + + treeRoot.sort(); + tree.expandPath(new TreePath(tree.getModel().getRoot())); + tree.updateUI(); + } catch (java.util.ConcurrentModificationException e) { + //ignore, the last file will reset everything + } + + //TODO add a setting for this + // expandAll(tree, true); + } + + @SuppressWarnings("rawtypes") + private void expandAll(final JTree tree, final TreePath parent, + final boolean expand) { + // Traverse children + final TreeNode node = (TreeNode) parent.getLastPathComponent(); + if (node.getChildCount() >= 0) { + for (final Enumeration e = node.children(); e.hasMoreElements(); ) { + final TreeNode n = (TreeNode) e.nextElement(); + final TreePath path = parent.pathByAddingChild(n); + expandAll(tree, path, expand); + } + } + + // Expansion or collapse must be done bottom-up + if (expand) { + tree.expandPath(parent); + } else { + tree.collapsePath(parent); + } + } + + public void resetWorkspace() + { + treeRoot.removeAllChildren(); + tree.repaint(); + tree.updateUI(); + } + + public void openPath(TreePath path) + { + if (path == null || path.getPathCount() == 1) + return; + + final StringBuilder nameBuffer = new StringBuilder(); + for (int i = 2; i < path.getPathCount(); i++) + { + nameBuffer.append(path.getPathComponent(i)); + if (i < path.getPathCount() - 1) + nameBuffer.append("/"); + } + + String cheapHax = path.getPathComponent(1).toString(); + FileContainer container = null; + + for (FileContainer c : BytecodeViewer.files) + { + if (c.name.equals(cheapHax)) + container = c; + } + + String name = nameBuffer.toString(); + if (name.endsWith(".class")) + { + final ClassNode cn = BytecodeViewer.getClassNode(Objects.requireNonNull(container), + name.substring(0, name.length() - ".class".length())); + + if (cn != null) + BytecodeViewer.viewer.openClassFile(container, nameBuffer.toString(), cn); + else + BytecodeViewer.viewer.openFile(container, nameBuffer.toString(), + BytecodeViewer.getFileContents(nameBuffer.toString())); + } + else + { + BytecodeViewer.viewer.openFile(container, nameBuffer.toString(), + BytecodeViewer.getFileContents(nameBuffer.toString())); + } + } + + public void attachTreeListeners() + { + tree.addMouseListener(new MouseAdapter() + { + @Override + public void mouseReleased(MouseEvent e) + { + if (e.isMetaDown()) + { + ResourceTree tree = (ResourceTree) e.getSource(); + TreePath selPath = ResourceListPane.this.tree.getPathForLocation(e.getX(), e.getY()); + if (selPath == null) + return; + + DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) selPath.getLastPathComponent(); + Enumeration enumeration = treeRoot.children(); + while (enumeration.hasMoreElements()) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement(); + if (node.isNodeAncestor(selectNode)) + { + //rightClickMenu.show(tree, e.getX(), e.getY()); + showPopMenu(tree, selPath, e.getX(), e.getY()); + break; + } + } + } + } + }); + + this.open.addActionListener(e -> { + final TreeNode root = (TreeNode) tree.getModel().getRoot(); + expandAll(tree, new TreePath(root), true); + }); + + this.close.addActionListener(e -> { + final TreeNode root = (TreeNode) tree.getModel().getRoot(); + final TreePath path = new TreePath(root); + expandAll(tree, path, false); + tree.expandPath(path); + }); + + this.tree.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + openPath(tree.getPathForLocation(e.getX(), e.getY())); + } + }); + + this.tree.addTreeSelectionListener(arg0 -> { + if (cancel) { + cancel = false; + return; + } + + openPath(arg0.getPath()); + }); + + this.tree.addKeyListener(new KeyListener() + { + @Override + public void keyReleased(KeyEvent e) { } + + @Override + public void keyTyped(KeyEvent e) { } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + if (e.getSource() instanceof ResourceTree) { + ResourceTree tree = (ResourceTree) e.getSource(); + openPath(tree.getSelectionPath()); + } + } else if ((int) e.getKeyChar() != 0 && (int) e.getKeyChar() != 8 && (int) e.getKeyChar() != 127 && (int) e.getKeyChar() != 65535 && !e.isControlDown() && !e.isAltDown()) { + quickSearch.grabFocus(); + quickSearch.setText("" + e.getKeyChar()); + cancel = true; + } else if (e.isControlDown() && (int) e.getKeyChar() == 6) //ctrl + f + { + quickSearch.grabFocus(); + } else { + cancel = true; + } + } + }); + } + + public void attachQuickSearchListeners() + { + quickSearch.addKeyListener(search); + quickSearch.addFocusListener(new FocusListener() { + @Override + public void focusGained(final FocusEvent arg0) { + if (quickSearch.getText().equals(quickSearchText)) { + quickSearch.setText(""); + quickSearch.setForeground(Color.black); + } + } + + @Override + public void focusLost(final FocusEvent arg0) { + if (quickSearch.getText().isEmpty()) { + quickSearch.setText(quickSearchText); + quickSearch.setForeground(Color.gray); + } + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListRightClickRemove.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListRightClickRemove.java new file mode 100644 index 00000000..b702864a --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceListRightClickRemove.java @@ -0,0 +1,61 @@ +package the.bytecode.club.bytecodeviewer.gui.resourcelist; + +import the.bytecode.club.bytecodeviewer.BytecodeViewer; +import the.bytecode.club.bytecodeviewer.util.FileContainer; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import java.awt.event.ActionEvent; +import java.util.Enumeration; +import java.util.Objects; + +/** + * @author Konloch + * @since 6/22/2021 + */ +public class ResourceListRightClickRemove extends AbstractAction +{ + private final ResourceListPane resourceListPane; + private final int x; + private final int y; + private final ResourceTree tree; + + public ResourceListRightClickRemove(ResourceListPane resourceListPane, int x, int y, ResourceTree tree) + { + super("Remove"); + this.resourceListPane = resourceListPane; + this.x = x; + this.y = y; + this.tree = tree; + } + + @Override + public void actionPerformed(ActionEvent e) + { + TreePath selPath = resourceListPane.tree.getPathForLocation(x, y); + DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) Objects.requireNonNull(selPath).getLastPathComponent(); + Enumeration enumeration = resourceListPane.treeRoot.children(); + while (enumeration.hasMoreElements()) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement(); + if (node.isNodeAncestor(selectNode)) + { + DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); + root.remove(node); + + for (FileContainer fileContainer : BytecodeViewer.files) + { + if (fileContainer.name.equals(selectNode.toString())) + { + resourceListPane.removeFile(fileContainer); + break; + } + } + + resourceListPane.updateTree(); + return; + } + } + } +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTree.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTree.java new file mode 100644 index 00000000..49fb4a80 --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTree.java @@ -0,0 +1,51 @@ +package the.bytecode.club.bytecodeviewer.gui.resourcelist; + +import the.bytecode.club.bytecodeviewer.gui.extras.StringMetricsUtil; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import java.awt.*; + +/** + * @author Konloch + * @since 6/22/2021 + */ +public class ResourceTree extends JTree +{ + private static final long serialVersionUID = -2355167326094772096L; + DefaultMutableTreeNode treeRoot; + + public ResourceTree(final DefaultMutableTreeNode treeRoot) + { + super(treeRoot); + this.treeRoot = treeRoot; + } + + StringMetricsUtil m = null; + + @Override + public void paint(final Graphics g) + { + try + { + super.paint(g); + if (m == null) + { + m = new StringMetricsUtil((Graphics2D) g); + } + if (treeRoot.getChildCount() < 1) + { + g.setColor(new Color(0, 0, 0, 100)); + g.fillRect(0, 0, getWidth(), getHeight()); + g.setColor(Color.white); + String s = "Drag class/jar/zip/APK/DEX here"; + g.drawString(s, + ((int) ((getWidth() / 2) - (m.getWidth(s) / 2))), + getHeight() / 2); + } + } + catch (InternalError | NullPointerException ignored) + { + } + } +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTreeNode.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTreeNode.java new file mode 100644 index 00000000..84353e6d --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/ResourceTreeNode.java @@ -0,0 +1,70 @@ +package the.bytecode.club.bytecodeviewer.gui.resourcelist; + +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.MutableTreeNode; +import java.util.Comparator; + +/** + * @author Konloch + * @since 6/22/2021 + */ +public class ResourceTreeNode extends DefaultMutableTreeNode +{ + + private static final long serialVersionUID = -8817777566176729571L; + + public ResourceTreeNode(final Object o) + { + super(o); + } + + @Override + public void insert(final MutableTreeNode newChild, final int childIndex) + { + super.insert(newChild, childIndex); + } + + public void sort() + { + recursiveSort(this); + } + + @SuppressWarnings("unchecked") + private void recursiveSort(final ResourceTreeNode node) + { + node.children.sort(nodeComparator); + for (ResourceTreeNode nextNode : (Iterable) node.children) + { + if (nextNode.getChildCount() > 0) + { + recursiveSort(nextNode); + } + } + } + + protected Comparator nodeComparator = new Comparator() + { + @Override + public int compare(final ResourceTreeNode o1, final ResourceTreeNode o2) + { + // To make sure nodes with children are always on top + final int firstOffset = o1.getChildCount() > 0 ? -1000 : 0; + final int secondOffset = o2.getChildCount() > 0 ? 1000 : 0; + return o1.toString().compareToIgnoreCase(o2.toString()) + + firstOffset + secondOffset; + } + + @Override + public boolean equals(final Object obj) + { + return false; + } + + @Override + public int hashCode() + { + return 7; + } + }; + +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java new file mode 100644 index 00000000..4ec3217f --- /dev/null +++ b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourcelist/SearchKeyAdapter.java @@ -0,0 +1,129 @@ +package the.bytecode.club.bytecodeviewer.gui.resourcelist; + +import the.bytecode.club.bytecodeviewer.BytecodeViewer; + +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Enumeration; + +/** + * @author Konloch + * @since 6/22/2021 + */ +public class SearchKeyAdapter extends KeyAdapter +{ + private final ResourceListPane resourceListPane; + + public SearchKeyAdapter(ResourceListPane resourceListPane) {this.resourceListPane = resourceListPane;} + + @Override + public void keyPressed(final KeyEvent ke) + { + //only trigger on enter + if (ke.getKeyCode() != KeyEvent.VK_ENTER) + return; + + final String qt = resourceListPane.quickSearch.getText(); + resourceListPane.quickSearch.setText(""); + + if (qt.isEmpty()) //NOPE + return; + + String[] path; + int found = 0; + + if (qt.contains(".")) + { + path = qt.split("\\."); + } + else + { + path = new String[]{qt}; + } + + ResourceTreeNode curNode = resourceListPane.treeRoot; + if (resourceListPane.exact.isSelected()) + { + pathLoop: + for (int i = 0; i < path.length; i++) + { + final String pathName = path[i]; + final boolean isLast = i == path.length - 1; + + for (int c = 0; c < curNode.getChildCount(); c++) + { + final ResourceTreeNode child = (ResourceTreeNode) curNode.getChildAt(c); + System.out.println(pathName + ":" + child.getUserObject()); + + if (child.getUserObject().equals(pathName)) + { + curNode = child; + if (isLast) + { + System.out.println("Found! " + curNode); + found++; + final TreePath pathn = new TreePath(curNode.getPath()); + resourceListPane.tree.setSelectionPath(pathn); + resourceListPane.tree.makeVisible(pathn); + resourceListPane.tree.scrollPathToVisible(pathn); + resourceListPane.openPath(pathn); //auto open + break pathLoop; + } + continue pathLoop; + } + } + + System.out.println("Could not find " + pathName); + break; + } + } + else + { + @SuppressWarnings("unchecked") + Enumeration enums = curNode.depthFirstEnumeration(); + while (enums != null && enums.hasMoreElements()) + { + ResourceTreeNode node = enums.nextElement(); + if (node.isLeaf()) + { + if (((String) (node.getUserObject())).toLowerCase().contains(path[path.length - 1].toLowerCase())) + { + TreeNode[] pathArray = node.getPath(); + int k = 0; + StringBuilder fullPath = new StringBuilder(); + while (pathArray != null + && k < pathArray.length) + { + ResourceTreeNode n = (ResourceTreeNode) pathArray[k]; + String s = (String) (n.getUserObject()); + fullPath.append(s); + if (k++ != pathArray.length - 1) + { + fullPath.append("."); + } + } + String fullPathString = fullPath.toString(); + if (fullPathString.toLowerCase().contains(qt.toLowerCase())) + { + System.out.println("Found! " + node); + found++; + if (found >= 30) + { //TODO probably make this a setting, no real reason it's 30 + BytecodeViewer.showMessage("Uh oh, there could be more results but you've" + + " triggered the 30 classes at once limit. Try refining your search."); + return; + } + final TreePath pathn = new TreePath(node.getPath()); + resourceListPane.tree.setSelectionPath(pathn.getParentPath()); + resourceListPane.tree.setSelectionPath(pathn); + resourceListPane.tree.makeVisible(pathn); + resourceListPane.tree.scrollPathToVisible(pathn); + } + } + } + } + } + } +} diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ResourceListPane.java b/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ResourceListPane.java deleted file mode 100644 index b98e64fd..00000000 --- a/src/main/java/the/bytecode/club/bytecodeviewer/gui/resourceviewer/ResourceListPane.java +++ /dev/null @@ -1,641 +0,0 @@ -package the.bytecode.club.bytecodeviewer.gui.resourceviewer; - -//TODO fix for Java 9+ -import com.sun.java.swing.plaf.windows.WindowsTreeUI; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.font.FontRenderContext; -import java.awt.geom.Rectangle2D; -import java.io.File; -import java.util.Comparator; -import java.util.Enumeration; -import java.util.Map.Entry; -import java.util.Objects; -import javax.swing.AbstractAction; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; -import org.objectweb.asm.tree.ClassNode; -import the.bytecode.club.bytecodeviewer.BytecodeViewer; -import the.bytecode.club.bytecodeviewer.gui.VisibleComponent; -import the.bytecode.club.bytecodeviewer.util.FileContainer; -import the.bytecode.club.bytecodeviewer.util.FileDrop; -import the.bytecode.club.bytecodeviewer.util.LazyNameUtil; - -/*************************************************************************** - * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * - * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - ***************************************************************************/ - -/** - * The file navigation pane. - * - * @author Konloch - * @author WaterWolf - * @author afffsdd - */ - -public class ResourceListPane extends VisibleComponent implements FileDrop.Listener -{ - JCheckBox exact = new JCheckBox("Exact"); - JButton open = new JButton("+"); - JButton close = new JButton("-"); - - MyTreeNode treeRoot = new MyTreeNode("Loaded Files:"); - public MyTree tree = new MyTree(treeRoot); - final String quickSearchText = "Quick file search (no file extension)"; - final JTextField quickSearch = new JTextField(quickSearchText); - boolean cancel = false; - - public KeyAdapter search = new KeyAdapter() { - @Override - public void keyPressed(final KeyEvent ke) { - if (ke.getKeyCode() == KeyEvent.VK_ENTER) { - - final String qt = quickSearch.getText(); - quickSearch.setText(""); - - if (qt.isEmpty()) //NOPE - return; - - - String[] path; - int found = 0; - - if (qt.contains(".")) { - path = qt.split("\\."); - /*String[] path2 = new String[path.length]; - for (int i = 0; i < path.length; i++) { - path2[i] = path[i]; - if (i + 2 == path.length) { - path2[i + 1] = "." + path[i + 1]; - } - }*/ - } else { - path = new String[]{qt}; - } - - MyTreeNode curNode = treeRoot; - if (exact.isSelected()) { - pathLoop: - for (int i = 0; i < path.length; i++) { - final String pathName = path[i]; - final boolean isLast = i == path.length - 1; - - for (int c = 0; c < curNode.getChildCount(); c++) { - final MyTreeNode child = (MyTreeNode) curNode.getChildAt(c); - System.out.println(pathName + ":" + child.getUserObject()); - - if (child.getUserObject().equals(pathName)) { - curNode = child; - if (isLast) { - System.out.println("Found! " + curNode); - found++; - final TreePath pathn = new TreePath(curNode.getPath()); - tree.setSelectionPath(pathn); - tree.makeVisible(pathn); - tree.scrollPathToVisible(pathn); - openPath(pathn); //auto open - break pathLoop; - } - continue pathLoop; - } - } - - System.out.println("Could not find " + pathName); - break; - } - } else { - { - @SuppressWarnings("unchecked") - Enumeration enums = curNode.depthFirstEnumeration(); - while (enums != null && enums.hasMoreElements()) { - - MyTreeNode node = enums.nextElement(); - if (node.isLeaf()) { - if (((String) (node.getUserObject())).toLowerCase().contains(path[path.length - 1].toLowerCase())) { - TreeNode[] pathArray = node.getPath(); - int k = 0; - StringBuilder fullPath = new StringBuilder(); - while (pathArray != null - && k < pathArray.length) { - MyTreeNode n = (MyTreeNode) pathArray[k]; - String s = (String) (n.getUserObject()); - fullPath.append(s); - if (k++ != pathArray.length - 1) { - fullPath.append("."); - } - } - String fullPathString = fullPath.toString(); - if (fullPathString.toLowerCase().contains(qt.toLowerCase())) { - System.out.println("Found! " + node); - found++; - if (found >= 30) { - BytecodeViewer.showMessage("Uh oh, there could be more results but you've" - + " triggered the 30 classes at once limit. Try refining your " - + "search."); - return; - } - final TreePath pathn = new TreePath(node.getPath()); - tree.setSelectionPath(pathn.getParentPath()); - tree.setSelectionPath(pathn); - tree.makeVisible(pathn); - tree.scrollPathToVisible(pathn); - } - } - } - } - } - } - } - } - }; - - private void showPopMenu(MyTree tree, TreePath selPath, int x, int y) { - if (selPath == null) { - return; - } - - final JPopupMenu pop = new JPopupMenu(); - pop.add(new AbstractAction("Remove") { - @Override - public void actionPerformed(ActionEvent e) { - TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y); - DefaultMutableTreeNode selectNode = - (DefaultMutableTreeNode) Objects.requireNonNull(selPath).getLastPathComponent(); - Enumeration enumeration = treeRoot.children(); - while (enumeration.hasMoreElements()) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement(); - if (node.isNodeAncestor(selectNode)) { - DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot(); - root.remove(node); - for (FileContainer fileContainer : BytecodeViewer.files) { - if (fileContainer.name.equals(selectNode.toString())) { - removeFile(fileContainer); - break; - } - } - updateTree(); - return; - } - } - } - }); - - //TODO fix for Java 9+ - pop.add(new AbstractAction("Expand", WindowsTreeUI.ExpandedIcon.createExpandedIcon()) { - @Override - public void actionPerformed(ActionEvent e) { - TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y); - expandAll(tree, Objects.requireNonNull(selPath), true); - } - }); - - pop.add(new AbstractAction("Collapse", WindowsTreeUI.CollapsedIcon.createCollapsedIcon()) { - @Override - public void actionPerformed(ActionEvent e) { - TreePath selPath = ResourceListPane.this.tree.getPathForLocation(x, y); - expandAll(tree, Objects.requireNonNull(selPath), false); - } - }); - - pop.show(this.tree, x, y); - } - - private void removeFile(FileContainer fileContainer) { - BytecodeViewer.files.remove(fileContainer); - LazyNameUtil.removeName(fileContainer.name); - } - - public ResourceListPane() { - super("ClassNavigation"); - tree.setRootVisible(false); - tree.setShowsRootHandles(true); - quickSearch.setForeground(Color.gray); - setTitle("Files"); - - tree.addMouseListener(new MouseAdapter() { - @Override - public void mouseReleased(MouseEvent e) { - if (e.isMetaDown()) { - MyTree tree = (MyTree) e.getSource(); - TreePath selPath = ResourceListPane.this.tree.getPathForLocation(e.getX(), e.getY()); - if (selPath == null) { - return; - } - - DefaultMutableTreeNode selectNode = (DefaultMutableTreeNode) selPath.getLastPathComponent(); - Enumeration enumeration = treeRoot.children(); - while (enumeration.hasMoreElements()) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement(); - if (node.isNodeAncestor(selectNode)) { - // pop.show(tree, e.getX(), e.getY()); - showPopMenu(tree, selPath, e.getX(), e.getY()); - break; - } - } - } - } - }); - - this.open.addActionListener(e -> { - final TreeNode root = (TreeNode) tree.getModel().getRoot(); - expandAll(tree, new TreePath(root), true); - }); - - this.close.addActionListener(e -> { - final TreeNode root = (TreeNode) tree.getModel().getRoot(); - final TreePath path = new TreePath(root); - expandAll(tree, path, false); - tree.expandPath(path); - }); - - this.tree.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - openPath(tree.getPathForLocation(e.getX(), e.getY())); - } - }); - - this.tree.addTreeSelectionListener(arg0 -> { - if (cancel) { - cancel = false; - return; - } - openPath(arg0.getPath()); - }); - - this.tree.addKeyListener(new KeyListener() { - @Override - public void keyReleased(KeyEvent e) { - } - - @Override - public void keyTyped(KeyEvent e) { - } - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - if (e.getSource() instanceof MyTree) { - MyTree tree = (MyTree) e.getSource(); - openPath(tree.getSelectionPath()); - } - } else if ((int) e.getKeyChar() != 0 && (int) e.getKeyChar() != 8 && (int) e.getKeyChar() != 127 && (int) e.getKeyChar() != 65535 && !e.isControlDown() && !e.isAltDown()) { - quickSearch.grabFocus(); - quickSearch.setText("" + e.getKeyChar()); - cancel = true; - } else if (e.isControlDown() && (int) e.getKeyChar() == 6) //ctrl + f - { - quickSearch.grabFocus(); - } else { - cancel = true; - } - } - }); - - quickSearch.addKeyListener(search); - - quickSearch.addFocusListener(new FocusListener() { - @Override - public void focusGained(final FocusEvent arg0) { - if (quickSearch.getText().equals(quickSearchText)) { - quickSearch.setText(""); - quickSearch.setForeground(Color.black); - } - } - - @Override - public void focusLost(final FocusEvent arg0) { - if (quickSearch.getText().isEmpty()) { - quickSearch.setText(quickSearchText); - quickSearch.setForeground(Color.gray); - } - } - }); - - getContentPane().setLayout(new BorderLayout()); - getContentPane().add(new JScrollPane(tree), BorderLayout.CENTER); - - JPanel p2 = new JPanel(); - p2.setLayout(new BorderLayout()); - p2.add(quickSearch, BorderLayout.NORTH); - JPanel p3 = new JPanel(new BorderLayout()); - p3.add(exact, BorderLayout.WEST); - JPanel p4 = new JPanel(new BorderLayout()); - p4.add(open, BorderLayout.EAST); - p4.add(close, BorderLayout.WEST); - p3.add(p4, BorderLayout.EAST); - p2.add(p3, BorderLayout.SOUTH); - - getContentPane().add(p2, BorderLayout.SOUTH); - - this.setVisible(true); - new FileDrop(this, this); - } - - public void openClassFileToWorkSpace(final FileContainer container, final String name, final ClassNode node) { - BytecodeViewer.viewer.openClassFile(container, name, node); - } - - public void openFileToWorkSpace(final FileContainer container, String name, byte[] contents) { - BytecodeViewer.viewer.openFile(container, name, contents); - } - - @Override - public void filesDropped(final File[] files) { - if (files.length < 1) - return; - BytecodeViewer.openFiles(files, true); - } - - public void updateTree() { - try { - treeRoot.removeAllChildren(); - for (FileContainer container : BytecodeViewer.files) { - MyTreeNode root = new MyTreeNode(container.name); - treeRoot.add(root); - ImageRenderer renderer = new ImageRenderer(); - tree.setCellRenderer(renderer); - - if (!container.classes.isEmpty()) { - for (ClassNode c : container.classes) { - String name = c.name; - final String[] spl = name.split("/"); - if (spl.length < 2) { - root.add(new MyTreeNode(name + ".class")); - } else { - MyTreeNode parent = root; - for (int i1 = 0; i1 < spl.length; i1++) { - String s = spl[i1]; - if (i1 == spl.length - 1) - s += ".class"; - MyTreeNode child = null; - for (int i = 0; i < parent.getChildCount(); i++) { - if (((MyTreeNode) parent.getChildAt(i)).getUserObject() - .equals(s)) { - child = (MyTreeNode) parent.getChildAt(i); - break; - } - } - if (child == null) { - child = new MyTreeNode(s); - parent.add(child); - } - parent = child; - } - } - } - } - - if (!container.files.isEmpty()) { - for (final Entry entry : container.files.entrySet()) { - String name = entry.getKey(); - final String[] spl = name.split("/"); - if (spl.length < 2) { - root.add(new MyTreeNode(name)); - } else { - MyTreeNode parent = root; - for (final String s : spl) { - MyTreeNode child = null; - for (int i = 0; i < parent.getChildCount(); i++) { - if (((MyTreeNode) parent.getChildAt(i)).getUserObject() - .equals(s)) { - child = (MyTreeNode) parent.getChildAt(i); - break; - } - } - if (child == null) { - child = new MyTreeNode(s); - parent.add(child); - } - parent = child; - } - } - } - } - - } - - treeRoot.sort(); - tree.expandPath(new TreePath(tree.getModel().getRoot())); - tree.updateUI(); - } catch (java.util.ConcurrentModificationException e) { - //ignore, the last file will reset everything - } - // expandAll(tree, true); - } - - @SuppressWarnings("rawtypes") - private void expandAll(final JTree tree, final TreePath parent, - final boolean expand) { - // Traverse children - final TreeNode node = (TreeNode) parent.getLastPathComponent(); - if (node.getChildCount() >= 0) { - for (final Enumeration e = node.children(); e.hasMoreElements(); ) { - final TreeNode n = (TreeNode) e.nextElement(); - final TreePath path = parent.pathByAddingChild(n); - expandAll(tree, path, expand); - } - } - - // Expansion or collapse must be done bottom-up - if (expand) { - tree.expandPath(parent); - } else { - tree.collapsePath(parent); - } - } - - public class MyTree extends JTree { - - private static final long serialVersionUID = -2355167326094772096L; - DefaultMutableTreeNode treeRoot; - - public MyTree(final DefaultMutableTreeNode treeRoot) { - super(treeRoot); - this.treeRoot = treeRoot; - } - - StringMetrics m = null; - - @Override - public void paint(final Graphics g) { - try { - super.paint(g); - if (m == null) { - m = new StringMetrics((Graphics2D) g); - } - if (treeRoot.getChildCount() < 1) { - g.setColor(new Color(0, 0, 0, 100)); - g.fillRect(0, 0, getWidth(), getHeight()); - g.setColor(Color.white); - String s = "Drag class/jar/zip/APK/DEX here"; - g.drawString(s, - ((int) ((getWidth() / 2) - (m.getWidth(s) / 2))), - getHeight() / 2); - } - } catch (java.lang.InternalError | java.lang.NullPointerException ignored) { - } - } - } - - public static class MyTreeNode extends DefaultMutableTreeNode { - - private static final long serialVersionUID = -8817777566176729571L; - - public MyTreeNode(final Object o) { - super(o); - } - - @Override - public void insert(final MutableTreeNode newChild, final int childIndex) { - super.insert(newChild, childIndex); - } - - public void sort() { - recursiveSort(this); - } - - @SuppressWarnings("unchecked") - private void recursiveSort(final MyTreeNode node) { - node.children.sort(nodeComparator); - for (MyTreeNode nextNode : (Iterable) node.children) { - if (nextNode.getChildCount() > 0) { - recursiveSort(nextNode); - } - } - } - - protected Comparator nodeComparator = new Comparator() { - @Override - public int compare(final MyTreeNode o1, final MyTreeNode o2) { - // To make sure nodes with children are always on top - final int firstOffset = o1.getChildCount() > 0 ? -1000 : 0; - final int secondOffset = o2.getChildCount() > 0 ? 1000 : 0; - return o1.toString().compareToIgnoreCase(o2.toString()) - + firstOffset + secondOffset; - } - - @Override - public boolean equals(final Object obj) { - return false; - } - - @Override - public int hashCode() { - return 7; - } - }; - - } - - /** - * @author http://stackoverflow.com/a/18450804 - */ - static class StringMetrics { - - Font font; - FontRenderContext context; - - public StringMetrics(Graphics2D g2) { - - font = g2.getFont(); - context = g2.getFontRenderContext(); - } - - Rectangle2D getBounds(String message) { - - return font.getStringBounds(message, context); - } - - double getWidth(String message) { - - Rectangle2D bounds = getBounds(message); - return bounds.getWidth(); - } - - double getHeight(String message) { - - Rectangle2D bounds = getBounds(message); - return bounds.getHeight(); - } - - } - - public void resetWorkspace() { - treeRoot.removeAllChildren(); - tree.repaint(); - tree.updateUI(); - } - - public void openPath(TreePath path) { - if (path == null || path.getPathCount() == 1) { - return; - } - - final StringBuilder nameBuffer = new StringBuilder(); - for (int i = 2; i < path.getPathCount(); i++) { - nameBuffer.append(path.getPathComponent(i)); - if (i < path.getPathCount() - 1) { - nameBuffer.append("/"); - } - } - - String cheapHax = path.getPathComponent(1).toString(); - FileContainer container = null; - - for (FileContainer c : BytecodeViewer.files) { - if (c.name.equals(cheapHax)) - container = c; - } - - String name = nameBuffer.toString(); - if (name.endsWith(".class")) { - - final ClassNode cn = BytecodeViewer.getClassNode(Objects.requireNonNull(container), name.substring(0, - name.length() - ".class".length())); - if (cn != null) { - openClassFileToWorkSpace(container, nameBuffer.toString(), cn); - } else { - openFileToWorkSpace(container, nameBuffer.toString(), - BytecodeViewer.getFileContents(nameBuffer.toString())); - } - } else { - openFileToWorkSpace(container, nameBuffer.toString(), - BytecodeViewer.getFileContents(nameBuffer.toString())); - } - } -} \ No newline at end of file diff --git a/src/main/java/the/bytecode/club/bytecodeviewer/util/OpenFile.java b/src/main/java/the/bytecode/club/bytecodeviewer/util/OpenFile.java index 24ef9763..9725644b 100644 --- a/src/main/java/the/bytecode/club/bytecodeviewer/util/OpenFile.java +++ b/src/main/java/the/bytecode/club/bytecodeviewer/util/OpenFile.java @@ -4,7 +4,7 @@ import org.apache.commons.io.FileUtils; import org.objectweb.asm.tree.ClassNode; import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.api.ExceptionUI; -import the.bytecode.club.bytecodeviewer.gui.resourceviewer.ResourceListPane; +import the.bytecode.club.bytecodeviewer.gui.resourcelist.ResourceListPane; import the.bytecode.club.bytecodeviewer.gui.MainViewerGUI; import java.io.File;