Projects STRLCPY jadx Commits 4a072731
🤬
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/JadxWrapper.java
    skipped 27 lines
    28 28  import jadx.core.dex.visitors.rename.RenameVisitor;
    29 29  import jadx.core.utils.exceptions.JadxRuntimeException;
    30 30  import jadx.core.utils.files.FileUtils;
     31 +import jadx.gui.plugins.context.PluginsContext;
    31 32  import jadx.gui.settings.JadxProject;
    32 33  import jadx.gui.settings.JadxSettings;
    33 34  import jadx.gui.ui.MainWindow;
    skipped 13 lines
    47 48   
    48 49   private final MainWindow mainWindow;
    49 50   private volatile @Nullable JadxDecompiler decompiler;
     51 + private PluginsContext pluginsContext;
    50 52   
    51 53   public JadxWrapper(MainWindow mainWindow) {
    52 54   this.mainWindow = mainWindow;
    skipped 9 lines
    62 64   jadxArgs.setCodeData(project.getCodeData());
    63 65   
    64 66   this.decompiler = new JadxDecompiler(jadxArgs);
     67 + this.pluginsContext = new PluginsContext(mainWindow);
     68 + this.decompiler.setJadxGuiContext(pluginsContext);
    65 69   this.decompiler.load();
    66 70   initCodeCache();
    67 71   }
    skipped 18 lines
    86 90   if (decompiler != null) {
    87 91   decompiler.close();
    88 92   decompiler = null;
     93 + }
     94 + if (pluginsContext != null) {
     95 + pluginsContext.reset();
     96 + pluginsContext = null;
    89 97   }
    90 98   }
    91 99   } catch (Exception e) {
    skipped 194 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/context/PluginsContext.java
     1 +package jadx.gui.plugins.context;
     2 + 
     3 +import javax.swing.JMenu;
     4 + 
     5 +import org.slf4j.Logger;
     6 +import org.slf4j.LoggerFactory;
     7 + 
     8 +import jadx.api.plugins.gui.JadxGuiContext;
     9 +import jadx.gui.ui.MainWindow;
     10 +import jadx.gui.utils.UiUtils;
     11 +import jadx.gui.utils.ui.ActionHandler;
     12 + 
     13 +public class PluginsContext implements JadxGuiContext {
     14 + private static final Logger LOG = LoggerFactory.getLogger(PluginsContext.class);
     15 + 
     16 + private final MainWindow mainWindow;
     17 + 
     18 + public PluginsContext(MainWindow mainWindow) {
     19 + this.mainWindow = mainWindow;
     20 + }
     21 + 
     22 + public void reset() {
     23 + JMenu pluginsMenu = mainWindow.getPluginsMenu();
     24 + pluginsMenu.removeAll();
     25 + pluginsMenu.setVisible(false);
     26 + }
     27 + 
     28 + @Override
     29 + public void uiRun(Runnable runnable) {
     30 + UiUtils.uiRun(runnable);
     31 + }
     32 + 
     33 + @Override
     34 + public void addMenuAction(String name, Runnable action) {
     35 + ActionHandler item = new ActionHandler(ev -> {
     36 + try {
     37 + mainWindow.getBackgroundExecutor().execute(name, action);
     38 + } catch (Exception e) {
     39 + LOG.error("Error running action for menu item: {}", name, e);
     40 + }
     41 + });
     42 + item.setNameAndDesc(name);
     43 + JMenu pluginsMenu = mainWindow.getPluginsMenu();
     44 + pluginsMenu.add(item);
     45 + pluginsMenu.setVisible(true);
     46 + }
     47 +}
     48 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/settings/TabStateViewAdapter.java
    skipped 7 lines
    8 8  import jadx.gui.settings.data.TabViewState;
    9 9  import jadx.gui.settings.data.ViewPoint;
    10 10  import jadx.gui.treemodel.JClass;
     11 +import jadx.gui.treemodel.JInputScript;
    11 12  import jadx.gui.treemodel.JNode;
    12 13  import jadx.gui.treemodel.JResource;
    13 14  import jadx.gui.ui.MainWindow;
    skipped 38 lines
    52 53   return mw.getCacheObject().getNodeCache().makeFrom(javaClass);
    53 54   }
    54 55   break;
     56 + 
    55 57   case "resource":
    56 58   JResource tmpNode = new JResource(null, tvs.getTabPath(), JResource.JResType.FILE);
    57 59   return mw.getTreeRoot().searchNode(tmpNode); // equals method in JResource check only name
     60 + 
     61 + case "script":
     62 + return mw.getTreeRoot()
     63 + .followStaticPath("JInputs", "JInputScripts")
     64 + .searchNode(node -> node instanceof JInputScript && node.getName().equals(tvs.getTabPath()));
    58 65   }
    59 66   return null;
    60 67   }
    skipped 6 lines
    67 74   }
    68 75   if (node instanceof JResource) {
    69 76   tvs.setType("resource");
     77 + tvs.setTabPath(node.getName());
     78 + return true;
     79 + }
     80 + if (node instanceof JInputScript) {
     81 + tvs.setType("script");
    70 82   tvs.setTabPath(node.getName());
    71 83   return true;
    72 84   }
    skipped 4 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JClass.java
    skipped 1 lines
    2 2   
    3 3  import javax.swing.Icon;
    4 4  import javax.swing.ImageIcon;
     5 +import javax.swing.JPopupMenu;
    5 6   
    6 7  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
    7 8  import org.jetbrains.annotations.NotNull;
    skipped 5 lines
    13 14  import jadx.api.JavaNode;
    14 15  import jadx.core.dex.attributes.AFlag;
    15 16  import jadx.core.dex.info.AccessInfo;
     17 +import jadx.gui.ui.MainWindow;
    16 18  import jadx.gui.ui.TabbedPane;
    17 19  import jadx.gui.ui.codearea.ClassCodeContentPanel;
     20 +import jadx.gui.ui.dialog.RenameDialog;
    18 21  import jadx.gui.ui.panel.ContentPanel;
    19 22  import jadx.gui.utils.CacheObject;
    20 23  import jadx.gui.utils.NLS;
    skipped 100 lines
    121 124   @Override
    122 125   public String getSyntaxName() {
    123 126   return SyntaxConstants.SYNTAX_STYLE_JAVA;
     127 + }
     128 + 
     129 + @Override
     130 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     131 + return RenameDialog.buildRenamePopup(mainWindow, this);
    124 132   }
    125 133   
    126 134   @Override
    skipped 93 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JEditableNode.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.util.ArrayList;
     4 +import java.util.List;
     5 +import java.util.function.Consumer;
     6 + 
     7 +public abstract class JEditableNode extends JNode {
     8 + 
     9 + private volatile boolean changed = false;
     10 + private final List<Consumer<Boolean>> changeListeners = new ArrayList<>();
     11 + 
     12 + public abstract void save(String newContent);
     13 + 
     14 + @Override
     15 + public boolean isEditable() {
     16 + return true;
     17 + }
     18 + 
     19 + public boolean isChanged() {
     20 + return changed;
     21 + }
     22 + 
     23 + public void setChanged(boolean changed) {
     24 + if (this.changed != changed) {
     25 + this.changed = changed;
     26 + for (Consumer<Boolean> changeListener : changeListeners) {
     27 + changeListener.accept(changed);
     28 + }
     29 + }
     30 + }
     31 + 
     32 + public void addChangeListener(Consumer<Boolean> listener) {
     33 + changeListeners.add(listener);
     34 + }
     35 +}
     36 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JField.java
    skipped 3 lines
    4 4   
    5 5  import javax.swing.Icon;
    6 6  import javax.swing.ImageIcon;
     7 +import javax.swing.JPopupMenu;
    7 8   
    8 9  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
    9 10  import org.jetbrains.annotations.NotNull;
    skipped 2 lines
    12 13  import jadx.api.JavaNode;
    13 14  import jadx.core.dex.attributes.AFlag;
    14 15  import jadx.core.dex.info.AccessInfo;
     16 +import jadx.gui.ui.MainWindow;
     17 +import jadx.gui.ui.dialog.RenameDialog;
    15 18  import jadx.gui.utils.OverlayIcon;
    16 19  import jadx.gui.utils.UiUtils;
    17 20   
    skipped 34 lines
    52 55   @Override
    53 56   public boolean canRename() {
    54 57   return !field.getFieldNode().contains(AFlag.DONT_RENAME);
     58 + }
     59 + 
     60 + @Override
     61 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     62 + return RenameDialog.buildRenamePopup(mainWindow, this);
    55 63   }
    56 64   
    57 65   @Override
    skipped 80 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JInputFile.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.nio.file.Path;
     4 + 
     5 +import javax.swing.Icon;
     6 +import javax.swing.JPopupMenu;
     7 + 
     8 +import jadx.gui.ui.MainWindow;
     9 +import jadx.gui.utils.Icons;
     10 +import jadx.gui.utils.NLS;
     11 +import jadx.gui.utils.ui.SimpleMenuItem;
     12 + 
     13 +public class JInputFile extends JNode {
     14 + 
     15 + private final Path filePath;
     16 + 
     17 + public JInputFile(Path filePath) {
     18 + this.filePath = filePath;
     19 + }
     20 + 
     21 + @Override
     22 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     23 + JPopupMenu menu = new JPopupMenu();
     24 + menu.add(new SimpleMenuItem(NLS.str("popup.add_files"), mainWindow::addFiles));
     25 + menu.add(new SimpleMenuItem(NLS.str("popup.remove"), () -> mainWindow.removeInput(filePath)));
     26 + return menu;
     27 + }
     28 + 
     29 + @Override
     30 + public JClass getJParent() {
     31 + return null;
     32 + }
     33 + 
     34 + @Override
     35 + public Icon getIcon() {
     36 + return Icons.FILE;
     37 + }
     38 + 
     39 + @Override
     40 + public String makeString() {
     41 + return filePath.getFileName().toString();
     42 + }
     43 +}
     44 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JInputFiles.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.nio.file.Path;
     4 +import java.util.List;
     5 + 
     6 +import javax.swing.Icon;
     7 +import javax.swing.ImageIcon;
     8 +import javax.swing.JPopupMenu;
     9 + 
     10 +import jadx.gui.ui.MainWindow;
     11 +import jadx.gui.utils.NLS;
     12 +import jadx.gui.utils.UiUtils;
     13 +import jadx.gui.utils.ui.SimpleMenuItem;
     14 + 
     15 +public class JInputFiles extends JNode {
     16 + private static final ImageIcon INPUT_FILES_ICON = UiUtils.openSvgIcon("nodes/moduleDirectory");
     17 + 
     18 + public JInputFiles(List<Path> files) {
     19 + for (Path file : files) {
     20 + add(new JInputFile(file));
     21 + }
     22 + }
     23 + 
     24 + @Override
     25 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     26 + JPopupMenu menu = new JPopupMenu();
     27 + menu.add(new SimpleMenuItem(NLS.str("popup.add_files"), mainWindow::addFiles));
     28 + return menu;
     29 + }
     30 + 
     31 + @Override
     32 + public JClass getJParent() {
     33 + return null;
     34 + }
     35 + 
     36 + @Override
     37 + public Icon getIcon() {
     38 + return INPUT_FILES_ICON;
     39 + }
     40 + 
     41 + @Override
     42 + public String makeString() {
     43 + return NLS.str("tree.input_files");
     44 + }
     45 +}
     46 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JInputScript.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.nio.file.Path;
     4 + 
     5 +import javax.swing.Icon;
     6 +import javax.swing.ImageIcon;
     7 +import javax.swing.JPopupMenu;
     8 + 
     9 +import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
     10 +import org.jetbrains.annotations.NotNull;
     11 +import org.slf4j.Logger;
     12 +import org.slf4j.LoggerFactory;
     13 + 
     14 +import jadx.api.ICodeInfo;
     15 +import jadx.api.impl.SimpleCodeInfo;
     16 +import jadx.core.utils.exceptions.JadxRuntimeException;
     17 +import jadx.core.utils.files.FileUtils;
     18 +import jadx.gui.ui.MainWindow;
     19 +import jadx.gui.ui.TabbedPane;
     20 +import jadx.gui.ui.codearea.CodeContentPanel;
     21 +import jadx.gui.ui.panel.ContentPanel;
     22 +import jadx.gui.utils.NLS;
     23 +import jadx.gui.utils.UiUtils;
     24 +import jadx.gui.utils.ui.SimpleMenuItem;
     25 + 
     26 +public class JInputScript extends JEditableNode {
     27 + private static final Logger LOG = LoggerFactory.getLogger(JInputScript.class);
     28 + 
     29 + private static final ImageIcon SCRIPT_ICON = UiUtils.openSvgIcon("nodes/kotlin_script");
     30 + 
     31 + private final Path scriptPath;
     32 + private final String name;
     33 + 
     34 + public JInputScript(Path scriptPath) {
     35 + this.scriptPath = scriptPath;
     36 + this.name = scriptPath.getFileName().toString().replace(".jadx.kts", "");
     37 + }
     38 + 
     39 + @Override
     40 + public ContentPanel getContentPanel(TabbedPane tabbedPane) {
     41 + return new CodeContentPanel(tabbedPane, this);
     42 + }
     43 + 
     44 + @Override
     45 + public @NotNull ICodeInfo getCodeInfo() {
     46 + try {
     47 + return new SimpleCodeInfo(FileUtils.readFile(scriptPath));
     48 + } catch (Exception e) {
     49 + throw new JadxRuntimeException("Failed to read script file: " + scriptPath.toAbsolutePath(), e);
     50 + }
     51 + }
     52 + 
     53 + @Override
     54 + public void save(String newContent) {
     55 + try {
     56 + FileUtils.writeFile(scriptPath, newContent);
     57 + LOG.debug("Script saved: {}", scriptPath.toAbsolutePath());
     58 + } catch (Exception e) {
     59 + throw new JadxRuntimeException("Failed to write script file: " + scriptPath.toAbsolutePath(), e);
     60 + }
     61 + }
     62 + 
     63 + @Override
     64 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     65 + JPopupMenu menu = new JPopupMenu();
     66 + menu.add(new SimpleMenuItem(NLS.str("popup.add_scripts"), mainWindow::addFiles));
     67 + menu.add(new SimpleMenuItem(NLS.str("popup.new_script"), mainWindow::addNewScript));
     68 + menu.add(new SimpleMenuItem(NLS.str("popup.remove"), () -> mainWindow.removeInput(scriptPath)));
     69 + return menu;
     70 + }
     71 + 
     72 + @Override
     73 + public boolean isEditable() {
     74 + return true;
     75 + }
     76 + 
     77 + @Override
     78 + public String getSyntaxName() {
     79 + return SyntaxConstants.SYNTAX_STYLE_KOTLIN;
     80 + }
     81 + 
     82 + @Override
     83 + public JClass getJParent() {
     84 + return null;
     85 + }
     86 + 
     87 + @Override
     88 + public Icon getIcon() {
     89 + return SCRIPT_ICON;
     90 + }
     91 + 
     92 + @Override
     93 + public String getName() {
     94 + return name;
     95 + }
     96 + 
     97 + @Override
     98 + public String makeString() {
     99 + return name;
     100 + }
     101 +}
     102 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JInputScripts.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.nio.file.Path;
     4 +import java.util.List;
     5 + 
     6 +import javax.swing.Icon;
     7 +import javax.swing.ImageIcon;
     8 +import javax.swing.JPopupMenu;
     9 + 
     10 +import jadx.gui.ui.MainWindow;
     11 +import jadx.gui.utils.NLS;
     12 +import jadx.gui.utils.UiUtils;
     13 +import jadx.gui.utils.ui.SimpleMenuItem;
     14 + 
     15 +public class JInputScripts extends JNode {
     16 + private static final ImageIcon INPUT_SCRIPTS_ICON = UiUtils.openSvgIcon("nodes/scriptsModel");
     17 + 
     18 + public JInputScripts(List<Path> scripts) {
     19 + for (Path script : scripts) {
     20 + add(new JInputScript(script));
     21 + }
     22 + }
     23 + 
     24 + @Override
     25 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     26 + JPopupMenu menu = new JPopupMenu();
     27 + menu.add(new SimpleMenuItem(NLS.str("popup.add_scripts"), mainWindow::addFiles));
     28 + menu.add(new SimpleMenuItem(NLS.str("popup.new_script"), mainWindow::addNewScript));
     29 + return menu;
     30 + }
     31 + 
     32 + @Override
     33 + public JClass getJParent() {
     34 + return null;
     35 + }
     36 + 
     37 + @Override
     38 + public Icon getIcon() {
     39 + return INPUT_SCRIPTS_ICON;
     40 + }
     41 + 
     42 + @Override
     43 + public String makeString() {
     44 + return NLS.str("tree.input_scripts");
     45 + }
     46 +}
     47 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JInputs.java
     1 +package jadx.gui.treemodel;
     2 + 
     3 +import java.nio.file.Path;
     4 +import java.util.ArrayList;
     5 +import java.util.Iterator;
     6 +import java.util.List;
     7 + 
     8 +import javax.swing.Icon;
     9 +import javax.swing.ImageIcon;
     10 + 
     11 +import jadx.core.utils.files.FileUtils;
     12 +import jadx.gui.JadxWrapper;
     13 +import jadx.gui.utils.NLS;
     14 +import jadx.gui.utils.UiUtils;
     15 + 
     16 +public class JInputs extends JNode {
     17 + private static final ImageIcon INPUTS_ICON = UiUtils.openSvgIcon("nodes/projectStructure");
     18 + 
     19 + public JInputs(JadxWrapper wrapper) {
     20 + List<Path> inputs = wrapper.getProject().getFilePaths();
     21 + List<Path> files = FileUtils.expandDirs(inputs);
     22 + List<Path> scripts = new ArrayList<>();
     23 + Iterator<Path> it = files.iterator();
     24 + while (it.hasNext()) {
     25 + Path file = it.next();
     26 + if (file.getFileName().toString().endsWith(".jadx.kts")) {
     27 + scripts.add(file);
     28 + it.remove();
     29 + }
     30 + }
     31 + 
     32 + add(new JInputFiles(files));
     33 + add(new JInputScripts(scripts));
     34 + }
     35 + 
     36 + @Override
     37 + public JClass getJParent() {
     38 + return null;
     39 + }
     40 + 
     41 + @Override
     42 + public Icon getIcon() {
     43 + return INPUTS_ICON;
     44 + }
     45 + 
     46 + @Override
     47 + public String makeString() {
     48 + return NLS.str("tree.inputs_title");
     49 + }
     50 +}
     51 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JMethod.java
    skipped 4 lines
    5 5   
    6 6  import javax.swing.Icon;
    7 7  import javax.swing.ImageIcon;
     8 +import javax.swing.JPopupMenu;
    8 9   
    9 10  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
    10 11  import org.jetbrains.annotations.NotNull;
    skipped 3 lines
    14 15  import jadx.core.dex.attributes.AFlag;
    15 16  import jadx.core.dex.info.AccessInfo;
    16 17  import jadx.core.dex.instructions.args.ArgType;
     18 +import jadx.gui.ui.MainWindow;
     19 +import jadx.gui.ui.dialog.RenameDialog;
    17 20  import jadx.gui.utils.Icons;
    18 21  import jadx.gui.utils.OverlayIcon;
    19 22  import jadx.gui.utils.UiUtils;
    skipped 85 lines
    105 108   return false;
    106 109   }
    107 110   return !mth.getMethodNode().contains(AFlag.DONT_RENAME);
     111 + }
     112 + 
     113 + @Override
     114 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     115 + return RenameDialog.buildRenamePopup(mainWindow, this);
    108 116   }
    109 117   
    110 118   String makeBaseString() {
    skipped 103 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JNode.java
    1 1  package jadx.gui.treemodel;
    2 2   
    3 3  import java.util.Comparator;
     4 +import java.util.Enumeration;
     5 +import java.util.function.Predicate;
    4 6   
    5 7  import javax.swing.Icon;
     8 +import javax.swing.JPopupMenu;
    6 9  import javax.swing.tree.DefaultMutableTreeNode;
    7 10   
    8 11  import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
    skipped 2 lines
    11 14   
    12 15  import jadx.api.ICodeInfo;
    13 16  import jadx.api.JavaNode;
     17 +import jadx.gui.ui.MainWindow;
    14 18  import jadx.gui.ui.TabbedPane;
    15 19  import jadx.gui.ui.panel.ContentPanel;
    16 20   
    skipped 28 lines
    45 49   return ICodeInfo.EMPTY;
    46 50   }
    47 51   
     52 + public boolean isEditable() {
     53 + return false;
     54 + }
     55 + 
    48 56   public abstract Icon getIcon();
    49 57   
    50 58   public String getName() {
    skipped 6 lines
    57 65   
    58 66   public boolean canRename() {
    59 67   return false;
     68 + }
     69 + 
     70 + public @Nullable JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     71 + return null;
    60 72   }
    61 73   
    62 74   public abstract String makeString();
    skipped 32 lines
    95 107   
    96 108   public String getTooltip() {
    97 109   return makeLongStringHtml();
     110 + }
     111 + 
     112 + public @Nullable JNode searchNode(Predicate<JNode> filter) {
     113 + Enumeration<?> en = this.breadthFirstEnumeration();
     114 + while (en.hasMoreElements()) {
     115 + JNode node = (JNode) en.nextElement();
     116 + if (filter.test(node)) {
     117 + return node;
     118 + }
     119 + }
     120 + return null;
    98 121   }
    99 122   
    100 123   private static final Comparator<JNode> COMPARATOR = Comparator
    skipped 14 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JPackage.java
    skipped 5 lines
    6 6   
    7 7  import javax.swing.Icon;
    8 8  import javax.swing.ImageIcon;
     9 +import javax.swing.JPopupMenu;
    9 10   
    10 11  import jadx.api.JavaPackage;
    11 12  import jadx.core.utils.Utils;
    12 13  import jadx.gui.JadxWrapper;
     14 +import jadx.gui.ui.MainWindow;
     15 +import jadx.gui.ui.popupmenu.JPackagePopupMenu;
    13 16  import jadx.gui.utils.UiUtils;
    14 17   
    15 18  public class JPackage extends JNode {
    skipped 50 lines
    66 69   add(cls);
    67 70   }
    68 71   }
     72 + }
     73 + 
     74 + @Override
     75 + public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
     76 + return new JPackagePopupMenu(mainWindow, this);
    69 77   }
    70 78   
    71 79   @Override
    skipped 80 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JResource.java
    skipped 23 lines
    24 24  import jadx.gui.ui.codearea.CodeContentPanel;
    25 25  import jadx.gui.ui.panel.ContentPanel;
    26 26  import jadx.gui.ui.panel.ImagePanel;
     27 +import jadx.gui.utils.Icons;
    27 28  import jadx.gui.utils.NLS;
    28 29  import jadx.gui.utils.UiUtils;
    29 30  import jadx.gui.utils.res.ResTableHelper;
    skipped 2 lines
    32 33   private static final long serialVersionUID = -201018424302612434L;
    33 34   
    34 35   private static final ImageIcon ROOT_ICON = UiUtils.openSvgIcon("nodes/resourcesRoot");
    35  - private static final ImageIcon FOLDER_ICON = UiUtils.openSvgIcon("nodes/folder");
    36  - private static final ImageIcon FILE_ICON = UiUtils.openSvgIcon("nodes/file_any_type");
    37 36   private static final ImageIcon ARSC_ICON = UiUtils.openSvgIcon("nodes/resourceBundle");
    38 37   private static final ImageIcon XML_ICON = UiUtils.openSvgIcon("nodes/xml");
    39 38   private static final ImageIcon IMAGE_ICON = UiUtils.openSvgIcon("nodes/ImagesFileType");
    skipped 204 lines
    244 243   case ROOT:
    245 244   return ROOT_ICON;
    246 245   case DIR:
    247  - return FOLDER_ICON;
     246 + return Icons.FOLDER;
    248 247   
    249 248   case FILE:
    250 249   ResourceType resType = resFile.getType();
    skipped 15 lines
    266 265   }
    267 266   return UNKNOWN_ICON;
    268 267   }
    269  - return FILE_ICON;
     268 + return Icons.FILE;
    270 269   }
    271 270   
    272 271   public static boolean isSupportedForView(ResourceType type) {
    skipped 53 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/treemodel/JRoot.java
    skipped 2 lines
    3 3  import java.io.File;
    4 4  import java.nio.file.Path;
    5 5  import java.util.ArrayList;
     6 +import java.util.Arrays;
    6 7  import java.util.Enumeration;
    7 8  import java.util.List;
    8 9  import java.util.regex.Pattern;
    9 10   
    10 11  import javax.swing.Icon;
    11 12  import javax.swing.ImageIcon;
     13 +import javax.swing.tree.TreeNode;
    12 14   
    13 15  import org.jetbrains.annotations.Nullable;
    14 16   
    15 17  import jadx.api.ResourceFile;
     18 +import jadx.core.utils.exceptions.JadxRuntimeException;
    16 19  import jadx.gui.JadxWrapper;
     20 +import jadx.gui.settings.JadxProject;
    17 21  import jadx.gui.treemodel.JResource.JResType;
    18 22  import jadx.gui.utils.NLS;
    19 23  import jadx.gui.utils.UiUtils;
    skipped 15 lines
    35 39   
    36 40   public final void update() {
    37 41   removeAllChildren();
     42 + add(new JInputs(wrapper));
    38 43   add(new JSources(this, wrapper));
    39 44   
    40 45   List<ResourceFile> resources = wrapper.getResources();
    skipped 46 lines
    87 92   return null;
    88 93   }
    89 94   
    90  - public JNode searchNode(JNode node) {
     95 + public @Nullable JNode searchNode(JNode node) {
    91 96   Enumeration<?> en = this.breadthFirstEnumeration();
    92 97   while (en.hasMoreElements()) {
    93 98   Object obj = en.nextElement();
    skipped 4 lines
    98 103   return null;
    99 104   }
    100 105   
     106 + public JNode followStaticPath(String... path) {
     107 + List<String> list = Arrays.asList(path);
     108 + JNode node = getNodeByClsPath(this, 0, list);
     109 + if (node == null) {
     110 + throw new JadxRuntimeException("Incorrect static path in tree: " + list);
     111 + }
     112 + return node;
     113 + }
     114 + 
     115 + private static @Nullable JNode getNodeByClsPath(JNode start, int pos, List<String> path) {
     116 + if (pos >= path.size()) {
     117 + return start;
     118 + }
     119 + String clsName = path.get(pos);
     120 + Enumeration<TreeNode> en = start.children();
     121 + while (en.hasMoreElements()) {
     122 + JNode node = (JNode) en.nextElement();
     123 + if (node.getClass().getSimpleName().equals(clsName)) {
     124 + return getNodeByClsPath(node, pos + 1, path);
     125 + }
     126 + }
     127 + return null;
     128 + }
     129 + 
    101 130   public boolean isFlatPackages() {
    102 131   return flatPackages;
    103 132   }
    skipped 30 lines
    134 163   
    135 164   @Override
    136 165   public String makeString() {
    137  - List<Path> paths = wrapper.getProject().getFilePaths();
     166 + JadxProject project = wrapper.getProject();
     167 + if (project.getProjectPath() != null) {
     168 + return project.getName();
     169 + }
     170 + List<Path> paths = project.getFilePaths();
    138 171   int count = paths.size();
    139 172   if (count == 0) {
    140 173   return "File not open";
    skipped 25 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
    skipped 86 lines
    87 87  import jadx.api.ResourceFile;
    88 88  import jadx.api.plugins.utils.CommonFileUtils;
    89 89  import jadx.core.Jadx;
     90 +import jadx.core.export.TemplateFile;
    90 91  import jadx.core.utils.ListUtils;
    91 92  import jadx.core.utils.StringUtils;
    92 93  import jadx.core.utils.files.FileUtils;
    skipped 11 lines
    104 105  import jadx.gui.settings.JadxSettingsWindow;
    105 106  import jadx.gui.treemodel.ApkSignature;
    106 107  import jadx.gui.treemodel.JClass;
    107  -import jadx.gui.treemodel.JField;
    108 108  import jadx.gui.treemodel.JLoadableNode;
    109  -import jadx.gui.treemodel.JMethod;
    110 109  import jadx.gui.treemodel.JNode;
    111 110  import jadx.gui.treemodel.JPackage;
    112 111  import jadx.gui.treemodel.JResource;
    skipped 5 lines
    118 117  import jadx.gui.ui.dialog.ADBDialog;
    119 118  import jadx.gui.ui.dialog.AboutDialog;
    120 119  import jadx.gui.ui.dialog.LogViewerDialog;
    121  -import jadx.gui.ui.dialog.RenameDialog;
    122 120  import jadx.gui.ui.dialog.SearchDialog;
    123 121  import jadx.gui.ui.filedialog.FileDialogWrapper;
    124 122  import jadx.gui.ui.filedialog.FileOpenMode;
    skipped 1 lines
    126 124  import jadx.gui.ui.panel.IssuesPanel;
    127 125  import jadx.gui.ui.panel.JDebuggerPanel;
    128 126  import jadx.gui.ui.panel.ProgressPanel;
    129  -import jadx.gui.ui.popupmenu.JPackagePopupMenu;
    130 127  import jadx.gui.ui.treenodes.StartPageNode;
    131 128  import jadx.gui.ui.treenodes.SummaryNode;
    132 129  import jadx.gui.update.JadxUpdate;
    skipped 82 lines
    215 212   private JDebuggerPanel debuggerPanel;
    216 213   private JSplitPane verticalSplitter;
    217 214   
    218  - private List<ILoadListener> loadListeners = new ArrayList<>();
     215 + private final List<ILoadListener> loadListeners = new ArrayList<>();
    219 216   private boolean loaded;
     217 + 
     218 + private JMenu pluginsMenu;
    220 219   
    221 220   public MainWindow(JadxSettings settings) {
    222 221   this.settings = settings;
    skipped 180 lines
    403 402   s -> update());
    404 403   }
    405 404   
     405 + public void addNewScript() {
     406 + FileDialogWrapper fileDialog = new FileDialogWrapper(this, FileOpenMode.CUSTOM_SAVE);
     407 + fileDialog.setTitle(NLS.str("file.save"));
     408 + Path workingDir = project.getWorkingDir();
     409 + Path baseDir = workingDir != null ? workingDir : settings.getLastSaveFilePath();
     410 + fileDialog.setSelectedFile(baseDir.resolve("script.jadx.kts"));
     411 + fileDialog.setFileExtList(Collections.singletonList("jadx.kts"));
     412 + fileDialog.setSelectionMode(JFileChooser.FILES_ONLY);
     413 + List<Path> paths = fileDialog.show();
     414 + if (paths.size() != 1) {
     415 + return;
     416 + }
     417 + Path scriptFile = paths.get(0);
     418 + try {
     419 + TemplateFile tmpl = TemplateFile.fromResources("/files/script.jadx.kts.tmpl");
     420 + FileUtils.writeFile(scriptFile, tmpl.build());
     421 + } catch (Exception e) {
     422 + LOG.error("Failed to save new script file: {}", scriptFile, e);
     423 + }
     424 + List<Path> inputs = project.getFilePaths();
     425 + inputs.add(scriptFile);
     426 + project.setFilePaths(inputs);
     427 + project.save();
     428 + reopen();
     429 + }
     430 + 
     431 + public void removeInput(Path file) {
     432 + List<Path> inputs = project.getFilePaths();
     433 + inputs.remove(file);
     434 + project.setFilePaths(inputs);
     435 + project.save();
     436 + reopen();
     437 + }
     438 + 
    406 439   public void open(Path path) {
    407 440   open(Collections.singletonList(path), EMPTY_RUNNABLE);
    408 441   }
    skipped 372 lines
    781 814   }
    782 815   
    783 816   private void treeRightClickAction(MouseEvent e) {
    784  - JNode obj = getJNodeUnderMouse(e);
    785  - if (obj instanceof JPackage) {
    786  - JPackagePopupMenu menu = new JPackagePopupMenu(this, (JPackage) obj);
    787  - menu.show(e.getComponent(), e.getX(), e.getY());
    788  - } else if (obj instanceof JClass || obj instanceof JField || obj instanceof JMethod) {
    789  - JMenuItem jmi = new JMenuItem(NLS.str("popup.rename"));
    790  - jmi.addActionListener(action -> RenameDialog.rename(this, obj));
    791  - JPopupMenu menu = new JPopupMenu();
    792  - menu.add(jmi);
     817 + JNode node = getJNodeUnderMouse(e);
     818 + if (node == null) {
     819 + return;
     820 + }
     821 + JPopupMenu menu = node.onTreePopupMenu(this);
     822 + if (menu != null) {
    793 823   menu.show(e.getComponent(), e.getX(), e.getY());
    794 824   }
    795 825   }
    skipped 142 lines
    938 968   }
    939 969   };
    940 970   saveAllAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.save_all"));
    941  - saveAllAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_S, UiUtils.ctrlButton()));
     971 + saveAllAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton()));
    942 972   
    943 973   Action exportAction = new AbstractAction(NLS.str("file.export_gradle"), ICON_EXPORT) {
    944 974   @Override
    skipped 2 lines
    947 977   }
    948 978   };
    949 979   exportAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("file.export_gradle"));
    950  - exportAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton()));
     980 + exportAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
    951 981   
    952 982   JMenu recentProjects = new JMenu(NLS.str("menu.recent_projects"));
    953 983   recentProjects.addMenuListener(new RecentProjectsMenuListener(recentProjects));
    skipped 194 lines
    1148 1178   nav.add(backAction);
    1149 1179   nav.add(forwardAction);
    1150 1180   
     1181 + pluginsMenu = new JMenu(NLS.str("menu.plugins"));
     1182 + pluginsMenu.setMnemonic(KeyEvent.VK_P);
     1183 + pluginsMenu.setVisible(false);
     1184 + 
    1151 1185   JMenu tools = new JMenu(NLS.str("menu.tools"));
    1152 1186   tools.setMnemonic(KeyEvent.VK_T);
    1153 1187   tools.add(deobfMenuItem);
    skipped 18 lines
    1172 1206   menuBar.add(view);
    1173 1207   menuBar.add(nav);
    1174 1208   menuBar.add(tools);
     1209 + menuBar.add(pluginsMenu);
    1175 1210   menuBar.add(help);
    1176 1211   setJMenuBar(menuBar);
    1177 1212   
    skipped 452 lines
    1630 1665   @Override
    1631 1666   public void menuCanceled(MenuEvent e) {
    1632 1667   }
     1668 + }
     1669 + 
     1670 + public JMenu getPluginsMenu() {
     1671 + return pluginsMenu;
    1633 1672   }
    1634 1673  }
    1635 1674   
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/TabComponent.java
    skipped 17 lines
    18 18  import javax.swing.plaf.basic.BasicButtonUI;
    19 19   
    20 20  import jadx.gui.treemodel.JClass;
     21 +import jadx.gui.treemodel.JEditableNode;
    21 22  import jadx.gui.treemodel.JNode;
    22 23  import jadx.gui.ui.panel.ContentPanel;
    23 24  import jadx.gui.utils.Icons;
    skipped 29 lines
    53 54   setOpaque(false);
    54 55   
    55 56   JNode node = contentPanel.getNode();
    56  - String tabTitle;
    57  - if (node.getRootClass() != null) {
    58  - tabTitle = node.getRootClass().getName();
    59  - } else {
    60  - tabTitle = node.makeLongStringHtml();
    61  - }
    62  - label = new NodeLabel(tabTitle, node.disableHtml());
     57 + label = new NodeLabel(buildTabTitle(node), node.disableHtml());
    63 58   label.setFont(getLabelFont());
    64 59   String toolTip = contentPanel.getTabTooltip();
    65 60   if (toolTip != null) {
    skipped 1 lines
    67 62   }
    68 63   label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
    69 64   label.setIcon(node.getIcon());
     65 + if (node instanceof JEditableNode) {
     66 + ((JEditableNode) node).addChangeListener(c -> label.setText(buildTabTitle(node)));
     67 + }
    70 68   
    71 69   final JButton closeBtn = new JButton();
    72 70   closeBtn.setIcon(Icons.CLOSE_INACTIVE);
    skipped 29 lines
    102 100   add(label);
    103 101   add(closeBtn);
    104 102   setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
     103 + }
     104 + 
     105 + private String buildTabTitle(JNode node) {
     106 + String tabTitle;
     107 + if (node.getRootClass() != null) {
     108 + tabTitle = node.getRootClass().getName();
     109 + } else {
     110 + tabTitle = node.makeLongStringHtml();
     111 + }
     112 + if (node instanceof JEditableNode) {
     113 + if (((JEditableNode) node).isChanged()) {
     114 + return "*" + tabTitle;
     115 + }
     116 + }
     117 + return tabTitle;
    105 118   }
    106 119   
    107 120   private JPopupMenu createTabPopupMenu(final ContentPanel contentPanel) {
    skipped 59 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/codearea/AbstractCodeArea.java
    skipped 45 lines
    46 46  import jadx.core.utils.exceptions.JadxRuntimeException;
    47 47  import jadx.gui.settings.JadxSettings;
    48 48  import jadx.gui.treemodel.JClass;
     49 +import jadx.gui.treemodel.JEditableNode;
    49 50  import jadx.gui.treemodel.JNode;
    50 51  import jadx.gui.ui.MainWindow;
    51 52  import jadx.gui.ui.panel.ContentPanel;
    skipped 1 lines
    53 54  import jadx.gui.utils.JumpPosition;
    54 55  import jadx.gui.utils.NLS;
    55 56  import jadx.gui.utils.UiUtils;
     57 +import jadx.gui.utils.ui.DocumentUpdateListener;
    56 58  import jadx.gui.utils.ui.ZoomActions;
    57 59   
    58 60  public abstract class AbstractCodeArea extends RSyntaxTextArea {
    skipped 16 lines
    75 77   protected ContentPanel contentPanel;
    76 78   protected JNode node;
    77 79   
     80 + protected volatile boolean loaded = false;
     81 + 
    78 82   public AbstractCodeArea(ContentPanel contentPanel, JNode node) {
    79 83   this.contentPanel = contentPanel;
    80 84   this.node = Objects.requireNonNull(node);
    81 85   
    82 86   setMarkOccurrences(false);
    83  - setEditable(false);
     87 + setEditable(node.isEditable());
    84 88   setCodeFoldingEnabled(false);
    85 89   setFadeCurrentLineHighlight(true);
    86 90   setCloseCurlyBraces(true);
    skipped 3 lines
    90 94   JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
    91 95   setLineWrap(settings.isCodeAreaLineWrap());
    92 96   addWrapLineMenuAction(settings);
    93  - 
    94  - addCaretActions();
    95  - addFastCopyAction();
    96 97   
    97 98   ZoomActions.register(this, settings, this::loadSettings);
     99 + 
     100 + if (node instanceof JEditableNode) {
     101 + JEditableNode editableNode = (JEditableNode) node;
     102 + addSaveActions(editableNode);
     103 + addChangeUpdates(editableNode);
     104 + } else {
     105 + addCaretActions();
     106 + addFastCopyAction();
     107 + }
    98 108   }
    99 109   
    100 110   private void addWrapLineMenuAction(JadxSettings settings) {
    skipped 85 lines
    186 196   });
    187 197   }
    188 198   
     199 + private void addSaveActions(JEditableNode node) {
     200 + addKeyListener(new KeyAdapter() {
     201 + @Override
     202 + public void keyPressed(KeyEvent e) {
     203 + if (e.getKeyCode() == KeyEvent.VK_S && UiUtils.isCtrlDown(e)) {
     204 + node.save(AbstractCodeArea.this.getText());
     205 + node.setChanged(false);
     206 + }
     207 + }
     208 + });
     209 + }
     210 + 
     211 + private void addChangeUpdates(JEditableNode editableNode) {
     212 + getDocument().addDocumentListener(new DocumentUpdateListener(ev -> {
     213 + if (loaded) {
     214 + editableNode.setChanged(true);
     215 + }
     216 + }));
     217 + }
     218 + 
    189 219   private String highlightCaretWord(String lastText, int pos) {
    190 220   String text = getWordByPosition(pos);
    191 221   if (StringUtils.isEmpty(text)) {
    skipped 52 lines
    244 274   
    245 275   /**
    246 276   * Implement in this method the code that loads and sets the content to be displayed
     277 + * Call `setLoaded()` on load finish.
    247 278   */
    248 279   public abstract void load();
     280 + 
     281 + public void setLoaded() {
     282 + this.loaded = true;
     283 + }
    249 284   
    250 285   /**
    251 286   * Implement in this method the code that reloads node from cache and sets the new content to be
    skipped 170 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java
    skipped 98 lines
    99 99   if (getText().isEmpty()) {
    100 100   setText(getCodeInfo().getCodeStr());
    101 101   setCaretPosition(0);
     102 + setLoaded();
    102 103   }
    103 104   }
    104 105   
    skipped 196 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeContentPanel.java
    skipped 2 lines
    3 3  import java.awt.BorderLayout;
    4 4  import java.awt.Point;
    5 5   
     6 +import org.slf4j.Logger;
     7 +import org.slf4j.LoggerFactory;
     8 + 
    6 9  import jadx.gui.treemodel.JNode;
    7 10  import jadx.gui.ui.TabbedPane;
    8 11  import jadx.gui.ui.panel.IViewStateSupport;
    9 12   
    10 13  public final class CodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport {
    11 14   private static final long serialVersionUID = 5310536092010045565L;
     15 + 
     16 + private static final Logger LOG = LoggerFactory.getLogger(CodeContentPanel.class);
    12 17   
    13 18   private final CodePanel codePanel;
    14 19   
    skipped 44 lines
    59 64   
    60 65   @Override
    61 66   public void restoreEditorViewState(EditorViewState viewState) {
    62  - codePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());
    63  - codePanel.getCodeArea().setCaretPosition(viewState.getCaretPos());
     67 + try {
     68 + codePanel.getCodeScrollPane().getViewport().setViewPosition(viewState.getViewPoint());
     69 + codePanel.getCodeArea().setCaretPosition(viewState.getCaretPos());
     70 + } catch (Exception e) {
     71 + LOG.error("Failed to restore view state", e);
     72 + }
    64 73   }
    65 74   
    66 75   @Override
    skipped 5 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/codearea/SmaliArea.java
    skipped 92 lines
    93 93   curVersion = shouldUseSmaliPrinterV2();
    94 94   model.load();
    95 95   setCaretPosition(0);
     96 + setLoaded();
    96 97   }
    97 98   }
    98 99   
    skipped 344 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java
    skipped 19 lines
    20 20  import javax.swing.JButton;
    21 21  import javax.swing.JDialog;
    22 22  import javax.swing.JLabel;
     23 +import javax.swing.JMenuItem;
    23 24  import javax.swing.JPanel;
     25 +import javax.swing.JPopupMenu;
    24 26  import javax.swing.JTextField;
    25 27  import javax.swing.WindowConstants;
    26 28   
    skipped 55 lines
    82 84   UiUtils.uiRun(() -> renameDialog.setVisible(true));
    83 85   UiUtils.uiRun(renameDialog::initRenameField); // wait for UI events to propagate
    84 86   return true;
     87 + }
     88 + 
     89 + public static JPopupMenu buildRenamePopup(MainWindow mainWindow, JNode node) {
     90 + JMenuItem jmi = new JMenuItem(NLS.str("popup.rename"));
     91 + jmi.addActionListener(action -> RenameDialog.rename(mainWindow, node));
     92 + JPopupMenu menu = new JPopupMenu();
     93 + menu.add(jmi);
     94 + return menu;
    85 95   }
    86 96   
    87 97   private RenameDialog(MainWindow mainWindow, JNode source, JNode node) {
    skipped 274 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java
    skipped 15 lines
    16 16   
    17 17  public class FileDialogWrapper {
    18 18   
     19 + private static final List<String> OPEN_FILES_EXTS = Arrays.asList(
     20 + "apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "jadx.kts");
     21 + 
    19 22   private final MainWindow mainWindow;
    20 23   
    21 24   private boolean isOpen;
    skipped 38 lines
    60 63   
    61 64   private void initForMode(FileOpenMode mode) {
    62 65   switch (mode) {
    63  - case OPEN:
    64 66   case OPEN_PROJECT:
     67 + title = NLS.str("file.open_title");
     68 + fileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);
     69 + selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
     70 + currentDir = mainWindow.getSettings().getLastOpenFilePath();
     71 + isOpen = true;
     72 + break;
     73 + 
     74 + case OPEN:
     75 + title = NLS.str("file.open_title");
     76 + fileExtList = new ArrayList<>(OPEN_FILES_EXTS);
     77 + fileExtList.add(JadxProject.PROJECT_EXTENSION);
     78 + fileExtList.add("aab");
     79 + selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
     80 + currentDir = mainWindow.getSettings().getLastOpenFilePath();
     81 + isOpen = true;
     82 + break;
     83 + 
    65 84   case ADD:
    66  - if (mode == FileOpenMode.OPEN_PROJECT) {
    67  - fileExtList = Collections.singletonList(JadxProject.PROJECT_EXTENSION);
    68  - title = NLS.str("file.open_title");
    69  - } else {
    70  - fileExtList = new ArrayList<>(Arrays.asList("apk", "dex", "jar", "class", "smali", "zip", "xapk", "aar", "arsc"));
    71  - if (mode == FileOpenMode.OPEN) {
    72  - fileExtList.addAll(Arrays.asList(JadxProject.PROJECT_EXTENSION, "aab"));
    73  - title = NLS.str("file.open_title");
    74  - } else {
    75  - title = NLS.str("file.add_files_action");
    76  - }
    77  - }
     85 + title = NLS.str("file.add_files_action");
     86 + fileExtList = OPEN_FILES_EXTS;
    78 87   selectionMode = JFileChooser.FILES_AND_DIRECTORIES;
    79 88   currentDir = mainWindow.getSettings().getLastOpenFilePath();
    80 89   isOpen = true;
    skipped 59 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/Icons.java
    skipped 16 lines
    17 17   public static final ImageIcon FINAL = openSvgIcon("nodes/finalMark");
    18 18   
    19 19   public static final ImageIcon START_PAGE = openSvgIcon("nodes/newWindow");
     20 + 
     21 + public static final ImageIcon FOLDER = UiUtils.openSvgIcon("nodes/folder");
     22 + public static final ImageIcon FILE = UiUtils.openSvgIcon("nodes/file_any_type");
    20 23  }
    21 24   
  • ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/ui/DocumentUpdateListener.java
    skipped 24 lines
    25 25   
    26 26   @Override
    27 27   public void changedUpdate(DocumentEvent event) {
    28  - this.listener.accept(event);
     28 + // ignore attributes change
    29 29   }
    30 30  }
    31 31   
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/ui/SimpleMenuItem.java
     1 +package jadx.gui.utils.ui;
     2 + 
     3 +import javax.swing.JMenuItem;
     4 + 
     5 +public class SimpleMenuItem extends JMenuItem {
     6 + 
     7 + public SimpleMenuItem(String text, Runnable action) {
     8 + super(text);
     9 + addActionListener(ev -> action.run());
     10 + }
     11 +}
     12 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/files/script.jadx.kts.tmpl
     1 +val jadx = getJadxInstance()
     2 + 
     3 +jadx.afterLoad {
     4 + log.info { "Hello from jadx script!" }
     5 +}
     6 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
    skipped 13 lines
    14 14  menu.class_search=Klassen-Suche
    15 15  menu.comment_search=Kommentar suchen
    16 16  menu.tools=Tools
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=Deobfuskierung
    18 19  menu.log=Log-Anzeige
    19 20  menu.help=Hilfe
    skipped 12 lines
    32 33  file.live_reload_desc=Dateien bei Änderungen autom. neuladen
    33 34  file.export_mappings_as=Zuordnungen exportieren als…
    34 35  file.save_all=Alles speichern
     36 +#file.save=Save
    35 37  file.export_gradle=Als Gradle-Projekt speichern
    36 38  file.save_all_msg=Verzeichnis für das Speichern dekompilierter Ressourcen auswählen
    37 39  file.exit=Beenden
    skipped 2 lines
    40 42  #start_page.start=Start
    41 43  #start_page.recent=Recent projects
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=Quelltexte
    44 49  tree.resources_title=Ressourcen
    45 50  tree.loading=Laden…
    skipped 183 lines
    229 234  popup.rename=Umbennen
    230 235  popup.search=Suche "%s"
    231 236  popup.search_global=Globale Suche "%s"
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=Paketauswahl
    234 243  exclude_dialog.ok=OK
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_en_US.properties
    skipped 13 lines
    14 14  menu.class_search=Class search
    15 15  menu.comment_search=Comment searchF
    16 16  menu.tools=Tools
     17 +menu.plugins=Plugins
    17 18  menu.deobfuscation=Deobfuscation
    18 19  menu.log=Log Viewer
    19 20  menu.help=Help
    skipped 12 lines
    32 33  file.live_reload_desc=Auto reload files on changes
    33 34  file.export_mappings_as=Export mappings as...
    34 35  file.save_all=Save all
     36 +file.save=Save
    35 37  file.export_gradle=Save as gradle project
    36 38  file.save_all_msg=Select directory for save decompiled sources
    37 39  file.exit=Exit
    skipped 2 lines
    40 42  start_page.start=Start
    41 43  start_page.recent=Recent projects
    42 44   
     45 +tree.inputs_title=Inputs
     46 +tree.input_files=Files
     47 +tree.input_scripts=Scripts
    43 48  tree.sources_title=Source code
    44 49  tree.resources_title=Resources
    45 50  tree.loading=Loading...
    skipped 183 lines
    229 234  popup.rename=Rename
    230 235  popup.search=Search "%s"
    231 236  popup.search_global=Global Search "%s"
     237 +popup.remove=Remove
     238 +popup.add_files=Add files
     239 +popup.add_scripts=Add scripts
     240 +popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=Package Selector
    234 243  exclude_dialog.ok=OK
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
    skipped 13 lines
    14 14  menu.class_search=Buscar clase
    15 15  #menu.comment_search=Comment search
    16 16  menu.tools=Herramientas
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=Desofuscación
    18 19  menu.log=Visor log
    19 20  menu.help=Ayuda
    skipped 12 lines
    32 33  #file.live_reload_desc=Auto reload files on changes
    33 34  #file.export_mappings_as=
    34 35  file.save_all=Guardar todo
     36 +#file.save=Save
    35 37  file.export_gradle=Guardar como proyecto Gradle
    36 38  file.save_all_msg=Seleccionar carpeta para guardar fuentes descompiladas
    37 39  file.exit=Salir
    skipped 2 lines
    40 42  #start_page.start=Start
    41 43  #start_page.recent=Recent projects
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=Código fuente
    44 49  tree.resources_title=Recursos
    45 50  tree.loading=Cargando...
    skipped 183 lines
    229 234  popup.rename=Nimeta ümber
    230 235  #popup.search=
    231 236  #popup.search_global=
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  #exclude_dialog.title=Package Selector
    234 243  #exclude_dialog.ok=OK
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
    skipped 13 lines
    14 14  menu.class_search=클래스 검색
    15 15  menu.comment_search=주석 검색
    16 16  menu.tools=도구
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=난독화 해제
    18 19  menu.log=로그 뷰어
    19 20  menu.help=도움말
    skipped 12 lines
    32 33  file.live_reload_desc=파일 내용 변경 시 자동으로 다시 로드
    33 34  file.export_mappings_as=다른 이름으로 매핑 내보내기...
    34 35  file.save_all=모두 저장
     36 +#file.save=Save
    35 37  file.export_gradle=Gradle 프로젝트로 저장
    36 38  file.save_all_msg=디컴파일된 소스를 저장할 디렉토리 선택
    37 39  file.exit=나가기
    skipped 2 lines
    40 42  start_page.start=시작
    41 43  start_page.recent=최근 프로젝트
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=소스코드
    44 49  tree.resources_title=리소스
    45 50  tree.loading=로딩중...
    skipped 183 lines
    229 234  popup.rename=이름 바꾸기
    230 235  popup.search="%s" 검색
    231 236  popup.search_global="%s" 전역 검색
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=패키지 선택기
    234 243  exclude_dialog.ok=확인
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties
    skipped 13 lines
    14 14  menu.class_search=Buscar por classe
    15 15  menu.comment_search=Busca por comentário
    16 16  menu.tools=Ferramentas
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=Desofuscar
    18 19  menu.log=Visualizador de log
    19 20  menu.help=Ajuda
    skipped 12 lines
    32 33  file.live_reload_desc=Recarregar arquivos automaticamente ao serem alterados
    33 34  file.export_mappings_as=Exportar mappings como...
    34 35  file.save_all=Salvar tudo
     36 +#file.save=Save
    35 37  file.export_gradle=Salvar como um projeto gradle
    36 38  file.save_all_msg=Selecionar diretório para salvar arquivos descompilados
    37 39  file.exit=Sair
    skipped 2 lines
    40 42  start_page.start=Começar
    41 43  start_page.recent=Projetos recentes
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=Código fonte
    44 49  tree.resources_title=Recursos
    45 50  tree.loading=Carregando...
    skipped 183 lines
    229 234  popup.rename=Renomear
    230 235  popup.search=Buscar "%s"
    231 236  popup.search_global=Busca global "%s"
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=Selecionar pacote
    234 243  exclude_dialog.ok=OK
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
    skipped 13 lines
    14 14  menu.class_search=类名搜索
    15 15  menu.comment_search=注释搜索
    16 16  menu.tools=工具
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=反混淆
    18 19  menu.log=日志查看器
    19 20  menu.help=帮助
    skipped 12 lines
    32 33  file.live_reload_desc=文件变动时自动重载
    33 34  #file.export_mappings_as=
    34 35  file.save_all=全部保存
     36 +#file.save=Save
    35 37  file.export_gradle=另存为 Gradle 项目
    36 38  file.save_all_msg=请选择保存反编译资源的目录
    37 39  file.exit=退出
    skipped 2 lines
    40 42  start_page.start=开始
    41 43  start_page.recent=最近项目
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=源代码
    44 49  tree.resources_title=资源文件
    45 50  tree.loading=加载中...
    skipped 183 lines
    229 234  popup.rename=重命名
    230 235  popup.search=搜索 “%s”
    231 236  popup.search_global=全局搜索 “%s”
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=选择要排除的包
    234 243  exclude_dialog.ok=确定
    skipped 110 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties
    skipped 13 lines
    14 14  menu.class_search=類別搜尋
    15 15  menu.comment_search=註解搜尋
    16 16  menu.tools=工具
     17 +#menu.plugins=Plugins
    17 18  menu.deobfuscation=去模糊化
    18 19  menu.log=日誌檢視器
    19 20  menu.help=幫助
    skipped 12 lines
    32 33  file.live_reload_desc=更動後自動重新載入檔案
    33 34  file.export_mappings_as=匯出對應為...
    34 35  file.save_all=全部儲存
     36 +#file.save=Save
    35 37  file.export_gradle=另存為 gradle 專案
    36 38  file.save_all_msg=選擇儲存反編譯原始碼的路徑
    37 39  file.exit=離開
    skipped 2 lines
    40 42  start_page.start=開始
    41 43  start_page.recent=近期專案
    42 44   
     45 +#tree.inputs_title=Inputs
     46 +#tree.input_files=Files
     47 +#tree.input_scripts=Scripts
    43 48  tree.sources_title=原始碼
    44 49  tree.resources_title=資源
    45 50  tree.loading=載入中...
    skipped 183 lines
    229 234  popup.rename=重新命名
    230 235  popup.search=搜尋 "%s"
    231 236  popup.search_global=全域搜尋 "%s"
     237 +#popup.remove=Remove
     238 +#popup.add_files=Add files
     239 +#popup.add_scripts=Add scripts
     240 +#popup.new_script=New script
    232 241   
    233 242  exclude_dialog.title=套件選擇
    234 243  exclude_dialog.ok=OK
    skipped 110 lines
  • jadx-gui/src/main/resources/icons/nodes/kotlin_script.svg
  • jadx-gui/src/main/resources/icons/nodes/moduleDirectory.svg
  • jadx-gui/src/main/resources/icons/nodes/projectStructure.svg
  • jadx-gui/src/main/resources/icons/nodes/scriptsModel.svg
Please wait...
Page is in error, reload to recover