Commit e733c917 authored by Skylot's avatar Skylot

gui: support images view/unpack

parent 4e982722
...@@ -193,9 +193,21 @@ limitations under the License. ...@@ -193,9 +193,21 @@ limitations under the License.
******************************************************************************* *******************************************************************************
Image Viewer (https://github.com/kazocsaba/imageviewer)
*******************************************************************************
Copyright (c) 2008-2012 Kazó Csaba
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*******************************************************************************
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
Icons copied from several places: Icons copied from several places:
- Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html) - Eclipse Project (JDT UI) - licensed under EPL v1.0 (http://www.eclipse.org/legal/epl-v10.html)
- famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed - famfamfam silk icon set (http://www.famfamfam.com/lab/icons/silk/) - licensed
under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/) under Creative Commons Attribution 2.5 License (http://creativecommons.org/licenses/by/2.5/)
JFontChooser Component - http://sourceforge.jp/projects/jfontchooser/
...@@ -48,7 +48,7 @@ public class ResourceFile { ...@@ -48,7 +48,7 @@ public class ResourceFile {
return type; return type;
} }
public ResContainer getContent() { public ResContainer loadContent() {
return ResourcesLoader.loadContent(decompiler, this); return ResourcesLoader.loadContent(decompiler, this);
} }
......
...@@ -13,7 +13,7 @@ public class ResourceFileContent extends ResourceFile { ...@@ -13,7 +13,7 @@ public class ResourceFileContent extends ResourceFile {
} }
@Override @Override
public ResContainer getContent() { public ResContainer loadContent() {
return ResContainer.singleFile(getName(), content); return ResContainer.singleFile(getName(), content);
} }
} }
...@@ -36,13 +36,13 @@ public enum ResourceType { ...@@ -36,13 +36,13 @@ public enum ResourceType {
case CODE: case CODE:
case LIB: case LIB:
case FONT: case FONT:
case IMG:
case UNKNOWN: case UNKNOWN:
return false; return false;
case MANIFEST: case MANIFEST:
case XML: case XML:
case ARSC: case ARSC:
case IMG:
return true; return true;
} }
return false; return false;
......
...@@ -108,6 +108,9 @@ public final class ResourcesLoader { ...@@ -108,6 +108,9 @@ public final class ResourcesLoader {
case ARSC: case ARSC:
return new ResTableParser().decodeFiles(inputStream); return new ResTableParser().decodeFiles(inputStream);
case IMG:
return ResContainer.singleImageFile(rf.getName(), inputStream);
} }
if (size > LOAD_SIZE_LIMIT) { if (size > LOAD_SIZE_LIMIT) {
return ResContainer.singleFile(rf.getName(), return ResContainer.singleFile(rf.getName(),
......
...@@ -20,7 +20,6 @@ import static jadx.core.utils.files.FileUtils.close; ...@@ -20,7 +20,6 @@ import static jadx.core.utils.files.FileUtils.close;
public class CodeWriter { public class CodeWriter {
private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class); private static final Logger LOG = LoggerFactory.getLogger(CodeWriter.class);
private static final int MAX_FILENAME_LENGTH = 128;
public static final String NL = System.getProperty("line.separator"); public static final String NL = System.getProperty("line.separator");
public static final String INDENT = " "; public static final String INDENT = " ";
...@@ -286,22 +285,10 @@ public class CodeWriter { ...@@ -286,22 +285,10 @@ public class CodeWriter {
if (code == null) { if (code == null) {
finish(); finish();
} }
String name = file.getName(); File outFile = FileUtils.prepareFile(file);
if (name.length() > MAX_FILENAME_LENGTH) {
int dotIndex = name.indexOf('.');
int cutAt = MAX_FILENAME_LENGTH - name.length() + dotIndex - 1;
if (cutAt <= 0) {
name = name.substring(0, MAX_FILENAME_LENGTH - 1);
} else {
name = name.substring(0, cutAt) + name.substring(dotIndex);
}
file = new File(file.getParentFile(), name);
}
PrintWriter out = null; PrintWriter out = null;
try { try {
FileUtils.makeDirsForFile(file); out = new PrintWriter(outFile, "UTF-8");
out = new PrintWriter(file, "UTF-8");
out.println(code); out.println(code);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Save file error", e); LOG.error("Save file error", e);
...@@ -309,4 +296,5 @@ public class CodeWriter { ...@@ -309,4 +296,5 @@ public class CodeWriter {
close(out); close(out);
} }
} }
} }
...@@ -12,6 +12,7 @@ import java.io.OutputStream; ...@@ -12,6 +12,7 @@ import java.io.OutputStream;
import java.util.jar.JarEntry; import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -19,6 +20,7 @@ public class FileUtils { ...@@ -19,6 +20,7 @@ public class FileUtils {
private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class); private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);
public static final int READ_BUFFER_SIZE = 8 * 1024; public static final int READ_BUFFER_SIZE = 8 * 1024;
private static final int MAX_FILENAME_LENGTH = 128;
private FileUtils() { private FileUtils() {
} }
...@@ -81,4 +83,21 @@ public class FileUtils { ...@@ -81,4 +83,21 @@ public class FileUtils {
LOG.error("Close exception for {}", c, e); LOG.error("Close exception for {}", c, e);
} }
} }
@NotNull
public static File prepareFile(File file) {
String name = file.getName();
if (name.length() > MAX_FILENAME_LENGTH) {
int dotIndex = name.indexOf('.');
int cutAt = MAX_FILENAME_LENGTH - name.length() + dotIndex - 1;
if (cutAt <= 0) {
name = name.substring(0, MAX_FILENAME_LENGTH - 1);
} else {
name = name.substring(0, cutAt) + name.substring(dotIndex);
}
file = new File(file.getParentFile(), name);
}
makeDirsForFile(file);
return file;
}
} }
package jadx.core.xmlgen; package jadx.core.xmlgen;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import jadx.core.utils.exceptions.JadxRuntimeException;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ResContainer implements Comparable<ResContainer> { public class ResContainer implements Comparable<ResContainer> {
private final String name; private final String name;
private final List<ResContainer> subFiles;
@Nullable @Nullable
private CodeWriter content; private CodeWriter content;
@Nullable
private BufferedImage image;
private final List<ResContainer> subFiles; private ResContainer(String name, List<ResContainer> subFiles) {
private ResContainer(String name, @Nullable CodeWriter content, List<ResContainer> subFiles) {
this.name = name; this.name = name;
this.content = content;
this.subFiles = subFiles; this.subFiles = subFiles;
} }
public static ResContainer singleFile(String name, CodeWriter content) { public static ResContainer singleFile(String name, CodeWriter content) {
return new ResContainer(name, content, Collections.<ResContainer>emptyList()); ResContainer resContainer = new ResContainer(name, Collections.<ResContainer>emptyList());
resContainer.content = content;
return resContainer;
}
public static ResContainer singleImageFile(String name, InputStream content) {
ResContainer resContainer = new ResContainer(name, Collections.<ResContainer>emptyList());
try {
resContainer.image = ImageIO.read(content);
} catch (Exception e) {
throw new JadxRuntimeException("Image load error", e);
}
return resContainer;
} }
public static ResContainer multiFile(String name) { public static ResContainer multiFile(String name) {
return new ResContainer(name, null, new ArrayList<ResContainer>()); return new ResContainer(name, new ArrayList<ResContainer>());
} }
public String getName() { public String getName() {
...@@ -48,21 +66,22 @@ public class ResContainer implements Comparable<ResContainer> { ...@@ -48,21 +66,22 @@ public class ResContainer implements Comparable<ResContainer> {
this.content = content; this.content = content;
} }
@Nullable
public BufferedImage getImage() {
return image;
}
public List<ResContainer> getSubFiles() { public List<ResContainer> getSubFiles() {
return subFiles; return subFiles;
} }
@Override @Override
public int compareTo(ResContainer o) { public int compareTo(@NotNull ResContainer o) {
return name.compareTo(o.name); return name.compareTo(o.name);
} }
@Override @Override
public String toString() { public String toString() {
return "ResContainer{" + return "Res{" + name + ", subFiles=" + subFiles + "}";
"name='" + name + "'" +
", content=" + content +
", subFiles=" + subFiles +
"}";
} }
} }
...@@ -4,10 +4,21 @@ import jadx.api.ResourceFile; ...@@ -4,10 +4,21 @@ import jadx.api.ResourceFile;
import jadx.api.ResourceType; import jadx.api.ResourceType;
import jadx.core.codegen.CodeWriter; import jadx.core.codegen.CodeWriter;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.List; import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static jadx.core.utils.files.FileUtils.prepareFile;
public class ResourcesSaver implements Runnable { public class ResourcesSaver implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(ResourcesSaver.class);
private final ResourceFile resourceFile; private final ResourceFile resourceFile;
private File outDir; private File outDir;
...@@ -21,7 +32,7 @@ public class ResourcesSaver implements Runnable { ...@@ -21,7 +32,7 @@ public class ResourcesSaver implements Runnable {
if (!ResourceType.isSupportedForUnpack(resourceFile.getType())) { if (!ResourceType.isSupportedForUnpack(resourceFile.getType())) {
return; return;
} }
ResContainer rc = resourceFile.getContent(); ResContainer rc = resourceFile.loadContent();
if (rc != null) { if (rc != null) {
saveResources(rc); saveResources(rc);
} }
...@@ -33,14 +44,32 @@ public class ResourcesSaver implements Runnable { ...@@ -33,14 +44,32 @@ public class ResourcesSaver implements Runnable {
} }
List<ResContainer> subFiles = rc.getSubFiles(); List<ResContainer> subFiles = rc.getSubFiles();
if (subFiles.isEmpty()) { if (subFiles.isEmpty()) {
CodeWriter cw = rc.getContent(); save(rc, outDir);
if (cw != null) {
cw.save(new File(outDir, rc.getFileName()));
}
} else { } else {
for (ResContainer subFile : subFiles) { for (ResContainer subFile : subFiles) {
saveResources(subFile); saveResources(subFile);
} }
} }
} }
private void save(ResContainer rc, File outDir) {
File outFile = new File(outDir, rc.getFileName());
BufferedImage image = rc.getImage();
if (image != null) {
String ext = FilenameUtils.getExtension(outFile.getName());
try {
outFile = prepareFile(outFile);
ImageIO.write(image, ext, outFile);
} catch (IOException e) {
LOG.error("Failed to save image: {}", rc.getName(), e);
}
return;
}
CodeWriter cw = rc.getContent();
if (cw != null) {
cw.save(outFile);
return;
}
LOG.warn("Resource '{}' not saved, unknown type", rc.getName());
}
} }
...@@ -8,6 +8,7 @@ dependencies { ...@@ -8,6 +8,7 @@ dependencies {
compile 'com.fifesoft:rsyntaxtextarea:2.5.8' compile 'com.fifesoft:rsyntaxtextarea:2.5.8'
compile 'com.google.code.gson:gson:2.3.1' compile 'com.google.code.gson:gson:2.3.1'
compile files('libs/jfontchooser-1.0.5.jar') compile files('libs/jfontchooser-1.0.5.jar')
compile 'hu.kazocsaba:image-viewer:1.2.3'
} }
applicationDistribution.with { applicationDistribution.with {
......
...@@ -81,7 +81,7 @@ public class JResource extends JNode implements Comparable<JResource> { ...@@ -81,7 +81,7 @@ public class JResource extends JNode implements Comparable<JResource> {
if (!loaded && resFile != null && type == JResType.FILE) { if (!loaded && resFile != null && type == JResType.FILE) {
loaded = true; loaded = true;
if (isSupportedForView(resFile.getType())) { if (isSupportedForView(resFile.getType())) {
ResContainer rc = resFile.getContent(); ResContainer rc = resFile.loadContent();
if (rc != null) { if (rc != null) {
addSubFiles(rc, this, 0); addSubFiles(rc, this, 0);
} }
...@@ -149,9 +149,13 @@ public class JResource extends JNode implements Comparable<JResource> { ...@@ -149,9 +149,13 @@ public class JResource extends JNode implements Comparable<JResource> {
@Override @Override
public String getSyntaxName() { public String getSyntaxName() {
if (resFile == null) {
return null;
}
switch (resFile.getType()) { switch (resFile.getType()) {
case CODE: case CODE:
return super.getSyntaxName(); return super.getSyntaxName();
case MANIFEST: case MANIFEST:
case XML: case XML:
return SyntaxConstants.SYNTAX_STYLE_XML; return SyntaxConstants.SYNTAX_STYLE_XML;
...@@ -205,23 +209,27 @@ public class JResource extends JNode implements Comparable<JResource> { ...@@ -205,23 +209,27 @@ public class JResource extends JNode implements Comparable<JResource> {
return FILE_ICON; return FILE_ICON;
} }
private boolean isSupportedForView(ResourceType type) { public static boolean isSupportedForView(ResourceType type) {
switch (type) { switch (type) {
case CODE: case CODE:
case FONT: case FONT:
case IMG:
case LIB: case LIB:
return false; return false;
case MANIFEST: case MANIFEST:
case XML: case XML:
case ARSC: case ARSC:
case IMG:
case UNKNOWN: case UNKNOWN:
return true; return true;
} }
return true; return true;
} }
public ResourceFile getResFile() {
return resFile;
}
@Override @Override
public JClass getJParent() { public JClass getJParent() {
return null; return null;
......
...@@ -52,7 +52,11 @@ public class JRoot extends JNode { ...@@ -52,7 +52,11 @@ public class JRoot extends JNode {
String name = parts[i]; String name = parts[i];
JResource subRF = getResourceByName(curRf, name); JResource subRF = getResourceByName(curRf, name);
if (subRF == null) { if (subRF == null) {
subRF = new JResource(rf, name, i != count - 1 ? JResType.DIR : JResType.FILE); if (i != count - 1) {
subRF = new JResource(null, name, JResType.DIR);
} else {
subRF = new JResource(rf, name, JResType.FILE);
}
curRf.getFiles().add(subRF); curRf.getFiles().add(subRF);
} }
curRf = subRF; curRf = subRF;
......
...@@ -34,18 +34,18 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes; ...@@ -34,18 +34,18 @@ import org.fife.ui.rsyntaxtextarea.TokenTypes;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
class ContentArea extends RSyntaxTextArea { class CodeArea extends RSyntaxTextArea {
private static final Logger LOG = LoggerFactory.getLogger(ContentArea.class); private static final Logger LOG = LoggerFactory.getLogger(CodeArea.class);
private static final long serialVersionUID = 6312736869579635796L; private static final long serialVersionUID = 6312736869579635796L;
public static final Color BACKGROUND = new Color(0xFAFAFA); public static final Color BACKGROUND = new Color(0xFAFAFA);
public static final Color JUMP_TOKEN_FGD = new Color(0x491BA1); public static final Color JUMP_TOKEN_FGD = new Color(0x491BA1);
private final ContentPanel contentPanel; private final CodePanel contentPanel;
private final JNode node; private final JNode node;
ContentArea(ContentPanel panel) { CodeArea(CodePanel panel) {
this.contentPanel = panel; this.contentPanel = panel;
this.node = panel.getNode(); this.node = panel.getNode();
...@@ -76,8 +76,8 @@ class ContentArea extends RSyntaxTextArea { ...@@ -76,8 +76,8 @@ class ContentArea extends RSyntaxTextArea {
setText(node.getContent()); setText(node.getContent());
} }
private void addMenuItems(ContentArea contentArea, JClass jCls) { private void addMenuItems(CodeArea codeArea, JClass jCls) {
Action findUsage = new FindUsageAction(contentArea, jCls); Action findUsage = new FindUsageAction(codeArea, jCls);
// TODO: hotkey works only when popup menu is shown // TODO: hotkey works only when popup menu is shown
// findUsage.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_F7, KeyEvent.ALT_DOWN_MASK)); // findUsage.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_F7, KeyEvent.ALT_DOWN_MASK));
...@@ -197,14 +197,14 @@ class ContentArea extends RSyntaxTextArea { ...@@ -197,14 +197,14 @@ class ContentArea extends RSyntaxTextArea {
private class FindUsageAction extends AbstractAction implements PopupMenuListener { private class FindUsageAction extends AbstractAction implements PopupMenuListener {
private static final long serialVersionUID = 4692546569977976384L; private static final long serialVersionUID = 4692546569977976384L;
private final ContentArea contentArea; private final CodeArea codeArea;
private final JClass jCls; private final JClass jCls;
private JavaNode node; private JavaNode node;
public FindUsageAction(ContentArea contentArea, JClass jCls) { public FindUsageAction(CodeArea codeArea, JClass jCls) {
super("Find Usage"); super("Find Usage");
this.contentArea = contentArea; this.codeArea = codeArea;
this.jCls = jCls; this.jCls = jCls;
} }
...@@ -222,11 +222,11 @@ class ContentArea extends RSyntaxTextArea { ...@@ -222,11 +222,11 @@ class ContentArea extends RSyntaxTextArea {
@Override @Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) { public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
node = null; node = null;
Point pos = contentArea.getMousePosition(); Point pos = codeArea.getMousePosition();
if (pos != null) { if (pos != null) {
Token token = contentArea.viewToToken(pos); Token token = codeArea.viewToToken(pos);
if (token != null) { if (token != null) {
node = getJavaNodeAtOffset(jCls, contentArea, token.getOffset()); node = getJavaNodeAtOffset(jCls, codeArea, token.getOffset());
} }
} }
setEnabled(node != null); setEnabled(node != null);
......
package jadx.gui.ui;
import jadx.gui.treemodel.JNode;
import jadx.gui.utils.Utils;
import javax.swing.AbstractAction;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
class CodePanel extends ContentPanel {
private static final long serialVersionUID = 5310536092010045565L;
private final SearchBar searchBar;
private final CodeArea codeArea;
private final JScrollPane scrollPane;
CodePanel(TabbedPane panel, JNode jnode) {
super(panel, jnode);
codeArea = new CodeArea(this);
searchBar = new SearchBar(codeArea);
scrollPane = new JScrollPane(codeArea);
scrollPane.setRowHeaderView(new LineNumbers(codeArea));
setLayout(new BorderLayout());
add(searchBar, BorderLayout.NORTH);
add(scrollPane);
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK);
Utils.addKeyBinding(codeArea, key, "SearchAction", new SearchAction());
}
private class SearchAction extends AbstractAction {
private static final long serialVersionUID = 8650568214755387093L;
@Override
public void actionPerformed(ActionEvent e) {
searchBar.toggle();
}
}
@Override
public void loadSettings() {
codeArea.loadSettings();
}
TabbedPane getTabbedPane() {
return tabbedPane;
}
JNode getNode() {
return node;
}
SearchBar getSearchBar() {
return searchBar;
}
CodeArea getCodeArea() {
return codeArea;
}
JScrollPane getScrollPane() {
return scrollPane;
}
}
...@@ -162,7 +162,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -162,7 +162,7 @@ public abstract class CommonSearchDialog extends JDialog {
resultsTable.setShowHorizontalLines(false); resultsTable.setShowHorizontalLines(false);
resultsTable.setDragEnabled(false); resultsTable.setDragEnabled(false);
resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
resultsTable.setBackground(ContentArea.BACKGROUND); resultsTable.setBackground(CodeArea.BACKGROUND);
resultsTable.setColumnSelectionAllowed(false); resultsTable.setColumnSelectionAllowed(false);
resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
resultsTable.setAutoscrolls(false); resultsTable.setAutoscrolls(false);
...@@ -352,7 +352,7 @@ public abstract class CommonSearchDialog extends JDialog { ...@@ -352,7 +352,7 @@ public abstract class CommonSearchDialog extends JDialog {
comp.setBackground(selectedBackground); comp.setBackground(selectedBackground);
comp.setForeground(selectedForeground); comp.setForeground(selectedForeground);
} else { } else {
comp.setBackground(ContentArea.BACKGROUND); comp.setBackground(CodeArea.BACKGROUND);
comp.setForeground(foreground); comp.setForeground(foreground);
} }
} }
......
package jadx.gui.ui; package jadx.gui.ui;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.utils.Utils;
import javax.swing.AbstractAction;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.KeyStroke;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
class ContentPanel extends JPanel { abstract class ContentPanel extends JPanel {
private static final long serialVersionUID = 5310536092010045565L; protected final TabbedPane tabbedPane;
protected final JNode node;
private final TabbedPane tabbedPane; ContentPanel(TabbedPane panel, JNode jnode) {
private final JNode node;
private final SearchBar searchBar;
private final ContentArea contentArea;
private final JScrollPane scrollPane;
ContentPanel(TabbedPane panel, JNode node) {
tabbedPane = panel; tabbedPane = panel;
this.node = node; node = jnode;
contentArea = new ContentArea(this);
searchBar = new SearchBar(contentArea);
scrollPane = new JScrollPane(contentArea);
scrollPane.setRowHeaderView(new LineNumbers(contentArea));
setLayout(new BorderLayout());
add(searchBar, BorderLayout.NORTH);
add(scrollPane);
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK);
Utils.addKeyBinding(contentArea, key, "SearchAction", new SearchAction());
} }
private class SearchAction extends AbstractAction { public abstract void loadSettings();
private static final long serialVersionUID = 8650568214755387093L;
@Override
public void actionPerformed(ActionEvent e) {
searchBar.toggle();
}
}
TabbedPane getTabbedPane() { TabbedPane getTabbedPane() {
return tabbedPane; return tabbedPane;
...@@ -55,16 +23,4 @@ class ContentPanel extends JPanel { ...@@ -55,16 +23,4 @@ class ContentPanel extends JPanel {
JNode getNode() { JNode getNode() {
return node; return node;
} }
SearchBar getSearchBar() {
return searchBar;
}
ContentArea getContentArea() {
return contentArea;
}
JScrollPane getScrollPane() {
return scrollPane;
}
} }
package jadx.gui.ui;
import hu.kazocsaba.imageviewer.ImageViewer;
import jadx.api.ResourceFile;
import jadx.gui.treemodel.JResource;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
public class ImagePanel extends ContentPanel {
ImagePanel(TabbedPane panel, JResource res) {
super(panel, res);
ResourceFile resFile = res.getResFile();
BufferedImage img = resFile.loadContent().getImage();
ImageViewer imageViewer = new ImageViewer(img);
imageViewer.setZoomFactor(2.);
setLayout(new BorderLayout());
add(imageViewer.getComponent());
}
@Override
public void loadSettings() {
}
}
...@@ -32,18 +32,18 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -32,18 +32,18 @@ public class LineNumbers extends JPanel implements CaretListener {
private static final int HEIGHT = Integer.MAX_VALUE - 1000000; private static final int HEIGHT = Integer.MAX_VALUE - 1000000;
private static final Color FOREGROUND = Color.GRAY; private static final Color FOREGROUND = Color.GRAY;
private static final Color BACKGROUND = ContentArea.BACKGROUND; private static final Color BACKGROUND = CodeArea.BACKGROUND;
private static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0); private static final Color CURRENT_LINE_FOREGROUND = new Color(227, 0, 0);
private ContentArea contentArea; private CodeArea codeArea;
private boolean useSourceLines = true; private boolean useSourceLines = true;
private int lastDigits; private int lastDigits;
private int lastLine; private int lastLine;
private Map<String, FontMetrics> fonts; private Map<String, FontMetrics> fonts;
public LineNumbers(ContentArea component) { public LineNumbers(CodeArea component) {
this.contentArea = component; this.codeArea = component;
setFont(component.getFont()); setFont(component.getFont());
setBackground(BACKGROUND); setBackground(BACKGROUND);
setForeground(FOREGROUND); setForeground(FOREGROUND);
...@@ -70,7 +70,7 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -70,7 +70,7 @@ public class LineNumbers extends JPanel implements CaretListener {
} }
private void setPreferredWidth() { private void setPreferredWidth() {
Element root = contentArea.getDocument().getDefaultRootElement(); Element root = codeArea.getDocument().getDefaultRootElement();
int lines = root.getElementCount(); int lines = root.getElementCount();
int digits = Math.max(String.valueOf(lines).length(), 3); int digits = Math.max(String.valueOf(lines).length(), 3);
if (lastDigits != digits) { if (lastDigits != digits) {
...@@ -92,12 +92,12 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -92,12 +92,12 @@ public class LineNumbers extends JPanel implements CaretListener {
@Override @Override
public void paintComponent(Graphics g) { public void paintComponent(Graphics g) {
super.paintComponent(g); super.paintComponent(g);
FontMetrics fontMetrics = contentArea.getFontMetrics(contentArea.getFont()); FontMetrics fontMetrics = codeArea.getFontMetrics(codeArea.getFont());
Insets insets = getInsets(); Insets insets = getInsets();
int availableWidth = getSize().width - insets.left - insets.right; int availableWidth = getSize().width - insets.left - insets.right;
Rectangle clip = g.getClipBounds(); Rectangle clip = g.getClipBounds();
int rowStartOffset = contentArea.viewToModel(new Point(0, clip.y)); int rowStartOffset = codeArea.viewToModel(new Point(0, clip.y));
int endOffset = contentArea.viewToModel(new Point(0, clip.y + clip.height)); int endOffset = codeArea.viewToModel(new Point(0, clip.y + clip.height));
while (rowStartOffset <= endOffset) { while (rowStartOffset <= endOffset) {
try { try {
...@@ -111,7 +111,7 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -111,7 +111,7 @@ public class LineNumbers extends JPanel implements CaretListener {
int x = availableWidth - stringWidth + insets.left; int x = availableWidth - stringWidth + insets.left;
int y = getOffsetY(rowStartOffset, fontMetrics); int y = getOffsetY(rowStartOffset, fontMetrics);
g.drawString(lineNumber, x, y); g.drawString(lineNumber, x, y);
rowStartOffset = Utilities.getRowEnd(contentArea, rowStartOffset) + 1; rowStartOffset = Utilities.getRowEnd(codeArea, rowStartOffset) + 1;
} catch (Exception e) { } catch (Exception e) {
break; break;
} }
...@@ -119,19 +119,19 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -119,19 +119,19 @@ public class LineNumbers extends JPanel implements CaretListener {
} }
private boolean isCurrentLine(int rowStartOffset) { private boolean isCurrentLine(int rowStartOffset) {
int caretPosition = contentArea.getCaretPosition(); int caretPosition = codeArea.getCaretPosition();
Element root = contentArea.getDocument().getDefaultRootElement(); Element root = codeArea.getDocument().getDefaultRootElement();
return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition); return root.getElementIndex(rowStartOffset) == root.getElementIndex(caretPosition);
} }
protected String getTextLineNumber(int rowStartOffset) { protected String getTextLineNumber(int rowStartOffset) {
Element root = contentArea.getDocument().getDefaultRootElement(); Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset); int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index); Element line = root.getElement(index);
if (line.getStartOffset() == rowStartOffset) { if (line.getStartOffset() == rowStartOffset) {
int lineNumber = index + 1; int lineNumber = index + 1;
if (useSourceLines) { if (useSourceLines) {
Integer sourceLine = contentArea.getSourceLine(lineNumber); Integer sourceLine = codeArea.getSourceLine(lineNumber);
if (sourceLine != null) { if (sourceLine != null) {
return String.valueOf(sourceLine); return String.valueOf(sourceLine);
} }
...@@ -143,7 +143,7 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -143,7 +143,7 @@ public class LineNumbers extends JPanel implements CaretListener {
} }
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException { private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics) throws BadLocationException {
Rectangle r = contentArea.modelToView(rowStartOffset); Rectangle r = codeArea.modelToView(rowStartOffset);
if (r == null) { if (r == null) {
throw new BadLocationException("Can't get Y offset", rowStartOffset); throw new BadLocationException("Can't get Y offset", rowStartOffset);
} }
...@@ -156,7 +156,7 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -156,7 +156,7 @@ public class LineNumbers extends JPanel implements CaretListener {
if (fonts == null) { if (fonts == null) {
fonts = new HashMap<String, FontMetrics>(); fonts = new HashMap<String, FontMetrics>();
} }
Element root = contentArea.getDocument().getDefaultRootElement(); Element root = codeArea.getDocument().getDefaultRootElement();
int index = root.getElementIndex(rowStartOffset); int index = root.getElementIndex(rowStartOffset);
Element line = root.getElement(index); Element line = root.getElement(index);
for (int i = 0; i < line.getElementCount(); i++) { for (int i = 0; i < line.getElementCount(); i++) {
...@@ -168,7 +168,7 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -168,7 +168,7 @@ public class LineNumbers extends JPanel implements CaretListener {
FontMetrics fm = fonts.get(key); FontMetrics fm = fonts.get(key);
if (fm == null) { if (fm == null) {
Font font = new Font(fontFamily, Font.PLAIN, fontSize); Font font = new Font(fontFamily, Font.PLAIN, fontSize);
fm = contentArea.getFontMetrics(font); fm = codeArea.getFontMetrics(font);
fonts.put(key, fm); fonts.put(key, fm);
} }
descent = Math.max(descent, fm.getDescent()); descent = Math.max(descent, fm.getDescent());
...@@ -179,8 +179,8 @@ public class LineNumbers extends JPanel implements CaretListener { ...@@ -179,8 +179,8 @@ public class LineNumbers extends JPanel implements CaretListener {
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
int caretPosition = contentArea.getCaretPosition(); int caretPosition = codeArea.getCaretPosition();
Element root = contentArea.getDocument().getDefaultRootElement(); Element root = codeArea.getDocument().getDefaultRootElement();
int currentLine = root.getElementIndex(caretPosition); int currentLine = root.getElementIndex(caretPosition);
if (lastLine != currentLine) { if (lastLine != currentLine) {
repaint(); repaint();
......
package jadx.gui.ui; package jadx.gui.ui;
import jadx.api.ResourceFile;
import jadx.gui.JadxWrapper; import jadx.gui.JadxWrapper;
import jadx.gui.jobs.BackgroundWorker; import jadx.gui.jobs.BackgroundWorker;
import jadx.gui.jobs.DecompileJob; import jadx.gui.jobs.DecompileJob;
...@@ -139,7 +140,7 @@ public class MainWindow extends JFrame { ...@@ -139,7 +140,7 @@ public class MainWindow extends JFrame {
setLocationAndPosition(); setLocationAndPosition();
setVisible(true); setVisible(true);
setLocationRelativeTo(null); setLocationRelativeTo(null);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
if (settings.getInput().isEmpty()) { if (settings.getInput().isEmpty()) {
openFile(); openFile();
...@@ -169,7 +170,7 @@ public class MainWindow extends JFrame { ...@@ -169,7 +170,7 @@ public class MainWindow extends JFrame {
public void openFile() { public void openFile() {
JFileChooser fileChooser = new JFileChooser(); JFileChooser fileChooser = new JFileChooser();
fileChooser.setAcceptAllFileFilterUsed(true); fileChooser.setAcceptAllFileFilterUsed(true);
String[] exts = {"apk", "dex", "jar", "class", "zip"}; String[] exts = {"apk", "dex", "jar", "class", "zip", "aar"};
String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')'); String description = "supported files: " + Arrays.toString(exts).replace('[', '(').replace(']', ')');
fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts)); fileChooser.setFileFilter(new FileNameExtensionFilter(description, exts));
fileChooser.setToolTipText(NLS.str("file.open")); fileChooser.setToolTipText(NLS.str("file.open"));
...@@ -245,7 +246,7 @@ public class MainWindow extends JFrame { ...@@ -245,7 +246,7 @@ public class MainWindow extends JFrame {
if (ret == JFileChooser.APPROVE_OPTION) { if (ret == JFileChooser.APPROVE_OPTION) {
settings.setLastSaveFilePath(fileChooser.getCurrentDirectory().getPath()); settings.setLastSaveFilePath(fileChooser.getCurrentDirectory().getPath());
ProgressMonitor progressMonitor = new ProgressMonitor(mainPanel, NLS.str("msg.saving_sources"), "", 0, 100); ProgressMonitor progressMonitor = new ProgressMonitor(mainPanel, NLS.str("msg.saving_sources"), "", 0, 100);
progressMonitor.setMillisToPopup(500); progressMonitor.setMillisToPopup(0);
wrapper.saveAll(fileChooser.getSelectedFile(), progressMonitor); wrapper.saveAll(fileChooser.getSelectedFile(), progressMonitor);
} }
} }
...@@ -296,11 +297,11 @@ public class MainWindow extends JFrame { ...@@ -296,11 +297,11 @@ public class MainWindow extends JFrame {
Object obj = tree.getLastSelectedPathComponent(); Object obj = tree.getLastSelectedPathComponent();
if (obj instanceof JResource) { if (obj instanceof JResource) {
JResource res = (JResource) obj; JResource res = (JResource) obj;
if (res.getContent() != null) { ResourceFile resFile = res.getResFile();
tabbedPane.codeJump(new Position(res, res.getLine())); if (resFile != null && JResource.isSupportedForView(resFile.getType())) {
tabbedPane.showResource(res);
} }
} } else if (obj instanceof JNode) {
if (obj instanceof JNode) {
JNode node = (JNode) obj; JNode node = (JNode) obj;
JClass cls = node.getRootClass(); JClass cls = node.getRootClass();
if (cls != null) { if (cls != null) {
......
package jadx.gui.ui; package jadx.gui.ui;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.gui.treemodel.JNode; import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.utils.JumpManager; import jadx.gui.utils.JumpManager;
import jadx.gui.utils.NLS; import jadx.gui.utils.NLS;
import jadx.gui.utils.Position; import jadx.gui.utils.Position;
...@@ -72,23 +75,39 @@ class TabbedPane extends JTabbedPane { ...@@ -72,23 +75,39 @@ class TabbedPane extends JTabbedPane {
} }
private void showCode(final Position pos) { private void showCode(final Position pos) {
final ContentPanel contentPanel = getCodePanel(pos.getNode()); final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode());
if (contentPanel == null) {
return;
}
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
setSelectedComponent(contentPanel); setSelectedComponent(contentPanel);
ContentArea contentArea = contentPanel.getContentArea(); CodeArea codeArea = contentPanel.getCodeArea();
int line = pos.getLine(); int line = pos.getLine();
if (line < 0) { if (line < 0) {
try { try {
line = 1 + contentArea.getLineOfOffset(-line); line = 1 + codeArea.getLineOfOffset(-line);
} catch (BadLocationException e) { } catch (BadLocationException e) {
LOG.error("Can't get line for: {}", pos, e); LOG.error("Can't get line for: {}", pos, e);
line = pos.getNode().getLine(); line = pos.getNode().getLine();
} }
} }
contentArea.scrollToLine(line); codeArea.scrollToLine(line);
contentArea.requestFocus(); codeArea.requestFocus();
}
});
}
public void showResource(JResource res) {
final ContentPanel contentPanel = getContentPanel(res);
if (contentPanel == null) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setSelectedComponent(contentPanel);
} }
}); });
} }
...@@ -105,10 +124,10 @@ class TabbedPane extends JTabbedPane { ...@@ -105,10 +124,10 @@ class TabbedPane extends JTabbedPane {
@Nullable @Nullable
private Position getCurrentPosition() { private Position getCurrentPosition() {
ContentPanel selectedCodePanel = getSelectedCodePanel(); ContentPanel selectedCodePanel = getSelectedCodePanel();
if (selectedCodePanel == null) { if (selectedCodePanel instanceof CodePanel) {
return null; return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition();
} }
return selectedCodePanel.getContentArea().getCurrentPosition(); return null;
} }
public void navBack() { public void navBack() {
...@@ -125,11 +144,7 @@ class TabbedPane extends JTabbedPane { ...@@ -125,11 +144,7 @@ class TabbedPane extends JTabbedPane {
} }
} }
public JumpManager getJumpManager() { private void addContentPanel(ContentPanel contentPanel) {
return jumps;
}
private void addCodePanel(ContentPanel contentPanel) {
openTabs.put(contentPanel.getNode(), contentPanel); openTabs.put(contentPanel.getNode(), contentPanel);
add(contentPanel); add(contentPanel);
} }
...@@ -139,17 +154,37 @@ class TabbedPane extends JTabbedPane { ...@@ -139,17 +154,37 @@ class TabbedPane extends JTabbedPane {
remove(contentPanel); remove(contentPanel);
} }
private ContentPanel getCodePanel(JNode cls) { @Nullable
ContentPanel panel = openTabs.get(cls); private ContentPanel getContentPanel(JNode node) {
ContentPanel panel = openTabs.get(node);
if (panel == null) { if (panel == null) {
panel = new ContentPanel(this, cls); panel = makeContentPanel(node);
addCodePanel(panel); if (panel == null) {
return null;
}
addContentPanel(panel);
setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel)); setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));
} }
return panel; return panel;
} }
@Nullable @Nullable
private ContentPanel makeContentPanel(JNode node) {
if (node instanceof JResource) {
JResource res = (JResource) node;
ResourceFile resFile = res.getResFile();
if (resFile != null) {
if (resFile.getType() == ResourceType.IMG) {
return new ImagePanel(this, res);
}
} else {
return null;
}
}
return new CodePanel(this, node);
}
@Nullable
ContentPanel getSelectedCodePanel() { ContentPanel getSelectedCodePanel() {
return (ContentPanel) getSelectedComponent(); return (ContentPanel) getSelectedComponent();
} }
...@@ -271,7 +306,7 @@ class TabbedPane extends JTabbedPane { ...@@ -271,7 +306,7 @@ class TabbedPane extends JTabbedPane {
public void loadSettings() { public void loadSettings() {
for (ContentPanel panel : openTabs.values()) { for (ContentPanel panel : openTabs.values()) {
panel.getContentArea().loadSettings(); panel.loadSettings();
} }
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment