Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Cynthia Foxwell 2023-08-03 21:58:09 -06:00
commit 95d3969e2e
18 changed files with 1032 additions and 866 deletions

View File

@ -23,6 +23,7 @@ jobs:
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Extract Maven project version

28
LICENSE
View File

@ -645,30 +645,4 @@ the "copyright" line and a pointer to where the full notice is found.
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 <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Bytecode Viewer(BCV) Copyright (C) 2014 Kalen "Konloch" Kinloch - http://bytecodeviewer.com - http://the.bytecode.club
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
along with this program. If not, see <http://www.gnu.org/licenses/>.

69
pom.xml
View File

@ -13,41 +13,42 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- Dependency versions -->
<annotations.version>23.0.0</annotations.version>
<annotations.version>24.0.1</annotations.version>
<antlr4.version>4.9.3</antlr4.version>
<apktool.version>2.6.1</apktool.version>
<asm.version>9.4</asm.version>
<apktool.version>2.8.0</apktool.version>
<asm.version>9.5</asm.version>
<bined.version>0.2.0</bined.version>
<byteanalysis.version>1.0bcv</byteanalysis.version>
<cfr.version>0.152</cfr.version>
<cloning.version>1.9.12</cloning.version>
<commons-cli.version>1.5.0</commons-cli.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-compiler.version>3.1.8</commons-compiler.version>
<commons-compress.version>1.21</commons-compress.version>
<commons-io.version>2.11.0</commons-io.version>
<commons-codec.version>1.16.0</commons-codec.version>
<commons-compiler.version>3.1.10</commons-compiler.version>
<commons-compress.version>1.23.0</commons-compress.version>
<commons-io.version>2.13.0</commons-io.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<commons-text.version>1.10.0</commons-text.version>
<darklaf.version>3.0.2</darklaf.version>
<darklaf-extensions-rsta.version>0.4.1</darklaf-extensions-rsta.version>
<decompiler-fernflower.version>6.0.0.Final</decompiler-fernflower.version>
<dex2jar.version>v56</dex2jar.version>
<decompiler-fernflower.version>6.2.5.Final</decompiler-fernflower.version>
<dex2jar.version>v64</dex2jar.version>
<fernflower.version>1.9.2</fernflower.version>
<gson.version>2.9.1</gson.version>
<guava.version>31.1-jre</guava.version>
<gson.version>2.10.1</gson.version>
<guava.version>32.1.1-jre</guava.version>
<httprequest.version>2.2.0</httprequest.version>
<imgscalr-lib.version>4.2</imgscalr-lib.version>
<jadx.version>1.4.4</jadx.version>
<jadx.version>1.4.7</jadx.version>
<jd-gui.version>1.6.6bcv</jd-gui.version>
<jgraphx.version>3.4.1.3</jgraphx.version>
<js.version>21.2.0</js.version>
<objenesis.version>3.3</objenesis.version>
<paged-data.version>0.2.0</paged-data.version>
<procyon.version>0.6.0</procyon.version>
<rsyntaxtextarea.version>3.3.0</rsyntaxtextarea.version>
<rsyntaxtextarea.version>3.3.3</rsyntaxtextarea.version>
<semantic-version.version>2.1.1</semantic-version.version>
<slf4j.version>2.0.3</slf4j.version>
<smali.version>2.5.2</smali.version>
<snakeyaml.version>1.33</snakeyaml.version>
<slf4j.version>2.0.7</slf4j.version>
<smali.version>3.0.3</smali.version>
<safeyaml.version>1.34.1</safeyaml.version>
<treelayout.version>1.0.3</treelayout.version>
<webp-imageio.version>0.2.2</webp-imageio.version>
<xpp3.version>1.1.4c</xpp3.version>
@ -73,6 +74,11 @@
</repositories>
<dependencies>
<dependency>
<groupId>com.konloch</groupId>
<artifactId>HTTPRequest</artifactId>
<version>${httprequest.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
@ -271,19 +277,19 @@
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.smali</groupId>
<groupId>com.android.tools.smali</groupId>
<artifactId>smali</artifactId>
<version>${smali.version}</version>
</dependency>
<dependency>
<groupId>org.smali</groupId>
<artifactId>baksmali</artifactId>
<groupId>com.android.tools.smali</groupId>
<artifactId>smali-baksmali</artifactId>
<version>${smali.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
<groupId>com.konloch</groupId>
<artifactId>safeyaml</artifactId>
<version>${safeyaml.version}</version>
</dependency>
<dependency>
<groupId>xpp3</groupId>
@ -314,11 +320,23 @@
<groupId>io.github.skylot</groupId>
<artifactId>jadx-dex-input</artifactId>
<version>${jadx.version}</version>
<exclusions>
<exclusion>
<groupId>org.smali</groupId>
<artifactId>baksmali</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.github.skylot</groupId>
<artifactId>jadx-smali-input</artifactId>
<version>${jadx.version}</version>
<exclusions>
<exclusion>
<groupId>org.smali</groupId>
<artifactId>smali</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.ThexXTURBOXx</groupId>
@ -380,7 +398,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
@ -390,7 +408,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
<configuration>
<source>${maven.compiler.source}</source>
</configuration>
@ -398,7 +416,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
@ -419,6 +437,7 @@
<exclude>META-INF/*LICENSE*</exclude>
<exclude>META-INF/*NOTICE*</exclude>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>LICENSE</exclude>
</excludes>
</filter>
<!-- Ignore all ASM-related files from d2j-external but MCTLE fix -->

View File

@ -1,276 +0,0 @@
package me.konloch.kontainer.io;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
/**
* A wrapper for Java SE classes to write/read an HTTP Request
*
* @author Konloch
*/
public class HTTPRequest {
public URL url;
private int timeout = 30000;
private String cookie;
private String referer;
private String postData;
private String useragent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0";
private Proxy proxy;
private boolean setFollowRedirects = true;
private BufferedReader reader;
private DataOutputStream writer;
private HttpURLConnection connection;
private Set<Entry<String, List<String>>> lastConnectionHeaders;
private int statusCode;
/**
* Creates a new HTTPRequest object
*
* @param url
*/
public HTTPRequest(URL url) {
this.url = url;
}
/**
* Sets a referer to send to the web server
*/
public void setReferer(String referer) {
this.referer = referer;
}
/**
* Set a cookie string to send to the web server
*/
public void setCookie(String cookie) {
this.cookie = cookie;
}
/**
* Sets post data to send to the web server
*/
public void setPostData(String postData) {
this.postData = postData;
}
/**
* Sets a custom useragent, default 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0'
*/
public void setUseragent(String useragent) {
this.useragent = useragent;
}
/**
* Sets the seconds till timeout, default 30,000 milliseconds
*/
public void setTimeout(int timeout) {
this.timeout = timeout;
}
/**
* Sets a proxy to connect through
*/
public void setProxy(Proxy proxy) {
this.proxy = proxy;
}
/**
* Used to get the headers the webserver sent on our last connection
*/
public Set<Entry<String, List<String>>> getLastConnectionHeaders() {
return lastConnectionHeaders;
}
public int getStatusCode()
{
return statusCode;
}
/**
* By default, follow redirects are enabled
*/
public void setFollowRedirects(boolean setFollowRedirects) {
this.setFollowRedirects = setFollowRedirects;
}
/**
* Used to set up the connection to read the content.
*/
private void setup() throws Exception {
if (proxy != null)
connection = (HttpURLConnection) url.openConnection(proxy);
else
connection = (HttpURLConnection) url.openConnection();
if (cookie != null)
connection.setRequestProperty("Cookie", cookie);
if (referer != null)
connection.addRequestProperty("Referer", referer);
connection.setRequestProperty("User-Agent", useragent);
connection.setReadTimeout(timeout);
connection.setConnectTimeout(timeout);
connection.setUseCaches(false);
HttpURLConnection.setFollowRedirects(setFollowRedirects);
if (postData != null) {
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
writer = new DataOutputStream(connection.getOutputStream());
writer.writeBytes(postData);
writer.flush();
}
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
}
/**
* Reads the entire page and returns a string array
*
* @return
* @throws Exception
*/
public String[] read() throws Exception {
List<String> st;
try {
setup();
st = new ArrayList<>();
String s;
while ((s = reader.readLine()) != null)
st.add(s);
lastConnectionHeaders = connection.getHeaderFields().entrySet();
statusCode = connection.getResponseCode();
} catch (Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return st.toArray(new String[0]);
}
/**
* Reads as many lines as expected unless it reaches the end.
*
* @param linesToRead
* @return
* @throws Exception
*/
public String[] read(int linesToRead) throws Exception {
List<String> st;
try {
setup();
st = new ArrayList<>();
for (int i = 0; i < linesToRead; i++) {
String s = reader.readLine();
if (s != null)
st.add(s);
}
lastConnectionHeaders = connection.getHeaderFields().entrySet();
statusCode = connection.getResponseCode();
} catch (Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return st.toArray(new String[0]);
}
/**
* Only reads the first line
*
* @return
* @throws Exception
*/
public String readSingle() throws Exception {
String s;
try {
setup();
s = reader.readLine();
lastConnectionHeaders = connection.getHeaderFields().entrySet();
statusCode = connection.getResponseCode();
} catch (Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return s;
}
/**
* Reads until it reaches the expected line then it returns it.
*
* @param linesToRead
* @return
* @throws Exception
*/
public String readSingle(int linesToRead) throws Exception {
String s;
try {
setup();
for (int i = 0; i < linesToRead - 1; i++)
reader.readLine();
s = reader.readLine();
lastConnectionHeaders = connection.getHeaderFields().entrySet();
statusCode = connection.getResponseCode();
} catch (Exception e) {
cleanup();
throw e;
} finally {
cleanup();
}
return s;
}
/**
* Used to clean up the connection, closes the connections and nulls the objects
*/
private void cleanup() {
try {
reader.close();
} catch (Exception ignored) {
}
try {
writer.close();
} catch (Exception ignored) {
}
try {
connection.disconnect();
} catch (Exception ignored) {
}
reader = null;
writer = null;
connection = null;
}
}

View File

@ -129,8 +129,6 @@ import static the.bytecode.club.bytecodeviewer.Constants.tempDirectory;
public class BytecodeViewer
{
//TODO fix this for tab dragging & better tab controls
public static boolean EXPERIMENTAL_TAB_CODE = false;
//the launch args called on BCV
public static String[] launchArgs;
@ -198,7 +196,7 @@ public class BytecodeViewer
//setup swing components
viewer = new MainViewerGUI();
SwingUtilities.updateComponentTreeUI(viewer);
//SwingUtilities.updateComponentTreeUI(viewer);
//load settings and set swing components state
SettingsSerializer.loadSettings();

View File

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Objects;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import me.konloch.kontainer.io.HTTPRequest;
import com.konloch.httprequest.HTTPRequest;
import org.apache.commons.io.FileUtils;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
@ -277,7 +277,6 @@ public class Boot {
public static void populateUrlList() throws Exception {
HTTPRequest req = new HTTPRequest(new URL("https://github.com/Konloch/bytecode-viewer/tree/master/libs"));
req.setTimeout(30000);
for (String s : req.read())
if (s.contains("href=\"/Konloch/bytecode-viewer/blob/master/libs/")) {
urlList.add("https://github.com" + s.split("href=")[1].split("\"")[1]);

View File

@ -10,7 +10,7 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import javax.swing.JFileChooser;
import me.konloch.kontainer.io.HTTPRequest;
import com.konloch.httprequest.HTTPRequest;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.Configuration;
import the.bytecode.club.bytecodeviewer.api.BCV;
@ -257,7 +257,7 @@ public class UpdateCheck implements Runnable
{
HTTPRequest request = new HTTPRequest(new URL(url));
request.readSingle();
return request.getStatusCode() == 200;
return request.getLastStatusCode() == 200;
}
private static void download(String url, File saveTo, Runnable onFinish) throws Exception

View File

@ -118,7 +118,12 @@ public class FernFlowerDecompiler extends InternalDecompiler
tempClass.delete();
final File outputJava = new File(start + ".java");
String javaDir = start;
if (BytecodeViewer.viewer.ren.isSelected()) {
javaDir = tempDirectory + "class_0";
}
final File outputJava = new File(javaDir + ".java");
if (outputJava.exists()) {
String s;
try {

View File

@ -116,7 +116,7 @@ public class MainViewerGUI extends JFrame
public final SearchBoxPane searchBoxPane = new SearchBoxPane();
public JSplitPane splitPane1;
public JSplitPane splitPane2;
//the root menu bar
public final JMenuBar rootMenu = new JMenuBar();
@ -366,9 +366,12 @@ public class MainViewerGUI extends JFrame
searchBoxPane.setPreferredSize(new Dimension(200, 50));
searchBoxPane.setMinimumSize(new Dimension(200, 50));
searchBoxPane.setMaximumSize(new Dimension(200, 2147483647));
workPane.setPreferredSize(new Dimension(1500, 1000));
splitPane1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, resourcePane, searchBoxPane);
splitPane2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane1, workPane);
getContentPane().add(splitPane2);
splitPane2.setResizeWeight(0.05);
splitPane1.setResizeWeight(0.5);
@ -376,6 +379,7 @@ public class MainViewerGUI extends JFrame
uiComponents.add(resourcePane);
uiComponents.add(searchBoxPane);
uiComponents.add(workPane);
// uiComponents.add(hierarchy);
viewPane1.setDefault();
viewPane2.setDefault();

View File

@ -5,6 +5,7 @@ import java.awt.Color;
import java.awt.Font;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JButton;
import javax.swing.JCheckBox;
@ -13,6 +14,7 @@ import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -50,9 +52,9 @@ import the.bytecode.club.bytecodeviewer.util.JTextAreaUtils;
* @author Konloch
* @since 6/25/2021
*/
public class SearchableRSyntaxTextArea extends RSyntaxTextArea
{
private final RTextScrollPane scrollPane = new RTextScrollPane(this);
public class SearchableRSyntaxTextArea extends RSyntaxTextArea {
private RTextScrollPane scrollPane = new RTextScrollPane(this);
private final JPanel searchPanel = new JPanel(new BorderLayout());
private final JTextField searchInput = new JTextField();
private final JCheckBox caseSensitiveSearch = new TranslatedJCheckBox("Match case", TranslatedComponents.MATCH_CASE);
@ -62,30 +64,26 @@ public class SearchableRSyntaxTextArea extends RSyntaxTextArea
private final Color blackScrollBackground = new Color(0x232323);
private final Color blackScrollForeground = new Color(0x575859);
private Runnable onCtrlS;
public SearchableRSyntaxTextArea()
{
if(Configuration.lafTheme == LAFTheme.HIGH_CONTRAST_DARK)
{
public SearchableRSyntaxTextArea() {
if (Configuration.lafTheme == LAFTheme.HIGH_CONTRAST_DARK) {
//this fixes the white border on the jScrollBar panes
scrollPane.getHorizontalScrollBar().setBackground(blackScrollBackground);
scrollPane.getHorizontalScrollBar().setForeground(blackScrollForeground);
scrollPane.getVerticalScrollBar().setBackground(blackScrollBackground);
scrollPane.getVerticalScrollBar().setForeground(blackScrollForeground);
}
else if(Configuration.lafTheme.isDark())
{
} else if (Configuration.lafTheme.isDark()) {
//this fixes the white border on the jScrollBar panes
scrollPane.getHorizontalScrollBar().setBackground(darkScrollBackground);
scrollPane.getHorizontalScrollBar().setForeground(darkScrollForeground);
scrollPane.getVerticalScrollBar().setBackground(darkScrollBackground);
scrollPane.getVerticalScrollBar().setForeground(darkScrollForeground);
}
setAntiAliasingEnabled(true);
scrollPane.setColumnHeaderView(searchPanel);
JButton searchNext = new JButton();
JButton searchPrev = new JButton();
JPanel buttonPane = new JPanel(new BorderLayout());
@ -96,90 +94,84 @@ public class SearchableRSyntaxTextArea extends RSyntaxTextArea
searchPanel.add(buttonPane, BorderLayout.WEST);
searchPanel.add(searchInput, BorderLayout.CENTER);
searchPanel.add(caseSensitiveSearch, BorderLayout.EAST);
searchNext.addActionListener(arg0 -> search(searchInput.getText(), true, caseSensitiveSearch.isSelected()));
searchPrev.addActionListener(arg0 -> search(searchInput.getText(), false, caseSensitiveSearch.isSelected()));
searchInput.addKeyListener(new ReleaseKeyListener(keyEvent ->
{
if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
search(searchInput.getText(), true, caseSensitiveSearch.isSelected());
}));
addKeyListener(new PressKeyListener(keyEvent ->
{
if ((keyEvent.getKeyCode() == KeyEvent.VK_F) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0))
searchInput.requestFocus();
if (onCtrlS != null && (keyEvent.getKeyCode() == KeyEvent.VK_S) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0))
{
if (onCtrlS != null && (keyEvent.getKeyCode() == KeyEvent.VK_S) && ((keyEvent.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)) {
onCtrlS.run();
return;
}
GlobalHotKeys.keyPressed(keyEvent);
}));
final Font newFont = getFont().deriveFont((float) BytecodeViewer.viewer.getFontSize());
//set number-bar font
setFont(newFont);
SwingUtilities.invokeLater(()-> {
SwingUtilities.invokeLater(() -> {
//attach CTRL + Mouse Wheel Zoom
attachCtrlMouseWheelZoom();
//set text font
setFont(newFont);
});
}
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch)
{
public void search(String search, boolean forwardSearchDirection, boolean caseSensitiveSearch) {
JTextAreaUtils.search(this, search, forwardSearchDirection, caseSensitiveSearch);
}
public void highlight(String pattern, boolean caseSensitiveSearch)
{
public void highlight(String pattern, boolean caseSensitiveSearch) {
JTextAreaUtils.highlight(this, pattern, caseSensitiveSearch);
}
public void attachCtrlMouseWheelZoom()
{
//get the existing scroll event
MouseWheelListener ogListener = scrollPane.getMouseWheelListeners().length > 0 ?
scrollPane.getMouseWheelListeners()[0] : null;
//remove the existing event
if(ogListener != null)
scrollPane.removeMouseWheelListener(ogListener);
//add a new event
scrollPane.addMouseWheelListener(e ->
{
if (getText().isEmpty())
return;
if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0)
{
public void attachCtrlMouseWheelZoom() {
scrollPane.addMouseWheelListener(e -> {
if (getText().isEmpty()) return;
if ((e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) {
Font font = getFont();
int size = font.getSize();
if (e.getWheelRotation() > 0) //Up
if (e.getWheelRotation() > 0)
setFont(new Font(font.getName(), font.getStyle(), --size >= 2 ? --size : 2));
else //Down
else
setFont(new Font(font.getName(), font.getStyle(), ++size));
e.consume();
}
else if(ogListener != null)
{
ogListener.mouseWheelMoved(e);
}
});
scrollPane = new RTextScrollPane() {
@Override
protected void processMouseWheelEvent(MouseWheelEvent event) {
if (!isWheelScrollingEnabled()) {
if (getParent() != null) {
getParent().dispatchEvent(SwingUtilities.convertMouseEvent(this, event, getParent()));
return;
}
}
super.processMouseWheelEvent(event);
}
};
scrollPane.setWheelScrollingEnabled(false);
}
public String getLineText(int line) {
try {
if (line < getLineCount()) {
@ -187,42 +179,36 @@ public class SearchableRSyntaxTextArea extends RSyntaxTextArea
int end = getLineEndOffset(line);
return getText(start, end - start).trim();
}
} catch (BadLocationException ignored) { }
} catch (BadLocationException ignored) {
}
return "";
}
public void setOnCtrlS(Runnable onCtrlS)
{
public void setOnCtrlS(Runnable onCtrlS) {
this.onCtrlS = onCtrlS;
}
public RTextScrollPane getScrollPane()
{
public RTextScrollPane getScrollPane() {
return scrollPane;
}
public JPanel getSearchPanel()
{
public JPanel getSearchPanel() {
return searchPanel;
}
public JTextField getSearchInput()
{
public JTextField getSearchInput() {
return searchInput;
}
public JCheckBox getCaseSensitiveSearch()
{
public JCheckBox getCaseSensitiveSearch() {
return caseSensitiveSearch;
}
public JLabel getTitleHeader()
{
public JLabel getTitleHeader() {
return titleHeader;
}
public Runnable getOnCtrlS()
{
public Runnable getOnCtrlS() {
return onCtrlS;
}
}

View File

@ -0,0 +1,74 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import com.github.weisj.darklaf.components.CloseButton;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.MouseClickedListener;
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
import javax.swing.*;
import java.awt.*;
public class CloseButtonComponent extends JPanel {
private final JTabbedPane pane;
public CloseButtonComponent(final JTabbedPane pane) {
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
if (pane == null) {
throw new NullPointerException("TabbedPane is null");
}
this.pane = pane;
setOpaque(false);
JLabel label = new JLabel() {
public String getText() {
int i = pane.indexOfTabComponent(CloseButtonComponent.this);
if (i != -1) {
return pane.getTitleAt(i);
}
return null;
}
};
add(label);
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
JButton button = new CloseButton();
add(button);
JPopupMenu rightClickMenu = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem(String.valueOf(TranslatedStrings.CLOSE_ALL_BUT_THIS));
JMenuItem closeTab = new JMenuItem(String.valueOf(TranslatedStrings.CLOSE_TAB));
rightClickMenu.add(closeAllTabs);
rightClickMenu.add(closeTab);
button.setComponentPopupMenu(rightClickMenu);
button.addMouseListener(new MouseClickedListener(e ->
{
if (pane.indexOfTabComponent(CloseButtonComponent.this) != -1)
pane.remove(pane.indexOfTabComponent(CloseButtonComponent.this));
}));
closeTab.addActionListener(e ->
{
if (pane.indexOfTabComponent(CloseButtonComponent.this) != -1)
pane.remove(pane.indexOfTabComponent(CloseButtonComponent.this));
});
closeAllTabs.addActionListener(e ->
{
while (true) {
if (pane.getTabCount() <= 1)
return;
if (pane.indexOfTabComponent(CloseButtonComponent.this) != 0)
pane.remove(0);
else
pane.remove(1);
}
});
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
}
}

View File

@ -0,0 +1,572 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import com.github.weisj.darklaf.ui.tabbedpane.DarkTabbedPaneUI;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
public class DraggableTabbedPane extends JTabbedPane {
public static final long serialVersionUID = 1L;
private static final int LINEWIDTH = 3;
private static final String NAME = "TabTransferData";
private final DataFlavor FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME);
private static final GhostGlassPane s_glassPane = new GhostGlassPane();
private boolean m_isDrawRect = false;
private final Rectangle2D m_lineRect = new Rectangle2D.Double();
private final Color m_lineColor = new Color(0, 100, 255);
private TabAcceptor m_acceptor;
public DraggableTabbedPane() {
super(SwingConstants.TOP, SCROLL_TAB_LAYOUT);
this.putClientProperty(DarkTabbedPaneUI.KEY_DND, true);
/*if (!Configuration.showDarkLAFComponentIcons) {
final DragSourceListener dsl = new DragSourceListener() {
public void dragEnter(DragSourceDragEvent e) {
e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
}
public void dragExit(DragSourceEvent e) {
e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
s_glassPane.setPoint(new Point(-1000, -1000));
s_glassPane.repaint();
}
public void dragOver(DragSourceDragEvent e) {
TabTransferData data = getTabTransferData(e);
if (data == null) {
e.getDragSourceContext().setCursor(
DragSource.DefaultMoveNoDrop);
return;
}
e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
}
public void dragDropEnd(DragSourceDropEvent e) {
m_isDrawRect = false;
m_lineRect.setRect(0, 0, 0, 0);
if (hasGhost()) {
s_glassPane.setVisible(false);
s_glassPane.setImage(null);
}
}
public void dropActionChanged(DragSourceDragEvent e) {
}
};
final DragGestureListener dgl = e -> {
Point tabPt = e.getDragOrigin();
int dragTabIndex = indexAtLocation(tabPt.x, tabPt.y);
if (dragTabIndex < 0) {
return;
}
initGlassPane(e.getComponent(), e.getDragOrigin(), dragTabIndex);
try {
e.startDrag(DragSource.DefaultMoveDrop, new TabTransferable(DraggableTabbedPane.this, dragTabIndex), dsl);
} catch (InvalidDnDOperationException idoe) {
idoe.printStackTrace();
}
};
new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new CDropTargetListener(), true);
new DragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
m_acceptor = (a_component, a_index) -> true;
}*/
}
public TabAcceptor getAcceptor() {
return m_acceptor;
}
public void setAcceptor(TabAcceptor a_value) {
m_acceptor = a_value;
}
private TabTransferData getTabTransferData(DropTargetDropEvent a_event) {
Transferable t = a_event.getTransferable();
if (!t.isDataFlavorSupported(FLAVOR)) return null;
try {
return (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private TabTransferData getTabTransferData(DropTargetDragEvent a_event) {
Transferable t = a_event.getTransferable();
if (!t.isDataFlavorSupported(FLAVOR)) return null;
try {
return (TabTransferData) a_event.getTransferable().getTransferData(FLAVOR);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private TabTransferData getTabTransferData(DragSourceDragEvent a_event) {
Transferable t = a_event.getDragSourceContext().getTransferable();
if (!t.isDataFlavorSupported(FLAVOR)) return null;
try {
return (TabTransferData) a_event.getDragSourceContext().getTransferable().getTransferData(FLAVOR);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
class TabTransferable implements Transferable {
private TabTransferData m_data = null;
public TabTransferable(DraggableTabbedPane a_tabbedPane, int a_tabIndex) {
m_data = new TabTransferData(a_tabbedPane, a_tabIndex);
}
public Object getTransferData(DataFlavor flavor) {
return m_data;
}
public DataFlavor[] getTransferDataFlavors() {
DataFlavor[] f = new DataFlavor[1];
f[0] = FLAVOR;
return f;
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.getHumanPresentableName().equals(NAME);
}
}
static class TabTransferData {
private DraggableTabbedPane m_tabbedPane = null;
private int m_tabIndex = -1;
public TabTransferData() {
}
public TabTransferData(DraggableTabbedPane a_tabbedPane, int a_tabIndex) {
m_tabbedPane = a_tabbedPane;
m_tabIndex = a_tabIndex;
}
public DraggableTabbedPane getTabbedPane() {
return m_tabbedPane;
}
public void setTabbedPane(DraggableTabbedPane pane) {
m_tabbedPane = pane;
}
public int getTabIndex() {
return m_tabIndex;
}
public void setTabIndex(int index) {
m_tabIndex = index;
}
}
private Point buildGhostLocation(Point a_location) {
Point retval = new Point(a_location);
switch (getTabPlacement()) {
case JTabbedPane.TOP: {
retval.y = 1;
retval.x -= s_glassPane.getGhostWidth() / 2;
}
break;
case JTabbedPane.BOTTOM: {
retval.y = getHeight() - 1 - s_glassPane.getGhostHeight();
retval.x -= s_glassPane.getGhostWidth() / 2;
}
break;
case JTabbedPane.LEFT: {
retval.x = 1;
retval.y -= s_glassPane.getGhostHeight() / 2;
}
break;
case JTabbedPane.RIGHT: {
retval.x = getWidth() - 1 - s_glassPane.getGhostWidth();
retval.y -= s_glassPane.getGhostHeight() / 2;
}
break;
}
retval = SwingUtilities.convertPoint(DraggableTabbedPane.this, retval, s_glassPane);
return retval;
}
class CDropTargetListener implements DropTargetListener {
public void dragEnter(DropTargetDragEvent e) {
if (isDragAcceptable(e)) {
e.acceptDrag(e.getDropAction());
} else {
e.rejectDrag();
}
}
public void dragExit(DropTargetEvent e) {
m_isDrawRect = false;
}
public void dropActionChanged(DropTargetDragEvent e) {
}
public void dragOver(final DropTargetDragEvent e) {
TabTransferData data = getTabTransferData(e);
if (data == null) return;
if (getTabPlacement() == JTabbedPane.TOP
|| getTabPlacement() == JTabbedPane.BOTTOM) {
initTargetLeftRightLine(getTargetTabIndex(e.getLocation()), data);
} else {
initTargetTopBottomLine(getTargetTabIndex(e.getLocation()), data);
}
repaint();
if (hasGhost()) {
s_glassPane.setPoint(buildGhostLocation(e.getLocation()));
s_glassPane.repaint();
}
}
public void drop(DropTargetDropEvent a_event) {
if (isDropAcceptable(a_event)) {
convertTab(getTabTransferData(a_event), getTargetTabIndex(a_event.getLocation()));
a_event.dropComplete(true);
} else {
a_event.dropComplete(false);
}
m_isDrawRect = false;
repaint();
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(getSelectedIndex(), new CloseButtonComponent(DraggableTabbedPane.this));
}
public boolean isDragAcceptable(DropTargetDragEvent e) {
Transferable t = e.getTransferable();
if (t == null) {
return false;
}
DataFlavor[] flavor = e.getCurrentDataFlavors();
if (!t.isDataFlavorSupported(flavor[0])) {
return false;
}
TabTransferData data = getTabTransferData(e);
if (data == null) return false;
if (DraggableTabbedPane.this == data.getTabbedPane()
&& data.getTabIndex() >= 0) {
return true;
}
if (DraggableTabbedPane.this != data.getTabbedPane()) {
if (m_acceptor != null) {
return m_acceptor.isDropAcceptable(data.getTabbedPane(), data.getTabIndex());
}
}
return false;
}
public boolean isDropAcceptable(DropTargetDropEvent e) {
Transferable t = e.getTransferable();
if (t == null) {
return false;
}
DataFlavor[] flavor = e.getCurrentDataFlavors();
if (!t.isDataFlavorSupported(flavor[0])) {
return false;
}
TabTransferData data = getTabTransferData(e);
if (data == null) return false;
if (DraggableTabbedPane.this == data.getTabbedPane()
&& data.getTabIndex() >= 0) {
return true;
}
if (DraggableTabbedPane.this != data.getTabbedPane()) {
if (m_acceptor != null) {
return m_acceptor.isDropAcceptable(data.getTabbedPane(), data.getTabIndex());
}
}
return false;
}
}
private boolean m_hasGhost = true;
public void setPaintGhost(boolean flag) {
m_hasGhost = flag;
}
public boolean hasGhost() {
return m_hasGhost;
}
/**
* returns potential index for drop.
*
* @param a_point point given in the drop site component's coordinate
* @return returns potential index for drop.
*/
private int getTargetTabIndex(Point a_point) {
boolean isTopOrBottom = getTabPlacement() == JTabbedPane.TOP
|| getTabPlacement() == JTabbedPane.BOTTOM;
// if the pane is empty, the target index is always zero.
if (getTabCount() == 0) {
return 0;
}
for (int i = 0; i < getTabCount(); i++) {
Rectangle r = getBoundsAt(i);
if (isTopOrBottom) {
r.setRect(r.x - r.width / 2D, r.y, r.width, r.height);
} else {
r.setRect(r.x, r.y - r.height / 2D, r.width, r.height);
}
if (r.contains(a_point)) {
return i;
}
}
Rectangle r = getBoundsAt(getTabCount() - 1);
if (isTopOrBottom) {
int x = r.x + r.width / 2;
r.setRect(x, r.y, getWidth() - x, r.height);
} else {
int y = r.y + r.height / 2;
r.setRect(r.x, y, r.width, getHeight() - y);
}
return r.contains(a_point) ? getTabCount() : -1;
}
private void convertTab(TabTransferData a_data, int a_targetIndex) {
if (a_data == null) return;
DraggableTabbedPane source = a_data.getTabbedPane();
int sourceIndex = a_data.getTabIndex();
if (sourceIndex < 0) {
return;
}
Component cmp = source.getComponentAt(sourceIndex);
String str = source.getTitleAt(sourceIndex);
if (this != source) {
source.remove(sourceIndex);
if (a_targetIndex == getTabCount()) {
addTab(str, cmp);
} else {
if (a_targetIndex < 0) {
a_targetIndex = 0;
}
insertTab(str, null, cmp, null, a_targetIndex);
}
setSelectedComponent(cmp);
return;
}
if (a_targetIndex < 0 || sourceIndex == a_targetIndex) {
return;
}
if (a_targetIndex == getTabCount()) {
source.remove(sourceIndex);
addTab(str, cmp);
setSelectedIndex(getTabCount() - 1);
} else if (sourceIndex > a_targetIndex) {
source.remove(sourceIndex);
insertTab(str, null, cmp, null, a_targetIndex);
setSelectedIndex(a_targetIndex);
} else {
source.remove(sourceIndex);
insertTab(str, null, cmp, null, a_targetIndex - 1);
setSelectedIndex(a_targetIndex - 1);
}
}
private void initTargetLeftRightLine(int next, TabTransferData a_data) {
if (next < 0) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
return;
}
if (a_data == null) return;
if ((a_data.getTabbedPane() == this) && (a_data.getTabIndex() == next || next - a_data.getTabIndex() == 1)) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
} else if (getTabCount() == 0) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
} else if (next == 0) {
Rectangle rect = getBoundsAt(0);
m_lineRect.setRect(-LINEWIDTH / 2D, rect.y, LINEWIDTH, rect.height);
m_isDrawRect = true;
} else if (next == getTabCount()) {
Rectangle rect = getBoundsAt(getTabCount() - 1);
m_lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2D, rect.y, LINEWIDTH, rect.height);
m_isDrawRect = true;
} else {
Rectangle rect = getBoundsAt(next - 1);
m_lineRect.setRect(rect.x + rect.width - LINEWIDTH / 2D, rect.y, LINEWIDTH, rect.height);
m_isDrawRect = true;
}
}
private void initTargetTopBottomLine(int next, TabTransferData a_data) {
if (next < 0) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
return;
}
if (a_data == null) return;
if ((a_data.getTabbedPane() == this) && (a_data.getTabIndex() == next || next - a_data.getTabIndex() == 1)) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
} else if (getTabCount() == 0) {
m_lineRect.setRect(0, 0, 0, 0);
m_isDrawRect = false;
return;
} else if (next == getTabCount()) {
Rectangle rect = getBoundsAt(getTabCount() - 1);
m_lineRect.setRect(rect.x, rect.y + rect.height - LINEWIDTH / 2D, rect.width, LINEWIDTH);
m_isDrawRect = true;
} else if (next == 0) {
Rectangle rect = getBoundsAt(0);
m_lineRect.setRect(rect.x, -LINEWIDTH / 2D, rect.width, LINEWIDTH);
m_isDrawRect = true;
} else {
Rectangle rect = getBoundsAt(next - 1);
m_lineRect.setRect(rect.x, rect.y + rect.height - LINEWIDTH / 2D, rect.width, LINEWIDTH);
m_isDrawRect = true;
}
}
private void initGlassPane(Component c, Point tabPt, int a_tabIndex) {
getRootPane().setGlassPane(s_glassPane);
if (hasGhost()) {
Rectangle rect = getBoundsAt(a_tabIndex);
BufferedImage image = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
c.paint(g);
image = image.getSubimage(rect.x, rect.y, rect.width, rect.height);
s_glassPane.setImage(image);
}
s_glassPane.setPoint(buildGhostLocation(tabPt));
s_glassPane.setVisible(true);
}
private Rectangle getTabAreaBound() {
Rectangle lastTab = getUI().getTabBounds(this, getTabCount() - 1);
return new Rectangle(0, 0, getWidth(), lastTab.y + lastTab.height);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (m_isDrawRect) {
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(m_lineColor);
g2.fill(m_lineRect);
}
}
public interface TabAcceptor {
boolean isDropAcceptable(DraggableTabbedPane a_component, int a_index);
}
}
class GhostGlassPane extends JPanel {
public static final long serialVersionUID = 1L;
private final AlphaComposite m_composite;
private final Point m_location = new Point(0, 0);
private BufferedImage m_draggingGhost = null;
public GhostGlassPane() {
setOpaque(false);
m_composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);
}
public void setImage(BufferedImage draggingGhost) {
m_draggingGhost = draggingGhost;
}
public void setPoint(Point a_location) {
m_location.x = a_location.x;
m_location.y = a_location.y;
}
public int getGhostWidth() {
if (m_draggingGhost == null) {
return 0;
}
return m_draggingGhost.getWidth(this);
}
public int getGhostHeight() {
if (m_draggingGhost == null) {
return 0;
}
return m_draggingGhost.getHeight(this);
}
public void paintComponent(Graphics g) {
if (m_draggingGhost == null) {
return;
}
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(m_composite);
g2.drawImage(m_draggingGhost, (int) m_location.getX(), (int) m_location.getY(), null);
}
}

View File

@ -35,14 +35,13 @@ import javax.swing.plaf.basic.BasicButtonUI;
* Using CloseButton of darklaf instead. 4/17/2022
*/
@Deprecated
public class TabExitButton extends JButton implements ActionListener
{
public class TabExitButton extends JButton implements ActionListener {
private final TabbedPane tabbedPane;
private final int tabIndex;
private final String tabWorkingName;
public TabExitButton(TabbedPane tabbedPane, int tabIndex, String tabWorkingName)
{
public TabExitButton(TabbedPane tabbedPane, int tabIndex, String tabWorkingName) {
this.tabbedPane = tabbedPane;
this.tabIndex = tabIndex;
this.tabWorkingName = tabWorkingName;
@ -65,29 +64,26 @@ public class TabExitButton extends JButton implements ActionListener
addActionListener(this);
}
public int getTabIndex()
{
public int getTabIndex() {
return tabIndex;
}
@Override
public void actionPerformed(final ActionEvent e)
{
public void actionPerformed(final ActionEvent e) {
final int i = tabbedPane.tabs.indexOfTabComponent(tabbedPane);
if (i != -1)
{
if (i != -1) {
tabbedPane.tabs.remove(i);
}
}
// we don't want to update UI for this button
@Override
public void updateUI() { }
public void updateUI() {
}
// paint the cross
@Override
protected void paintComponent(final Graphics g)
{
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
final Graphics2D g2 = (Graphics2D) g.create();
// shift the image for pressed buttons
@ -97,8 +93,9 @@ public class TabExitButton extends JButton implements ActionListener
g2.setStroke(new BasicStroke(2));
g2.setColor(Color.BLACK);
if (getModel().isRollover())
if (getModel().isRollover()) {
g2.setColor(Color.MAGENTA);
}
final int delta = 6;
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
@ -106,18 +103,15 @@ public class TabExitButton extends JButton implements ActionListener
g2.dispose();
}
public TabbedPane getTabbedPane()
{
public TabbedPane getTabbedPane() {
return tabbedPane;
}
public String getTabWorkingName()
{
public String getTabWorkingName() {
return tabWorkingName;
}
public static long getSerialVersionUID()
{
public static long getSerialVersionUID() {
return serialVersionUID;
}

View File

@ -1,29 +1,13 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import com.github.weisj.darklaf.components.CloseButton;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.gui.components.ButtonHoverAnimation;
import the.bytecode.club.bytecodeviewer.gui.components.MaxWidthJLabel;
import the.bytecode.club.bytecodeviewer.gui.components.listeners.MouseClickedListener;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
import the.bytecode.club.bytecodeviewer.gui.util.DelayTabbedPaneThread;
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseListener;
/***************************************************************************
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
@ -50,206 +34,42 @@ import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
* @author WaterWolf
* @since 09/26/2011
*/
public class TabbedPane extends JPanel
{
public final JTabbedPane tabs;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
public final String tabName;
public final String fileContainerName;
public final ResourceViewer resource;
private static long lastMouseClick = System.currentTimeMillis();
public final static MouseListener buttonHoverAnimation = new ButtonHoverAnimation();
public static final Color BLANK_COLOR = new Color(0, 0, 0, 0);
public TabbedPane(int tabIndex, String tabWorkingName, String fileContainerName, String name, final JTabbedPane existingTabs, ResourceViewer resource)
{
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
public class TabbedPane extends JPanel {
this.tabName = name;
this.fileContainerName = fileContainerName;
this.resource = resource;
if (existingTabs == null)
throw new NullPointerException("TabbedPane is null");
public final JTabbedPane tabs;
public final JLabel label;
private DelayTabbedPaneThread probablyABadIdea;
private long startedDragging = 0;
public final String tabName;
public final String fileContainerName;
public final ResourceViewer resource;
private static long lastMouseClick = System.currentTimeMillis();
public final static MouseListener buttonHoverAnimation = new ButtonHoverAnimation();
public static final Color BLANK_COLOR = new Color(0, 0, 0, 0);
this.tabs = existingTabs;
setOpaque(false);
public TabbedPane(int tabIndex, String tabWorkingName, String fileContainerName, String name, final JTabbedPane existingTabs, ResourceViewer resource) {
// unset default FlowLayout' gaps
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
// make JLabel read titles from JTabbedPane
label = new MaxWidthJLabel(tabName, 400, 20);
this.tabName = name;
this.fileContainerName = fileContainerName;
this.resource = resource;
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// tab button
JButton exitButton = new CloseButton();
this.add(exitButton);
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
//define the right click pop-up menu
JPopupMenu rightClickMenu = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem(TranslatedStrings.CLOSE_ALL_BUT_THIS + ": " + name);
JMenuItem closeTab = new JMenuItem(TranslatedStrings.CLOSE_TAB + ": " + name);
rightClickMenu.add(closeAllTabs);
rightClickMenu.add(closeTab);
//setComponentPopupMenu(rightClickMenu);
exitButton.setComponentPopupMenu(rightClickMenu);
exitButton.addMouseListener(new MouseClickedListener(e ->
{
if (this.getTabIndex() != -1)
existingTabs.remove(this.getTabIndex());
}));
closeTab.addActionListener(e ->
{
if (this.getTabIndex() != -1)
existingTabs.remove(this.getTabIndex());
});
closeAllTabs.addActionListener(e ->
{
if (existingTabs == null)
throw new NullPointerException("TabbedPane is null");
while (true)
{
if (existingTabs.getTabCount() <= 1)
return;
if (this.getTabIndex() != 0)
existingTabs.remove(0);
else
existingTabs.remove(1);
}
});
//tab dragging
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
/*label.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {
}
@Override public void mouseExited(MouseEvent arg0) {
}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});*/
this.addMouseListener(new MouseListener() {
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent arg0) {}
@Override public void mouseExited(MouseEvent arg0) {}
@Override public void mousePressed(MouseEvent e) {
onMousePressed(e);
}
@Override public void mouseReleased(MouseEvent e) {
stopDragging(e.getX(), e.getY());
}
});
}
//middle click close
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
{
this.addMouseListener(new MouseAdapter()
{
@Override
public void mouseReleased(MouseEvent e)
{
if (e.getButton() != MouseEvent.BUTTON2)
return;
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
if (i != -1)
existingTabs.remove(i);
}
});
}
}
this.tabs = existingTabs;
setOpaque(false);
private void stopDragging(int mouseX, int mouseY)
{
if (System.currentTimeMillis() - startedDragging >= 210)
{
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
System.out.println("debug-5: " + mouseX + ", " + mouseY);
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
int index = -1;
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
index = i; //replace this tabs position
}
// make JLabel read titles from JTabbedPane
label = new MaxWidthJLabel(tabName, 400, 20);
if (index == -1)
{
for (int i = 0; i < totalTabs; i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
//do some check to see if it's past the X or Y
if (c != null) {
System.out.println("debug-6: " + c.getBounds());
}
}
}
this.add(label);
// add more space between the label and the button
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
// add more space to the top of the component
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
}
if (index != -1)
{
BytecodeViewer.viewer.workPane.tabs.remove(this);
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
}
}
SwingUtilities.invokeLater(() ->
{
label.setBackground(BLANK_COLOR);
label.updateUI();
});
}
public void onMousePressed(MouseEvent e)
{
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
if(e.getButton() == 1)
{
startedDragging = System.currentTimeMillis();
//dragging = true;
if (probablyABadIdea != null)
probablyABadIdea.stopped = true;
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
probablyABadIdea.start();
repaint();
System.out.println(e.getX()+", "+e.getY());
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if(c != null && bounds.intersects(c.getBounds()))
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
}
}
else
{
stopDragging(e.getX(), e.getY());
}
}
private static final long serialVersionUID = -4774885688297538774L;
public int getTabIndex() {
return tabs.indexOfTabComponent(this);
}
private static final long serialVersionUID = -4774885688297538774L;
}

View File

@ -1,18 +1,5 @@
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ClassViewer;
@ -24,6 +11,13 @@ import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJButton;
import the.bytecode.club.bytecodeviewer.translation.components.TranslatedVisibleComponent;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;
import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
/***************************************************************************
@ -45,219 +39,205 @@ import static the.bytecode.club.bytecodeviewer.Constants.BLOCK_TAB_MENU;
***************************************************************************/
/**
* The pane that contains all of the resources as tabs.
* This pane contains all the resources, as tabs.
*
* @author Konloch
* @author WaterWolf
* @since 09/26/2011
*/
public class Workspace extends TranslatedVisibleComponent
{
public final JTabbedPane tabs;
public final JPanel buttonPanel;
public final JButton refreshClass;
public final Set<String> openedTabs = new HashSet<>();
public class Workspace extends TranslatedVisibleComponent {
public Workspace()
{
super("Workspace", TranslatedComponents.WORK_SPACE);
public JTabbedPane tabs;
public final JPanel buttonPanel;
public final JButton refreshClass;
public final Set<String> openedTabs = new HashSet<>();
this.tabs = new JTabbedPane();
JPopupMenu popUp = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This");
JMenuItem closeTab = new JMenuItem("Close Tab");
closeTab.addActionListener(e ->
{
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabExitButton.getTabIndex();
if (index != -1)
tabs.remove(index);
});
closeAllTabs.addActionListener(e ->
{
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabExitButton.getTabIndex();
while (true)
{
if (tabs.getTabCount() <= 1)
return;
if (index != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
tabs.addMouseListener(new MouseListener()
{
@Override
public void mouseClicked(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent arg0) { }
@Override
public void mouseExited(MouseEvent arg0) { }
public Workspace() {
super("Workspace", TranslatedComponents.WORK_SPACE);
@Override
public void mousePressed(MouseEvent e)
{
if (BLOCK_TAB_MENU)
return;
if (e.getButton() == 3)
{
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
{
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds()))
{
popUp.setVisible(true);
closeAllTabs.setText(TranslatedStrings.CLOSE_TAB + ": " + ((TabbedPane) c).tabName);
closeTab.setText(TranslatedStrings.CLOSE_TAB + ": " + ((TabbedPane) c).tabName);
}
else
{
popUp.setVisible(false);
}
}
}
}
this.tabs = new DraggableTabbedPane();
@Override
public void mouseReleased(MouseEvent e) { }
});
JPopupMenu popUp = new JPopupMenu();
JMenuItem closeAllTabs = new JMenuItem("Close All But This");
JMenuItem closeTab = new JMenuItem("Close Tab");
closeTab.addActionListener(e ->
{
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu) ((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabExitButton.getTabIndex();
popUp.add(closeAllTabs);
popUp.add(closeTab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(popUp);
if (index != -1)
tabs.remove(index);
});
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
closeAllTabs.addActionListener(e ->
{
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu) ((JMenuItem) e.getSource()).getParent()).getInvoker();
final int index = tabExitButton.getTabIndex();
buttonPanel = new JPanel(new FlowLayout());
while (true) {
if (tabs.getTabCount() <= 1)
return;
refreshClass = new TranslatedJButton("Refresh", TranslatedComponents.REFRESH);
refreshClass.addActionListener((event)->
{
refreshClass.setEnabled(false);
Thread t = new Thread(() -> new WorkspaceRefresh(event).run(), "Refresh");
t.start();
});
if (index != 0)
tabs.remove(0);
else
tabs.remove(1);
}
});
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
tabs.addContainerListener(new TabRemovalEvent());
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
@Override
public void mouseEntered(MouseEvent arg0) {
}
this.setVisible(true);
}
@Override
public void mouseExited(MouseEvent arg0) {
}
//load class resources
public void addClassResource(final ResourceContainer container, final String name)
{
addResource(container, name, new ClassViewer(container, name));
}
@Override
public void mousePressed(MouseEvent e) {
if (BLOCK_TAB_MENU)
return;
//Load file resources
public void addFileResource(final ResourceContainer container, final String name)
{
addResource(container, name, new FileViewer(container, name));
}
private void addResource(final ResourceContainer container, final String name, final ResourceViewer resourceView)
{
// Warn user and prevent 'nothing' from opening if no Decompiler is selected
if(BytecodeViewer.viewer.viewPane1.getSelectedDecompiler() == Decompiler.NONE &&
BytecodeViewer.viewer.viewPane2.getSelectedDecompiler() == Decompiler.NONE &&
BytecodeViewer.viewer.viewPane3.getSelectedDecompiler() == Decompiler.NONE)
{
BytecodeViewer.showMessage(TranslatedStrings.SUGGESTED_FIX_NO_DECOMPILER_WARNING.toString());
return;
}
//unlock the refresh button
BytecodeViewer.viewer.workPane.refreshClass.setEnabled(true);
final String workingName = container.getWorkingName(name);
//create a new tab if the resource isn't opened currently
if (!openedTabs.contains(workingName))
{
addResourceToTab(resourceView, workingName, container.name, name);
}
else //if the resource is already opened select this tab as the active one
{
//TODO openedTabs could be changed to a HashMap<String, Integer> for faster lookups
//search through each tab
for(int i = 0; i < tabs.getTabCount(); i++)
{
//find the matching resource and open it
ResourceViewer tab = ((TabbedPane)tabs.getTabComponentAt(i)).resource;
if(tab.resource.workingName.equals(workingName))
{
tabs.setSelectedIndex(i);
break;
}
}
}
}
public void addResourceToTab(ResourceViewer resourceView, String workingName, String containerName, String name)
{
//start processing the resource to be viewed
if(resourceView instanceof ClassViewer)
resourceView.refresh(null);
//add the resource view to the tabs
tabs.add(resourceView);
//get the resource view index
final int tabIndex = tabs.indexOfComponent(resourceView);
//create a new tabbed pane
TabbedPane tabbedPane = new TabbedPane(tabIndex, workingName, containerName, name, tabs, resourceView);
resourceView.tabbedPane = tabbedPane;
resourceView.resource.workingName = workingName;
//set the tabs index
tabs.setTabComponentAt(tabIndex, tabbedPane);
//open the tab that was just added
tabs.setSelectedIndex(tabIndex);
//set resource as opened in a tab
openedTabs.add(workingName);
//refresh the tab title
resourceView.refreshTitle();
}
if (e.getButton() == 3) {
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
public ResourceViewer getActiveResource() {
return (ResourceViewer) tabs.getSelectedComponent();
}
for (int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++) {
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
if (c != null && bounds.intersects(c.getBounds())) {
popUp.setVisible(true);
closeAllTabs.setText(TranslatedStrings.CLOSE_TAB + ": " + ((TabbedPane) c).tabName);
closeTab.setText(TranslatedStrings.CLOSE_TAB + ": " + ((TabbedPane) c).tabName);
} else {
popUp.setVisible(false);
}
}
}
}
public Component[] getLoadedViewers() {
return tabs.getComponents();
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
public void resetWorkspace()
{
tabs.removeAll();
tabs.updateUI();
}
private static final long serialVersionUID = 6542337997679487946L;
popUp.add(closeAllTabs);
popUp.add(closeTab);
if (!BLOCK_TAB_MENU)
tabs.setComponentPopupMenu(popUp);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tabs, BorderLayout.CENTER);
buttonPanel = new JPanel(new FlowLayout());
refreshClass = new TranslatedJButton("Refresh", TranslatedComponents.REFRESH);
refreshClass.addActionListener((event) ->
{
refreshClass.setEnabled(false);
Thread t = new Thread(() -> new WorkspaceRefresh(event).run(), "Refresh");
t.start();
});
buttonPanel.add(refreshClass);
buttonPanel.setVisible(false);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
tabs.addContainerListener(new TabRemovalEvent());
tabs.addChangeListener(arg0 -> buttonPanel.setVisible(tabs.getSelectedIndex() != -1));
this.setVisible(true);
}
//load class resources
public void addClassResource(final ResourceContainer container, final String name) {
addResource(container, name, new ClassViewer(container, name));
}
//Load file resources
public void addFileResource(final ResourceContainer container, final String name) {
addResource(container, name, new FileViewer(container, name));
}
private void addResource(final ResourceContainer container, final String name, final ResourceViewer resourceView) {
// Warn user and prevent 'nothing' from opening if no Decompiler is selected
if (BytecodeViewer.viewer.viewPane1.getSelectedDecompiler() == Decompiler.NONE &&
BytecodeViewer.viewer.viewPane2.getSelectedDecompiler() == Decompiler.NONE &&
BytecodeViewer.viewer.viewPane3.getSelectedDecompiler() == Decompiler.NONE) {
BytecodeViewer.showMessage(TranslatedStrings.SUGGESTED_FIX_NO_DECOMPILER_WARNING.toString());
return;
}
//unlock the refresh button
BytecodeViewer.viewer.workPane.refreshClass.setEnabled(true);
final String workingName = container.getWorkingName(name);
//create a new tab if the resource isn't opened currently
if (!openedTabs.contains(workingName)) {
addResourceToTab(resourceView, workingName, container.name, name);
} else //if the resource is already opened select this tab as the active one
{
//TODO openedTabs could be changed to a HashMap<String, Integer> for faster lookups
//search through each tab
for (int i = 0; i < tabs.getTabCount(); i++) {
//find the matching resource and open it
ResourceViewer tab = (ResourceViewer) tabs.getComponentAt(i);
if (tab.resource.workingName.equals(workingName)) {
tabs.setSelectedIndex(i);
break;
}
}
}
}
public void addResourceToTab(ResourceViewer resourceView, String workingName, String containerName, String name) {
//start processing the resource to be viewed
if (resourceView instanceof ClassViewer)
resourceView.refresh(null);
//add the resource view to the tabs
tabs.add(resourceView);
//get the resource view index
final int tabIndex = tabs.indexOfComponent(resourceView);
//create a new tabbed pane
resourceView.tabbedPane = new TabbedPane(tabIndex, workingName, containerName, name, tabs, resourceView);
resourceView.resource.workingName = workingName;
//set the tabs index
tabs.setTabComponentAt(tabIndex, new CloseButtonComponent(tabs));
//open the tab that was just added
tabs.setSelectedIndex(tabIndex);
//set resource as opened in a tab
openedTabs.add(workingName);
//refresh the tab title
resourceView.refreshTitle();
}
public ResourceViewer getActiveResource() {
return (ResourceViewer) tabs.getSelectedComponent();
}
public Component[] getLoadedViewers() {
return tabs.getComponents();
}
public void resetWorkspace() {
tabs.removeAll();
tabs.updateUI();
}
private static final long serialVersionUID = 6542337997679487946L;
}

View File

@ -14,6 +14,7 @@ import the.bytecode.club.bytecodeviewer.translation.components.TranslatedJLabel;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.List;
/***************************************************************************
@ -67,11 +68,13 @@ public class MemberWithAnnotationSearch implements SearchPanel {
if (srchText.isEmpty()) return;
node.fields.stream().filter(fn -> hasAnnotation(srchText, fn.invisibleAnnotations, fn.visibleAnnotations)).forEach(fn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, null, fn, fn.name + " " + fn.desc, "")));
node.methods.stream().filter(mn -> hasAnnotation(srchText, mn.invisibleAnnotations, mn.visibleAnnotations)).forEach(mn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, mn, null, mn.name + mn.desc, "")));
node.fields.stream().filter(fn -> hasAnnotation(srchText, Arrays.asList(fn.invisibleAnnotations, fn.visibleAnnotations)))
.forEach(fn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, null, fn, fn.name + " " + fn.desc, "")));
node.methods.stream().filter(mn -> hasAnnotation(srchText, Arrays.asList(mn.invisibleAnnotations, mn.visibleAnnotations)))
.forEach(mn -> BytecodeViewer.viewer.searchBoxPane.treeRoot.add(new LDCSearchTreeNodeResult(container, resourceWorkingName, node, mn, null, mn.name + mn.desc, "")));
}
public static boolean hasAnnotation(String annotation, List<AnnotationNode>... annoLists) {
public static boolean hasAnnotation(String annotation, List<List<AnnotationNode>> annoLists) {
if (annoLists == null) return false;
for (List<AnnotationNode> annos : annoLists) {
if (annos == null) continue;

View File

@ -1,6 +1,10 @@
package the.bytecode.club.bytecodeviewer.util;
import com.googlecode.d2j.dex.Dex2jar;
import com.googlecode.d2j.dex.DexExceptionHandler;
import com.googlecode.d2j.Method;
import com.googlecode.d2j.node.DexMethodNode;
import org.objectweb.asm.MethodVisitor;
import java.io.File;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -38,7 +42,16 @@ public class Dex2Jar {
*/
public static synchronized void dex2Jar(File input, File output) {
try {
Dex2jar d2Jar = Dex2jar.from(input);
Dex2jar d2Jar = Dex2jar.from(input)
.withExceptionHandler(new DexExceptionHandler() {
public void handleFileException(Exception e) {
e.printStackTrace();
}
public void handleMethodTranslateException(Method method, DexMethodNode methodNode, MethodVisitor mv, Exception e) {
e.printStackTrace();
}
});
d2Jar.to(output.toPath());
} catch (com.googlecode.d2j.DexException e) {
e.printStackTrace();

View File

@ -1,7 +1,7 @@
package the.bytecode.club.bytecodeviewer.util;
import java.net.URL;
import me.konloch.kontainer.io.HTTPRequest;
import com.konloch.httprequest.HTTPRequest;
import the.bytecode.club.bytecodeviewer.Configuration;
/***************************************************************************