Projects STRLCPY jadx Commits af9cebeb
🤬
  • feat(gui): allow to dock log viewer, new filter modes

  • Loading...
  • Skylot committed 1 year ago
    af9cebeb
    1 parent a2ac7f2c
  • ■ ■ ■ ■ ■
    jadx-gui/build.gradle
    skipped 13 lines
    14 14   
    15 15   // jadx-script autocomplete support
    16 16   implementation(project(":jadx-plugins::jadx-script:jadx-script-ide"))
     17 + implementation(project(":jadx-plugins::jadx-script:jadx-script-runtime"))
    17 18   implementation 'org.jetbrains.kotlin:kotlin-scripting-common:1.7.20'
    18 19   implementation 'com.fifesoft:autocomplete:3.3.0'
    19 20   
    20  - // use ktlint for lint and format jadx scripts
     21 + // use KtLint for format and check jadx scripts
    21 22   implementation 'com.pinterest.ktlint:ktlint-core:0.47.1'
    22 23   implementation 'com.pinterest.ktlint:ktlint-ruleset-standard:0.47.1'
    23 24   
    skipped 135 lines
  • ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/JadxGUI.java
    skipped 5 lines
    6 6  import org.slf4j.LoggerFactory;
    7 7   
    8 8  import jadx.cli.LogHelper;
     9 +import jadx.gui.logs.LogCollector;
    9 10  import jadx.gui.settings.JadxSettings;
    10 11  import jadx.gui.settings.JadxSettingsAdapter;
    11 12  import jadx.gui.ui.ExceptionDialog;
    skipped 1 lines
    13 14  import jadx.gui.utils.LafManager;
    14 15  import jadx.gui.utils.NLS;
    15 16  import jadx.gui.utils.SystemInfo;
    16  -import jadx.gui.utils.logs.LogCollector;
    17 17   
    18 18  public class JadxGUI {
    19 19   private static final Logger LOG = LoggerFactory.getLogger(JadxGUI.class);
    skipped 37 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/ILogListener.java
     1 +package jadx.gui.logs;
     2 + 
     3 +public interface ILogListener {
     4 + void onAppend(LogEvent logEvent);
     5 + 
     6 + void onReload();
     7 +}
     8 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/IssuesListener.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import javax.swing.SwingUtilities;
     4 + 
     5 +import ch.qos.logback.classic.Level;
     6 + 
     7 +import jadx.gui.ui.panel.IssuesPanel;
     8 +import jadx.gui.utils.rx.DebounceUpdate;
     9 + 
     10 +public class IssuesListener implements ILogListener {
     11 + private final IssuesPanel issuesPanel;
     12 + private final DebounceUpdate updater;
     13 + 
     14 + private int errors = 0;
     15 + private int warnings = 0;
     16 + 
     17 + public IssuesListener(IssuesPanel issuesPanel) {
     18 + this.issuesPanel = issuesPanel;
     19 + this.updater = new DebounceUpdate(500, this::onUpdate);
     20 + }
     21 + 
     22 + private void onUpdate() {
     23 + SwingUtilities.invokeLater(() -> issuesPanel.onUpdate(errors, warnings));
     24 + }
     25 + 
     26 + @Override
     27 + public void onAppend(LogEvent logEvent) {
     28 + switch (logEvent.getLevel().toInt()) {
     29 + case Level.ERROR_INT:
     30 + errors++;
     31 + updater.requestUpdate();
     32 + break;
     33 + 
     34 + case Level.WARN_INT:
     35 + warnings++;
     36 + updater.requestUpdate();
     37 + break;
     38 + }
     39 + }
     40 + 
     41 + @Override
     42 + public void onReload() {
     43 + errors = 0;
     44 + warnings = 0;
     45 + updater.requestUpdate();
     46 + }
     47 + 
     48 + public int getErrors() {
     49 + return errors;
     50 + }
     51 + 
     52 + public int getWarnings() {
     53 + return warnings;
     54 + }
     55 +}
     56 + 
  • ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/logs/LimitedQueue.java jadx-gui/src/main/java/jadx/gui/logs/LimitedQueue.java
    1  -package jadx.gui.utils.logs;
     1 +package jadx.gui.logs;
    2 2   
    3 3  import java.util.AbstractQueue;
    4 4  import java.util.ArrayDeque;
    skipped 47 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogAppender.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import org.apache.commons.lang3.StringUtils;
     4 +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
     5 + 
     6 +import jadx.core.utils.exceptions.JadxRuntimeException;
     7 +import jadx.gui.utils.UiUtils;
     8 + 
     9 +import static jadx.plugins.script.runtime.JadxScriptTemplateKt.JADX_SCRIPT_LOG_PREFIX;
     10 + 
     11 +class LogAppender implements ILogListener {
     12 + private final LogOptions options;
     13 + private final RSyntaxTextArea textArea;
     14 + 
     15 + public LogAppender(LogOptions options, RSyntaxTextArea textArea) {
     16 + this.options = options;
     17 + this.textArea = textArea;
     18 + }
     19 + 
     20 + @Override
     21 + public void onAppend(LogEvent logEvent) {
     22 + if (accept(logEvent)) {
     23 + UiUtils.uiRun(() -> textArea.append(logEvent.getMsg()));
     24 + }
     25 + }
     26 + 
     27 + @Override
     28 + public void onReload() {
     29 + UiUtils.uiRunAndWait(() -> textArea.append(StringUtils.repeat('=', 100) + '\n'));
     30 + }
     31 + 
     32 + private boolean accept(LogEvent logEvent) {
     33 + boolean byLevel = logEvent.getLevel().isGreaterOrEqual(options.getLogLevel());
     34 + if (!byLevel) {
     35 + return false;
     36 + }
     37 + switch (options.getMode()) {
     38 + case ALL:
     39 + return true;
     40 + 
     41 + case ALL_SCRIPTS:
     42 + return logEvent.getLoggerName().startsWith(JADX_SCRIPT_LOG_PREFIX);
     43 + 
     44 + case CURRENT_SCRIPT:
     45 + return logEvent.getLoggerName().equals(options.getFilter());
     46 + 
     47 + default:
     48 + throw new JadxRuntimeException("Unexpected log mode: " + options.getMode());
     49 + }
     50 + }
     51 +}
     52 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogCollector.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import java.util.ArrayList;
     4 +import java.util.List;
     5 +import java.util.Queue;
     6 + 
     7 +import org.jetbrains.annotations.Nullable;
     8 +import org.slf4j.LoggerFactory;
     9 + 
     10 +import ch.qos.logback.classic.Logger;
     11 +import ch.qos.logback.classic.LoggerContext;
     12 +import ch.qos.logback.classic.PatternLayout;
     13 +import ch.qos.logback.classic.spi.ILoggingEvent;
     14 +import ch.qos.logback.core.AppenderBase;
     15 +import ch.qos.logback.core.Layout;
     16 + 
     17 +public class LogCollector extends AppenderBase<ILoggingEvent> {
     18 + public static final int BUFFER_SIZE = 5000;
     19 + 
     20 + private static final LogCollector INSTANCE = new LogCollector();
     21 + 
     22 + public static LogCollector getInstance() {
     23 + return INSTANCE;
     24 + }
     25 + 
     26 + public static void register() {
     27 + Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
     28 + LoggerContext loggerContext = rootLogger.getLoggerContext();
     29 + 
     30 + PatternLayout layout = new PatternLayout();
     31 + layout.setContext(loggerContext);
     32 + layout.setPattern("%-5level: %msg%n");
     33 + layout.start();
     34 + 
     35 + INSTANCE.setContext(loggerContext);
     36 + INSTANCE.setLayout(layout);
     37 + INSTANCE.start();
     38 + 
     39 + rootLogger.addAppender(INSTANCE);
     40 + }
     41 + 
     42 + private final List<ILogListener> listeners = new ArrayList<>();
     43 + private final Queue<LogEvent> buffer = new LimitedQueue<>(BUFFER_SIZE);
     44 + 
     45 + private Layout<ILoggingEvent> layout;
     46 + 
     47 + public LogCollector() {
     48 + setName("LogCollector");
     49 + }
     50 + 
     51 + @Override
     52 + protected synchronized void append(ILoggingEvent event) {
     53 + String msg = layout.doLayout(event);
     54 + LogEvent logEvent = new LogEvent(event.getLevel(), event.getLoggerName(), msg);
     55 + buffer.offer(logEvent);
     56 + listeners.forEach(l -> l.onAppend(logEvent));
     57 + }
     58 + 
     59 + private void setLayout(Layout<ILoggingEvent> layout) {
     60 + this.layout = layout;
     61 + }
     62 + 
     63 + public synchronized void registerListener(ILogListener listener) {
     64 + listeners.add(listener);
     65 + buffer.forEach(listener::onAppend);
     66 + }
     67 + 
     68 + public synchronized boolean removeListener(@Nullable ILogListener listener) {
     69 + if (listener == null) {
     70 + return false;
     71 + }
     72 + return this.listeners.removeIf(l -> l == listener);
     73 + }
     74 + 
     75 + public synchronized boolean removeListenerByClass(Class<?> listenerCls) {
     76 + return this.listeners.removeIf(l -> l.getClass().equals(listenerCls));
     77 + }
     78 + 
     79 + public synchronized void reset() {
     80 + buffer.clear();
     81 + listeners.forEach(ILogListener::onReload);
     82 + }
     83 +}
     84 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogEvent.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import ch.qos.logback.classic.Level;
     4 + 
     5 +public final class LogEvent {
     6 + private final Level level;
     7 + private final String loggerName;
     8 + private final String msg;
     9 + 
     10 + LogEvent(Level level, String loggerName, String msg) {
     11 + this.level = level;
     12 + this.loggerName = loggerName;
     13 + this.msg = msg;
     14 + }
     15 + 
     16 + public Level getLevel() {
     17 + return level;
     18 + }
     19 + 
     20 + public String getLoggerName() {
     21 + return loggerName;
     22 + }
     23 + 
     24 + public String getMsg() {
     25 + return msg;
     26 + }
     27 + 
     28 + @Override
     29 + public String toString() {
     30 + return level + ": " + loggerName + " - " + msg;
     31 + }
     32 +}
     33 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogMode.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import org.apache.commons.lang3.StringUtils;
     4 + 
     5 +import jadx.gui.utils.NLS;
     6 + 
     7 +public enum LogMode {
     8 + ALL,
     9 + ALL_SCRIPTS,
     10 + CURRENT_SCRIPT;
     11 + 
     12 + private static final String[] NLS_STRINGS = StringUtils.split(NLS.str("log_viewer.modes"), '|');
     13 + 
     14 + public String getLocalizedName() {
     15 + return NLS_STRINGS[this.ordinal()];
     16 + }
     17 + 
     18 + @Override
     19 + public String toString() {
     20 + return getLocalizedName();
     21 + }
     22 +}
     23 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogOptions.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import org.jetbrains.annotations.Nullable;
     4 + 
     5 +import ch.qos.logback.classic.Level;
     6 + 
     7 +import jadx.core.utils.Utils;
     8 + 
     9 +import static jadx.plugins.script.runtime.JadxScriptTemplateKt.JADX_SCRIPT_LOG_PREFIX;
     10 + 
     11 +public class LogOptions {
     12 + 
     13 + /**
     14 + * Store latest requested log options
     15 + */
     16 + private static LogOptions current = new LogOptions(LogMode.ALL, Level.INFO, null);
     17 + 
     18 + public static LogOptions allWithLevel(@Nullable Level logLevel) {
     19 + Level level = Utils.getOrElse(logLevel, current.getLogLevel());
     20 + return store(new LogOptions(LogMode.ALL, level, null));
     21 + }
     22 + 
     23 + public static LogOptions forLevel(@Nullable Level logLevel) {
     24 + Level level = Utils.getOrElse(logLevel, current.getLogLevel());
     25 + return store(new LogOptions(current.getMode(), level, current.getFilter()));
     26 + }
     27 + 
     28 + public static LogOptions forMode(LogMode mode) {
     29 + return store(new LogOptions(mode, current.getLogLevel(), current.getFilter()));
     30 + }
     31 + 
     32 + public static LogOptions forScript(String scriptName) {
     33 + String filter = JADX_SCRIPT_LOG_PREFIX + scriptName;
     34 + return store(new LogOptions(LogMode.CURRENT_SCRIPT, current.getLogLevel(), filter));
     35 + }
     36 + 
     37 + public static LogOptions current() {
     38 + return current;
     39 + }
     40 + 
     41 + private static LogOptions store(LogOptions logOptions) {
     42 + current = logOptions;
     43 + return logOptions;
     44 + }
     45 + 
     46 + private final LogMode mode;
     47 + private final Level logLevel;
     48 + private final @Nullable String filter;
     49 + 
     50 + private LogOptions(LogMode mode, Level logLevel, @Nullable String filter) {
     51 + this.mode = mode;
     52 + this.logLevel = logLevel;
     53 + this.filter = filter;
     54 + }
     55 + 
     56 + public LogMode getMode() {
     57 + return mode;
     58 + }
     59 + 
     60 + public Level getLogLevel() {
     61 + return logLevel;
     62 + }
     63 + 
     64 + public @Nullable String getFilter() {
     65 + return filter;
     66 + }
     67 + 
     68 + @Override
     69 + public String toString() {
     70 + return "LogOptions{mode=" + mode + ", logLevel=" + logLevel + ", filter='" + filter + '\'' + '}';
     71 + }
     72 +}
     73 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/logs/LogPanel.java
     1 +package jadx.gui.logs;
     2 + 
     3 +import java.awt.BorderLayout;
     4 +import java.awt.Dimension;
     5 + 
     6 +import javax.swing.BorderFactory;
     7 +import javax.swing.Box;
     8 +import javax.swing.BoxLayout;
     9 +import javax.swing.JButton;
     10 +import javax.swing.JComboBox;
     11 +import javax.swing.JLabel;
     12 +import javax.swing.JPanel;
     13 +import javax.swing.JScrollPane;
     14 +import javax.swing.event.ChangeListener;
     15 + 
     16 +import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
     17 +import org.jetbrains.annotations.Nullable;
     18 + 
     19 +import ch.qos.logback.classic.Level;
     20 + 
     21 +import jadx.gui.settings.JadxSettings;
     22 +import jadx.gui.treemodel.JInputScript;
     23 +import jadx.gui.treemodel.JNode;
     24 +import jadx.gui.ui.MainWindow;
     25 +import jadx.gui.ui.codearea.AbstractCodeArea;
     26 +import jadx.gui.ui.panel.ContentPanel;
     27 +import jadx.gui.utils.NLS;
     28 + 
     29 +public class LogPanel extends JPanel {
     30 + private static final long serialVersionUID = -8077649118322056081L;
     31 + 
     32 + private static final Level[] LEVEL_ITEMS = { Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR };
     33 + 
     34 + private final MainWindow mainWindow;
     35 + private final Runnable dockAction;
     36 + private final Runnable hideAction;
     37 + 
     38 + private RSyntaxTextArea textPane;
     39 + private JComboBox<LogMode> modeCb;
     40 + private JComboBox<Level> levelCb;
     41 + 
     42 + private ChangeListener activeTabListener;
     43 + 
     44 + public LogPanel(MainWindow mainWindow, LogOptions logOptions, Runnable dockAction, Runnable hideAction) {
     45 + this.mainWindow = mainWindow;
     46 + this.dockAction = dockAction;
     47 + this.hideAction = hideAction;
     48 + initUI(logOptions);
     49 + applyLogOptions(logOptions);
     50 + }
     51 + 
     52 + public void applyLogOptions(LogOptions logOptions) {
     53 + if (logOptions.getMode() == LogMode.CURRENT_SCRIPT) {
     54 + String scriptName = getCurrentScriptName();
     55 + if (scriptName != null) {
     56 + logOptions = LogOptions.forScript(scriptName);
     57 + }
     58 + registerActiveTabListener();
     59 + } else {
     60 + removeActiveTabListener();
     61 + }
     62 + if (modeCb.getSelectedItem() != logOptions.getMode()) {
     63 + modeCb.setSelectedItem(logOptions.getMode());
     64 + }
     65 + if (levelCb.getSelectedItem() != logOptions.getLogLevel()) {
     66 + levelCb.setSelectedItem(logOptions.getLogLevel());
     67 + }
     68 + registerLogListener(logOptions);
     69 + }
     70 + 
     71 + public void loadSettings() {
     72 + AbstractCodeArea.loadCommonSettings(mainWindow, textPane);
     73 + }
     74 + 
     75 + private void initUI(LogOptions logOptions) {
     76 + JadxSettings settings = mainWindow.getSettings();
     77 + textPane = AbstractCodeArea.getDefaultArea(mainWindow);
     78 + textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
     79 + 
     80 + modeCb = new JComboBox<>(LogMode.values());
     81 + modeCb.setSelectedItem(logOptions.getMode());
     82 + modeCb.addActionListener(e -> applyLogOptions(LogOptions.forMode((LogMode) modeCb.getSelectedItem())));
     83 + JLabel modeLabel = new JLabel(NLS.str("log_viewer.mode"));
     84 + modeLabel.setLabelFor(modeCb);
     85 + 
     86 + levelCb = new JComboBox<>(LEVEL_ITEMS);
     87 + levelCb.setSelectedItem(logOptions.getLogLevel());
     88 + levelCb.addActionListener(e -> applyLogOptions(LogOptions.forLevel((Level) levelCb.getSelectedItem())));
     89 + JLabel levelLabel = new JLabel(NLS.str("log_viewer.log_level"));
     90 + levelLabel.setLabelFor(levelCb);
     91 + 
     92 + JButton clearBtn = new JButton(NLS.str("log_viewer.clear"));
     93 + clearBtn.addActionListener(ev -> {
     94 + LogCollector.getInstance().reset();
     95 + textPane.setText("");
     96 + });
     97 + 
     98 + JButton dockBtn = new JButton(NLS.str(settings.isDockLogViewer() ? "log_viewer.undock" : "log_viewer.dock"));
     99 + dockBtn.addActionListener(ev -> dockAction.run());
     100 + 
     101 + JButton hideBtn = new JButton(NLS.str("log_viewer.hide"));
     102 + hideBtn.addActionListener(ev -> hideAction.run());
     103 + 
     104 + JPanel start = new JPanel();
     105 + start.setLayout(new BoxLayout(start, BoxLayout.LINE_AXIS));
     106 + start.add(modeLabel);
     107 + start.add(Box.createRigidArea(new Dimension(5, 0)));
     108 + start.add(modeCb);
     109 + start.add(Box.createRigidArea(new Dimension(15, 0)));
     110 + start.add(levelLabel);
     111 + start.add(Box.createRigidArea(new Dimension(5, 0)));
     112 + start.add(levelCb);
     113 + start.add(Box.createRigidArea(new Dimension(5, 0)));
     114 + 
     115 + JPanel end = new JPanel();
     116 + end.setLayout(new BoxLayout(end, BoxLayout.LINE_AXIS));
     117 + end.add(clearBtn);
     118 + end.add(Box.createRigidArea(new Dimension(15, 0)));
     119 + end.add(dockBtn);
     120 + end.add(Box.createRigidArea(new Dimension(15, 0)));
     121 + end.add(hideBtn);
     122 + 
     123 + JPanel controlPane = new JPanel();
     124 + controlPane.setLayout(new BorderLayout());
     125 + controlPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
     126 + controlPane.add(start, BorderLayout.LINE_START);
     127 + controlPane.add(end, BorderLayout.LINE_END);
     128 + 
     129 + JScrollPane scrollPane = new JScrollPane(textPane);
     130 + 
     131 + setLayout(new BorderLayout(5, 5));
     132 + add(controlPane, BorderLayout.PAGE_START);
     133 + add(scrollPane, BorderLayout.CENTER);
     134 + }
     135 + 
     136 + private void registerLogListener(LogOptions logOptions) {
     137 + LogCollector logCollector = LogCollector.getInstance();
     138 + logCollector.removeListenerByClass(LogAppender.class);
     139 + textPane.setText("");
     140 + logCollector.registerListener(new LogAppender(logOptions, textPane));
     141 + }
     142 + 
     143 + private @Nullable String getCurrentScriptName() {
     144 + ContentPanel selectedCodePanel = mainWindow.getTabbedPane().getSelectedCodePanel();
     145 + if (selectedCodePanel != null) {
     146 + JNode node = selectedCodePanel.getNode();
     147 + if (node instanceof JInputScript) {
     148 + return node.getName();
     149 + }
     150 + }
     151 + return null;
     152 + }
     153 + 
     154 + private synchronized void registerActiveTabListener() {
     155 + removeActiveTabListener();
     156 + activeTabListener = e -> {
     157 + String scriptName = getCurrentScriptName();
     158 + if (scriptName != null) {
     159 + applyLogOptions(LogOptions.forScript(scriptName));
     160 + }
     161 + };
     162 + mainWindow.getTabbedPane().addChangeListener(activeTabListener);
     163 + }
     164 + 
     165 + private synchronized void removeActiveTabListener() {
     166 + if (activeTabListener != null) {
     167 + mainWindow.getTabbedPane().removeChangeListener(activeTabListener);
     168 + activeTabListener = null;
     169 + }
     170 + }
     171 + 
     172 + public void dispose() {
     173 + LogCollector.getInstance().removeListenerByClass(LogAppender.class);
     174 + removeActiveTabListener();
     175 + }
     176 +}
     177 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/plugins/script/ScriptContentPanel.java
    skipped 21 lines
    22 22  import com.pinterest.ktlint.core.LintError;
    23 23   
    24 24  import kotlin.script.experimental.api.ScriptDiagnostic;
     25 +import kotlin.script.experimental.api.ScriptDiagnostic.Severity;
    25 26   
    26 27  import jadx.gui.JadxWrapper;
     28 +import jadx.gui.logs.LogOptions;
    27 29  import jadx.gui.settings.JadxSettings;
    28 30  import jadx.gui.settings.LineNumbersMode;
    29 31  import jadx.gui.treemodel.JInputScript;
    skipped 9 lines
    39 41  import jadx.gui.utils.ui.NodeLabel;
    40 42  import jadx.plugins.script.ide.ScriptAnalyzeResult;
    41 43  import jadx.plugins.script.ide.ScriptCompiler;
     44 + 
     45 +import static jadx.plugins.script.runtime.JadxScriptTemplateKt.JADX_SCRIPT_LOG_PREFIX;
    42 46   
    43 47  public class ScriptContentPanel extends AbstractCodeContentPanel {
    44 48   private static final long serialVersionUID = 6575696321112417513L;
    45 49   
    46  - private static final Logger LOG = LoggerFactory.getLogger(ScriptContentPanel.class);
    47  - 
    48 50   private final ScriptCodeArea scriptArea;
    49 51   private final SearchBar searchBar;
    50 52   private final RTextScrollPane codeScrollPane;
    51 53   private final JPanel actionPanel;
    52 54   private final JLabel resultLabel;
    53 55   private final ScriptErrorService errorService;
     56 + private final Logger scriptLog;
    54 57   
    55 58   public ScriptContentPanel(TabbedPane panel, JInputScript scriptNode) {
    56 59   super(panel, scriptNode);
    skipped 3 lines
    60 63   actionPanel = buildScriptActionsPanel();
    61 64   searchBar = new SearchBar(scriptArea);
    62 65   codeScrollPane = new RTextScrollPane(scriptArea);
     66 + scriptLog = LoggerFactory.getLogger(JADX_SCRIPT_LOG_PREFIX + scriptNode.getName());
    63 67   
    64 68   initUI();
    65 69   applySettings();
    skipped 70 lines
    136 140   wrapper.resetGuiPluginsContext();
    137 141   wrapper.getDecompiler().reloadPasses();
    138 142   } catch (Exception e) {
    139  - LOG.error("Passes reload failed", e);
     143 + scriptLog.error("Passes reload failed", e);
    140 144   }
    141 145   }, taskStatus -> {
    142 146   tabbedPane.reloadInactiveTabs();
    skipped 10 lines
    153 157   ScriptCompiler scriptCompiler = new ScriptCompiler(fileName);
    154 158   ScriptAnalyzeResult result = scriptCompiler.analyze(code, scriptArea.getCaretPosition());
    155 159   List<ScriptDiagnostic> issues = result.getIssues();
     160 + boolean success = true;
    156 161   for (ScriptDiagnostic issue : issues) {
    157  - LOG.warn("Compiler issue: {}", issue);
     162 + Severity severity = issue.getSeverity();
     163 + if (severity == Severity.ERROR || severity == Severity.FATAL) {
     164 + scriptLog.error("{}", issue.render(false, true, true, true));
     165 + success = false;
     166 + } else {
     167 + scriptLog.warn("Compiler issue: {}", issue);
     168 + }
    158 169   }
    159  - boolean success = issues.stream().map(ScriptDiagnostic::getSeverity)
    160  - .noneMatch(s -> s == ScriptDiagnostic.Severity.ERROR || s == ScriptDiagnostic.Severity.FATAL);
    161  - 
    162 170   List<LintError> lintErrs = Collections.emptyList();
    163 171   if (success) {
    164 172   lintErrs = getLintIssues(code, fileName);
    skipped 5 lines
    170 178   errorService.apply();
    171 179   if (!success) {
    172 180   resultLabel.setText("Compiler issues: " + issues.size());
     181 + getTabbedPane().getMainWindow().showLogViewer(LogOptions.forScript(getNode().getName()));
    173 182   } else if (!lintErrs.isEmpty()) {
    174 183   resultLabel.setText("Lint issues: " + lintErrs.size());
    175 184   }
    176 185   return success;
    177 186   } catch (Throwable e) {
    178  - LOG.error("Failed to check code", e);
     187 + scriptLog.error("Failed to check code", e);
    179 188   return true;
    180 189   }
    181 190   }
    skipped 2 lines
    184 193   try {
    185 194   List<LintError> lintErrs = KtLintUtils.INSTANCE.lint(code, fileName);
    186 195   for (LintError error : lintErrs) {
    187  - LOG.warn("Lint issue: {}", error);
     196 + scriptLog.warn("Lint issue: {} ({}:{})(ruleId={})",
     197 + error.getDetail(), error.getLine(), error.getCol(), error.getRuleId());
    188 198   }
    189 199   return lintErrs;
    190 200   } catch (Throwable e) { // can throw initialization error
    191  - LOG.warn("KtLint failed", e);
     201 + scriptLog.warn("KtLint failed", e);
    192 202   return Collections.emptyList();
    193 203   }
    194 204   }
    skipped 10 lines
    205 215   errorService.clearErrors();
    206 216   }
    207 217   } catch (Throwable e) { // can throw initialization error
    208  - LOG.error("Failed to reformat code", e);
     218 + scriptLog.error("Failed to reformat code", e);
    209 219   }
    210 220   }
    211 221   
    skipped 27 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/settings/JadxSettings.java
    skipped 104 lines
    105 105   */
    106 106   private int treeWidth = 130;
    107 107   
     108 + private boolean dockLogViewer = true;
     109 + 
    108 110   private int settingsVersion = 0;
    109 111   
    110 112   @JadxSettingsAdapter.GsonExclude
    skipped 569 lines
    680 682   
    681 683   public void setJumpOnDoubleClick(boolean jumpOnDoubleClick) {
    682 684   this.jumpOnDoubleClick = jumpOnDoubleClick;
     685 + }
     686 + 
     687 + public boolean isDockLogViewer() {
     688 + return dockLogViewer;
     689 + }
     690 + 
     691 + public void setDockLogViewer(boolean dockLogViewer) {
     692 + this.dockLogViewer = dockLogViewer;
     693 + partialSync(settings -> this.dockLogViewer = dockLogViewer);
    683 694   }
    684 695   
    685 696   private void upgradeSettings(int fromVersion) {
    skipped 106 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
    skipped 32 lines
    33 33  import java.util.ArrayList;
    34 34  import java.util.Arrays;
    35 35  import java.util.Collections;
    36  -import java.util.HashSet;
    37 36  import java.util.List;
    38 37  import java.util.Locale;
    39 38  import java.util.Objects;
    40  -import java.util.Set;
    41 39  import java.util.Timer;
    42 40  import java.util.TimerTask;
    43 41  import java.util.concurrent.atomic.AtomicReference;
    44  -import java.util.stream.Collectors;
    45 42  import java.util.stream.Stream;
    46 43   
    47 44  import javax.swing.AbstractAction;
    skipped 5 lines
    53 50  import javax.swing.JFrame;
    54 51  import javax.swing.JMenu;
    55 52  import javax.swing.JMenuBar;
    56  -import javax.swing.JMenuItem;
    57 53  import javax.swing.JOptionPane;
    58 54  import javax.swing.JPanel;
    59 55  import javax.swing.JPopupMenu;
    skipped 5 lines
    65 61  import javax.swing.SwingUtilities;
    66 62  import javax.swing.ToolTipManager;
    67 63  import javax.swing.WindowConstants;
    68  -import javax.swing.event.MenuEvent;
    69  -import javax.swing.event.MenuListener;
    70 64  import javax.swing.event.TreeExpansionEvent;
    71 65  import javax.swing.event.TreeWillExpandListener;
    72 66  import javax.swing.tree.DefaultMutableTreeNode;
    skipped 33 lines
    106 100  import jadx.gui.jobs.ExportTask;
    107 101  import jadx.gui.jobs.ProcessResult;
    108 102  import jadx.gui.jobs.TaskStatus;
     103 +import jadx.gui.logs.LogCollector;
     104 +import jadx.gui.logs.LogOptions;
     105 +import jadx.gui.logs.LogPanel;
    109 106  import jadx.gui.plugins.quark.QuarkDialog;
    110 107  import jadx.gui.settings.JadxProject;
    111 108  import jadx.gui.settings.JadxSettings;
    skipped 19 lines
    131 128  import jadx.gui.ui.panel.IssuesPanel;
    132 129  import jadx.gui.ui.panel.JDebuggerPanel;
    133 130  import jadx.gui.ui.panel.ProgressPanel;
     131 +import jadx.gui.ui.popupmenu.RecentProjectsMenuListener;
    134 132  import jadx.gui.ui.treenodes.StartPageNode;
    135 133  import jadx.gui.ui.treenodes.SummaryNode;
    136 134  import jadx.gui.update.JadxUpdate;
    skipped 9 lines
    146 144  import jadx.gui.utils.SystemInfo;
    147 145  import jadx.gui.utils.UiUtils;
    148 146  import jadx.gui.utils.fileswatcher.LiveReloadWorker;
    149  -import jadx.gui.utils.logs.LogCollector;
    150 147  import jadx.gui.utils.ui.ActionHandler;
    151 148  import jadx.gui.utils.ui.NodeLabel;
    152 149  import jadx.plugins.mappings.save.MappingExporter;
    skipped 44 lines
    197 194   private MappingFormat currentMappingFormat;
    198 195   private boolean renamesChanged = false;
    199 196   
    200  - private JPanel mainPanel;
    201  - private JSplitPane splitPane;
     197 + private transient JPanel mainPanel;
     198 + private transient JSplitPane treeSplitPane;
     199 + private transient JSplitPane rightSplitPane;
     200 + private transient JSplitPane bottomSplitPane;
    202 201   
    203 202   private JTree tree;
    204 203   private DefaultTreeModel treeModel;
    skipped 16 lines
    221 220   private transient ProgressPanel progressPane;
    222 221   private transient Theme editorTheme;
    223 222   
    224  - private JDebuggerPanel debuggerPanel;
    225  - private JSplitPane verticalSplitter;
     223 + private transient IssuesPanel issuesPanel;
     224 + private transient @Nullable LogPanel logPanel;
     225 + private transient @Nullable JDebuggerPanel debuggerPanel;
    226 226   
    227 227   private final List<ILoadListener> loadListeners = new ArrayList<>();
    228 228   private boolean loaded;
    skipped 9 lines
    238 238   
    239 239   resetCache();
    240 240   FontUtils.registerBundledFonts();
     241 + setEditorTheme(settings.getEditorThemePath());
    241 242   initUI();
    242 243   this.backgroundExecutor = new BackgroundExecutor(settings, progressPane);
    243 244   initMenuAndToolbar();
    skipped 8 lines
    252 253   public void init() {
    253 254   pack();
    254 255   setLocationAndPosition();
    255  - splitPane.setDividerLocation(settings.getTreeWidth());
     256 + treeSplitPane.setDividerLocation(settings.getTreeWidth());
    256 257   heapUsageBar.setVisible(settings.isShowHeapUsageBar());
    257 258   setVisible(true);
    258 259   setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    skipped 375 lines
    634 635   if (!wrapper.getClasses().isEmpty()) {
    635 636   return;
    636 637   }
    637  - int errors = LogCollector.getInstance().getErrors();
     638 + int errors = issuesPanel.getErrorsCount();
    638 639   if (errors > 0) {
    639 640   int result = JOptionPane.showConfirmDialog(this,
    640 641   NLS.str("message.load_errors", errors),
    skipped 1 lines
    642 643   JOptionPane.OK_CANCEL_OPTION,
    643 644   JOptionPane.ERROR_MESSAGE);
    644 645   if (result == JOptionPane.OK_OPTION) {
    645  - LogViewerDialog.openWithLevel(this, Level.ERROR);
     646 + showLogViewer(LogOptions.allWithLevel(Level.ERROR));
    646 647   }
    647 648   } else {
    648 649   UiUtils.showMessageBox(this, NLS.str("message.no_classes"));
    skipped 486 lines
    1135 1136   exportAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_E, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
    1136 1137   
    1137 1138   JMenu recentProjects = new JMenu(NLS.str("menu.recent_projects"));
    1138  - recentProjects.addMenuListener(new RecentProjectsMenuListener(recentProjects));
     1139 + recentProjects.addMenuListener(new RecentProjectsMenuListener(this, recentProjects));
    1139 1140   
    1140 1141   Action prefsAction = new AbstractAction(NLS.str("menu.preferences"), ICON_PREF) {
    1141 1142   @Override
    skipped 31 lines
    1173 1174   this.syncWithEditor();
    1174 1175   }
    1175 1176   });
     1177 + 
     1178 + JCheckBoxMenuItem dockLog = new JCheckBoxMenuItem(NLS.str("menu.dock_log"));
     1179 + dockLog.setState(settings.isDockLogViewer());
     1180 + dockLog.addActionListener(event -> settings.setDockLogViewer(!settings.isDockLogViewer()));
    1176 1181   
    1177 1182   Action syncAction = new AbstractAction(NLS.str("menu.sync"), ICON_SYNC) {
    1178 1183   @Override
    skipped 62 lines
    1241 1246   deobfMenuItem = new JCheckBoxMenuItem(deobfAction);
    1242 1247   deobfMenuItem.setState(settings.isDeobfuscationOn());
    1243 1248   
    1244  - Action logAction = new AbstractAction(NLS.str("menu.log"), ICON_LOG) {
    1245  - @Override
    1246  - public void actionPerformed(ActionEvent e) {
    1247  - LogViewerDialog.open(MainWindow.this);
    1248  - }
    1249  - };
    1250  - logAction.putValue(Action.SHORT_DESCRIPTION, NLS.str("menu.log"));
    1251  - logAction.putValue(Action.ACCELERATOR_KEY, getKeyStroke(KeyEvent.VK_L,
    1252  - UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
     1249 + ActionHandler showLog = new ActionHandler(ev -> showLogViewer(LogOptions.current()));
     1250 + showLog.setNameAndDesc(NLS.str("menu.log"));
     1251 + showLog.setIcon(ICON_LOG);
     1252 + showLog.setKeyBinding(getKeyStroke(KeyEvent.VK_L, UiUtils.ctrlButton() | KeyEvent.SHIFT_DOWN_MASK));
    1253 1253   
    1254 1254   Action aboutAction = new AbstractAction(NLS.str("menu.about"), ICON_INFO) {
    1255 1255   @Override
    skipped 70 lines
    1326 1326   view.add(syncAction);
    1327 1327   view.add(heapUsageBarMenuItem);
    1328 1328   view.add(alwaysSelectOpened);
     1329 + view.add(dockLog);
    1329 1330   
    1330 1331   JMenu nav = new JMenu(NLS.str("menu.navigation"));
    1331 1332   nav.setMnemonic(KeyEvent.VK_N);
    skipped 16 lines
    1348 1349   
    1349 1350   JMenu help = new JMenu(NLS.str("menu.help"));
    1350 1351   help.setMnemonic(KeyEvent.VK_H);
    1351  - help.add(logAction);
     1352 + help.add(showLog);
    1352 1353   if (Jadx.isDevVersion()) {
    1353 1354   help.add(new AbstractAction("Show sample error report") {
    1354 1355   @Override
    skipped 47 lines
    1402 1403   toolbar.add(quarkAction);
    1403 1404   toolbar.add(openDeviceAction);
    1404 1405   toolbar.addSeparator();
    1405  - toolbar.add(logAction);
     1406 + toolbar.add(showLog);
    1406 1407   toolbar.addSeparator();
    1407 1408   toolbar.add(prefsAction);
    1408 1409   toolbar.addSeparator();
    skipped 22 lines
    1431 1432   private void initUI() {
    1432 1433   setMinimumSize(new Dimension(200, 150));
    1433 1434   mainPanel = new JPanel(new BorderLayout());
    1434  - splitPane = new JSplitPane();
    1435  - splitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
    1436  - mainPanel.add(splitPane);
     1435 + treeSplitPane = new JSplitPane();
     1436 + treeSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
     1437 + mainPanel.add(treeSplitPane);
    1437 1438   
    1438 1439   DefaultMutableTreeNode treeRootNode = new DefaultMutableTreeNode(NLS.str("msg.open_file"));
    1439 1440   treeModel = new DefaultTreeModel(treeRootNode);
    skipped 74 lines
    1514 1515   });
    1515 1516   
    1516 1517   progressPane = new ProgressPanel(this, true);
    1517  - IssuesPanel issuesPanel = new IssuesPanel(this);
     1518 + issuesPanel = new IssuesPanel(this);
    1518 1519   
    1519 1520   JPanel leftPane = new JPanel(new BorderLayout());
    1520 1521   JScrollPane treeScrollPane = new JScrollPane(tree);
    skipped 5 lines
    1526 1527   
    1527 1528   leftPane.add(treeScrollPane, BorderLayout.CENTER);
    1528 1529   leftPane.add(bottomPane, BorderLayout.PAGE_END);
    1529  - splitPane.setLeftComponent(leftPane);
     1530 + treeSplitPane.setLeftComponent(leftPane);
    1530 1531   
    1531 1532   tabbedPane = new TabbedPane(this);
    1532 1533   tabbedPane.setMinimumSize(new Dimension(150, 150));
    1533  - splitPane.setRightComponent(tabbedPane);
     1534 + 
     1535 + rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     1536 + rightSplitPane.setTopComponent(tabbedPane);
     1537 + rightSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
     1538 + 
     1539 + treeSplitPane.setRightComponent(rightSplitPane);
    1534 1540   
    1535 1541   new DropTarget(this, DnDConstants.ACTION_COPY, new MainDropTarget(this));
    1536 1542   
    1537 1543   heapUsageBar = new HeapUsageBar();
    1538 1544   mainPanel.add(heapUsageBar, BorderLayout.SOUTH);
    1539 1545   
    1540  - verticalSplitter = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    1541  - verticalSplitter.setTopComponent(splitPane);
    1542  - verticalSplitter.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
     1546 + bottomSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
     1547 + bottomSplitPane.setTopComponent(treeSplitPane);
     1548 + bottomSplitPane.setResizeWeight(SPLIT_PANE_RESIZE_WEIGHT);
    1543 1549   
    1544  - mainPanel.add(verticalSplitter, BorderLayout.CENTER);
     1550 + mainPanel.add(bottomSplitPane, BorderLayout.CENTER);
    1545 1551   setContentPane(mainPanel);
    1546 1552   setTitle(DEFAULT_TITLE);
    1547 1553   }
    skipped 123 lines
    1671 1677   tree.setRowHeight(-1);
    1672 1678   
    1673 1679   tabbedPane.loadSettings();
     1680 + if (logPanel != null) {
     1681 + logPanel.loadSettings();
     1682 + }
    1674 1683   }
    1675 1684   
    1676 1685   private void closeWindow() {
    skipped 1 lines
    1678 1687   if (!ensureProjectIsSaved()) {
    1679 1688   return;
    1680 1689   }
    1681  - settings.setTreeWidth(splitPane.getDividerLocation());
     1690 + settings.setTreeWidth(treeSplitPane.getDividerLocation());
    1682 1691   settings.saveWindowPos(this);
    1683 1692   settings.setMainWindowExtendedState(getExtendedState());
    1684 1693   if (debuggerPanel != null) {
    skipped 27 lines
    1712 1721   }
    1713 1722   
    1714 1723   private void saveSplittersInfo() {
    1715  - settings.setMainWindowVerticalSplitterLoc(verticalSplitter.getDividerLocation());
     1724 + settings.setMainWindowVerticalSplitterLoc(bottomSplitPane.getDividerLocation());
    1716 1725   settings.setDebuggerStackFrameSplitterLoc(debuggerPanel.getLeftSplitterLocation());
    1717 1726   settings.setDebuggerVarTreeSplitterLoc(debuggerPanel.getRightSplitterLocation());
    1718 1727   }
    skipped 61 lines
    1780 1789   if (debuggerPanel == null) {
    1781 1790   debuggerPanel = new JDebuggerPanel(this);
    1782 1791   debuggerPanel.loadSettings();
    1783  - verticalSplitter.setBottomComponent(debuggerPanel);
     1792 + bottomSplitPane.setBottomComponent(debuggerPanel);
    1784 1793   int loc = settings.getMainWindowVerticalSplitterLoc();
    1785 1794   if (loc == 0) {
    1786 1795   loc = 300;
    1787 1796   }
    1788  - verticalSplitter.setDividerLocation(loc);
     1797 + bottomSplitPane.setDividerLocation(loc);
    1789 1798   }
    1790 1799   }
    1791 1800   
    1792  - private class RecentProjectsMenuListener implements MenuListener {
    1793  - private final JMenu menu;
    1794  - 
    1795  - public RecentProjectsMenuListener(JMenu menu) {
    1796  - this.menu = menu;
     1801 + public void showLogViewer(LogOptions logOptions) {
     1802 + if (settings.isDockLogViewer()) {
     1803 + showDockedLog(logOptions);
     1804 + } else {
     1805 + LogViewerDialog.open(this, logOptions);
    1797 1806   }
     1807 + }
    1798 1808   
    1799  - @Override
    1800  - public void menuSelected(MenuEvent menuEvent) {
    1801  - Set<Path> current = new HashSet<>(project.getFilePaths());
    1802  - List<JMenuItem> items = settings.getRecentProjects()
    1803  - .stream()
    1804  - .filter(path -> !current.contains(path))
    1805  - .map(path -> {
    1806  - JMenuItem menuItem = new JMenuItem(path.toAbsolutePath().toString());
    1807  - menuItem.addActionListener(e -> open(Collections.singletonList(path)));
    1808  - return menuItem;
    1809  - }).collect(Collectors.toList());
    1810  - 
    1811  - menu.removeAll();
    1812  - if (items.isEmpty()) {
    1813  - menu.add(new JMenuItem(NLS.str("menu.no_recent_projects")));
    1814  - } else {
    1815  - items.forEach(menu::add);
    1816  - }
     1809 + private void showDockedLog(LogOptions logOptions) {
     1810 + if (logPanel != null) {
     1811 + logPanel.applyLogOptions(logOptions);
     1812 + return;
    1817 1813   }
     1814 + Runnable undock = () -> {
     1815 + hideDockedLog();
     1816 + settings.setDockLogViewer(false);
     1817 + LogViewerDialog.open(this, logOptions);
     1818 + };
     1819 + logPanel = new LogPanel(this, logOptions, undock, this::hideDockedLog);
     1820 + rightSplitPane.setBottomComponent(logPanel);
     1821 + }
    1818 1822   
    1819  - @Override
    1820  - public void menuDeselected(MenuEvent e) {
     1823 + private void hideDockedLog() {
     1824 + if (logPanel == null) {
     1825 + return;
    1821 1826   }
    1822  - 
    1823  - @Override
    1824  - public void menuCanceled(MenuEvent e) {
    1825  - }
     1827 + logPanel.dispose();
     1828 + logPanel = null;
     1829 + rightSplitPane.setBottomComponent(null);
    1826 1830   }
    1827 1831   
    1828 1832   public JMenu getPluginsMenu() {
    skipped 4 lines
  • ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/dialog/LogViewerDialog.java
    skipped 4 lines
    5 5  import java.awt.event.WindowAdapter;
    6 6  import java.awt.event.WindowEvent;
    7 7   
    8  -import javax.swing.BorderFactory;
    9  -import javax.swing.JButton;
    10  -import javax.swing.JComboBox;
    11 8  import javax.swing.JFrame;
    12  -import javax.swing.JLabel;
    13  -import javax.swing.JPanel;
    14  -import javax.swing.JScrollPane;
    15  -import javax.swing.SwingUtilities;
    16 9   
    17  -import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
    18  - 
    19  -import ch.qos.logback.classic.Level;
    20  - 
     10 +import jadx.gui.logs.LogOptions;
     11 +import jadx.gui.logs.LogPanel;
    21 12  import jadx.gui.settings.JadxSettings;
    22 13  import jadx.gui.ui.MainWindow;
    23  -import jadx.gui.ui.codearea.AbstractCodeArea;
    24 14  import jadx.gui.utils.NLS;
    25 15  import jadx.gui.utils.UiUtils;
    26  -import jadx.gui.utils.logs.ILogListener;
    27  -import jadx.gui.utils.logs.LogCollector;
    28 16   
    29 17  public class LogViewerDialog extends JFrame {
    30 18   private static final long serialVersionUID = -2188700277429054641L;
    31  - private static final Level[] LEVEL_ITEMS = { Level.DEBUG, Level.INFO, Level.WARN, Level.ERROR };
    32 19   
    33  - private static Level level = Level.WARN;
     20 + private static LogViewerDialog openLogDialog;
    34 21   
    35 22   private final transient JadxSettings settings;
    36  - private transient RSyntaxTextArea textPane;
    37  - private JComboBox<Level> levelCb;
     23 + private final transient LogPanel logPanel;
    38 24   
    39  - private static LogViewerDialog openLogDialog;
    40  - 
    41  - public static void open(MainWindow mainWindow) {
    42  - openWithLevel(mainWindow, level);
    43  - }
    44  - 
    45  - public static void openWithLevel(MainWindow mainWindow, Level newLevel) {
    46  - level = newLevel;
    47  - if (openLogDialog == null) {
    48  - LogViewerDialog newLogDialog = new LogViewerDialog(mainWindow);
    49  - newLogDialog.setVisible(true);
    50  - openLogDialog = newLogDialog;
     25 + public static void open(MainWindow mainWindow, LogOptions logOptions) {
     26 + LogViewerDialog logDialog;
     27 + if (openLogDialog != null) {
     28 + logDialog = openLogDialog;
    51 29   } else {
    52  - LogViewerDialog logDialog = openLogDialog;
    53  - logDialog.levelCb.setSelectedItem(level);
    54  - logDialog.setVisible(true);
    55  - logDialog.toFront();
     30 + logDialog = new LogViewerDialog(mainWindow, logOptions);
     31 + openLogDialog = logDialog;
    56 32   }
     33 + logDialog.setVisible(true);
     34 + logDialog.toFront();
    57 35   }
    58 36   
    59  - private LogViewerDialog(MainWindow mainWindow) {
    60  - this.settings = mainWindow.getSettings();
    61  - initUI(mainWindow);
    62  - registerLogListener();
    63  - settings.loadWindowPos(this);
    64  - addWindowListener(new WindowAdapter() {
    65  - @Override
    66  - public void windowClosing(WindowEvent e) {
    67  - openLogDialog = null;
    68  - }
    69  - });
    70  - }
    71  - 
    72  - public final void initUI(MainWindow mainWindow) {
     37 + private LogViewerDialog(MainWindow mainWindow, LogOptions logOptions) {
     38 + settings = mainWindow.getSettings();
    73 39   UiUtils.setWindowIcons(this);
    74 40   
    75  - textPane = AbstractCodeArea.getDefaultArea(mainWindow);
    76  - textPane.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
    77  - 
    78  - JPanel controlPane = new JPanel();
    79  - controlPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    80  - levelCb = new JComboBox<>(LEVEL_ITEMS);
    81  - levelCb.setSelectedItem(level);
    82  - levelCb.addActionListener(e -> {
    83  - int i = levelCb.getSelectedIndex();
    84  - level = LEVEL_ITEMS[i];
    85  - registerLogListener();
    86  - });
    87  - JLabel levelLabel = new JLabel(NLS.str("log_viewer.log_level"));
    88  - levelLabel.setLabelFor(levelCb);
    89  - controlPane.add(levelLabel);
    90  - controlPane.add(levelCb);
    91  - 
    92  - JScrollPane scrollPane = new JScrollPane(textPane);
    93  - 
    94  - JButton close = new JButton(NLS.str("tabs.close"));
    95  - close.addActionListener(event -> close());
    96  - close.setAlignmentX(0.5f);
    97  - 
     41 + Runnable dock = () -> {
     42 + mainWindow.getSettings().setDockLogViewer(true);
     43 + dispose();
     44 + mainWindow.showLogViewer(LogOptions.current());
     45 + };
     46 + logPanel = new LogPanel(mainWindow, logOptions, dock, this::dispose);
    98 47   Container contentPane = getContentPane();
    99  - contentPane.add(controlPane, BorderLayout.PAGE_START);
    100  - contentPane.add(scrollPane, BorderLayout.CENTER);
    101  - contentPane.add(close, BorderLayout.PAGE_END);
     48 + contentPane.add(logPanel, BorderLayout.CENTER);
    102 49   
    103 50   setTitle(NLS.str("log_viewer.title"));
    104 51   pack();
    105 52   setSize(800, 600);
    106 53   setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    107 54   setLocationRelativeTo(null);
    108  - }
    109  - 
    110  - private void registerLogListener() {
    111  - LogCollector logCollector = LogCollector.getInstance();
    112  - logCollector.resetListener();
    113  - textPane.setText("");
    114  - logCollector.registerListener(new ILogListener() {
     55 + settings.loadWindowPos(this);
     56 + addWindowListener(new WindowAdapter() {
    115 57   @Override
    116  - public Level getFilterLevel() {
    117  - return level;
    118  - }
    119  - 
    120  - @Override
    121  - public void onAppend(final String logStr) {
    122  - SwingUtilities.invokeLater(() -> textPane.append(logStr));
     58 + public void windowClosing(WindowEvent e) {
     59 + openLogDialog = null;
    123 60   }
    124 61   });
    125 62   }
    126 63   
    127  - private void close() {
    128  - dispose();
    129  - }
    130  - 
    131 64   @Override
    132 65   public void dispose() {
    133  - LogCollector.getInstance().resetListener();
     66 + logPanel.dispose();
    134 67   settings.saveWindowPos(this);
    135 68   super.dispose();
    136 69   }
    skipped 2 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/panel/IssuesPanel.java
    skipped 8 lines
    9 9  import javax.swing.ImageIcon;
    10 10  import javax.swing.JLabel;
    11 11  import javax.swing.JPanel;
    12  -import javax.swing.SwingUtilities;
    13 12   
    14 13  import ch.qos.logback.classic.Level;
    15 14   
     15 +import jadx.gui.logs.IssuesListener;
     16 +import jadx.gui.logs.LogCollector;
     17 +import jadx.gui.logs.LogOptions;
    16 18  import jadx.gui.ui.MainWindow;
    17  -import jadx.gui.ui.dialog.LogViewerDialog;
    18 19  import jadx.gui.utils.NLS;
    19 20  import jadx.gui.utils.UiUtils;
    20  -import jadx.gui.utils.logs.LogCollector;
    21 21   
    22 22  public class IssuesPanel extends JPanel {
    23 23   private static final long serialVersionUID = -7720576036668459218L;
    skipped 2 lines
    26 26   private static final ImageIcon WARN_ICON = UiUtils.openSvgIcon("ui/warning");
    27 27   
    28 28   private final MainWindow mainWindow;
     29 + private final IssuesListener issuesListener;
    29 30   private JLabel errorLabel;
    30 31   private JLabel warnLabel;
    31 32   
    32 33   public IssuesPanel(MainWindow mainWindow) {
    33 34   this.mainWindow = mainWindow;
    34 35   initUI();
    35  - LogCollector.getInstance().registerIssueListener((error, warnings) -> {
    36  - SwingUtilities.invokeLater(() -> onUpdate(error, warnings));
    37  - });
     36 + this.issuesListener = new IssuesListener(this);
     37 + LogCollector.getInstance().registerListener(issuesListener);
     38 + }
     39 + 
     40 + public int getErrorsCount() {
     41 + return issuesListener.getErrors();
    38 42   }
    39 43   
    40 44   private void initUI() {
    skipped 8 lines
    49 53   errorLabel.addMouseListener(new MouseAdapter() {
    50 54   @Override
    51 55   public void mouseClicked(MouseEvent e) {
    52  - LogViewerDialog.openWithLevel(mainWindow, Level.ERROR);
     56 + mainWindow.showLogViewer(LogOptions.allWithLevel(Level.ERROR));
    53 57   }
    54 58   });
    55 59   warnLabel.addMouseListener(new MouseAdapter() {
    56 60   @Override
    57 61   public void mouseClicked(MouseEvent e) {
    58  - LogViewerDialog.openWithLevel(mainWindow, Level.WARN);
     62 + mainWindow.showLogViewer(LogOptions.allWithLevel(Level.WARN));
    59 63   }
    60 64   });
    61 65   
    skipped 7 lines
    69 73   add(warnLabel);
    70 74   }
    71 75   
    72  - private void onUpdate(int error, int warnings) {
     76 + public void onUpdate(int error, int warnings) {
    73 77   if (error == 0 && warnings == 0) {
    74 78   setVisible(false);
    75 79   return;
    skipped 9 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/ui/popupmenu/RecentProjectsMenuListener.java
     1 +package jadx.gui.ui.popupmenu;
     2 + 
     3 +import java.nio.file.Path;
     4 +import java.util.Collections;
     5 +import java.util.HashSet;
     6 +import java.util.List;
     7 +import java.util.Set;
     8 +import java.util.stream.Collectors;
     9 + 
     10 +import javax.swing.JMenu;
     11 +import javax.swing.JMenuItem;
     12 +import javax.swing.event.MenuEvent;
     13 +import javax.swing.event.MenuListener;
     14 + 
     15 +import jadx.gui.ui.MainWindow;
     16 +import jadx.gui.utils.NLS;
     17 + 
     18 +public class RecentProjectsMenuListener implements MenuListener {
     19 + private final MainWindow mainWindow;
     20 + private final JMenu menu;
     21 + 
     22 + public RecentProjectsMenuListener(MainWindow mainWindow, JMenu menu) {
     23 + this.mainWindow = mainWindow;
     24 + this.menu = menu;
     25 + }
     26 + 
     27 + @Override
     28 + public void menuSelected(MenuEvent menuEvent) {
     29 + Set<Path> current = new HashSet<>(mainWindow.getProject().getFilePaths());
     30 + List<JMenuItem> items = mainWindow.getSettings().getRecentProjects()
     31 + .stream()
     32 + .filter(path -> !current.contains(path))
     33 + .map(path -> {
     34 + JMenuItem menuItem = new JMenuItem(path.toAbsolutePath().toString());
     35 + menuItem.addActionListener(e -> mainWindow.open(Collections.singletonList(path)));
     36 + return menuItem;
     37 + }).collect(Collectors.toList());
     38 + 
     39 + menu.removeAll();
     40 + if (items.isEmpty()) {
     41 + menu.add(new JMenuItem(NLS.str("menu.no_recent_projects")));
     42 + } else {
     43 + items.forEach(menu::add);
     44 + }
     45 + }
     46 + 
     47 + @Override
     48 + public void menuDeselected(MenuEvent e) {
     49 + }
     50 + 
     51 + @Override
     52 + public void menuCanceled(MenuEvent e) {
     53 + }
     54 +}
     55 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/logs/ILogIssuesListener.java
    1  -package jadx.gui.utils.logs;
    2  - 
    3  -public interface ILogIssuesListener {
    4  - void onChange(int error, int warnings);
    5  -}
    6  - 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/logs/ILogListener.java
    1  -package jadx.gui.utils.logs;
    2  - 
    3  -import ch.qos.logback.classic.Level;
    4  - 
    5  -public interface ILogListener {
    6  - Level getFilterLevel();
    7  - 
    8  - void onAppend(String logStr);
    9  -}
    10  - 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/logs/LogCollector.java
    1  -package jadx.gui.utils.logs;
    2  - 
    3  -import java.util.Queue;
    4  - 
    5  -import org.jetbrains.annotations.NotNull;
    6  -import org.jetbrains.annotations.Nullable;
    7  -import org.slf4j.LoggerFactory;
    8  - 
    9  -import ch.qos.logback.classic.Level;
    10  -import ch.qos.logback.classic.Logger;
    11  -import ch.qos.logback.classic.LoggerContext;
    12  -import ch.qos.logback.classic.PatternLayout;
    13  -import ch.qos.logback.classic.spi.ILoggingEvent;
    14  -import ch.qos.logback.core.AppenderBase;
    15  -import ch.qos.logback.core.Layout;
    16  - 
    17  -public class LogCollector extends AppenderBase<ILoggingEvent> {
    18  - public static final int BUFFER_SIZE = 5000;
    19  - 
    20  - private static final LogCollector INSTANCE = new LogCollector();
    21  - 
    22  - public static LogCollector getInstance() {
    23  - return INSTANCE;
    24  - }
    25  - 
    26  - public static void register() {
    27  - Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    28  - LoggerContext loggerContext = rootLogger.getLoggerContext();
    29  - 
    30  - PatternLayout layout = new PatternLayout();
    31  - layout.setContext(loggerContext);
    32  - layout.setPattern("%-5level: %msg%n");
    33  - layout.start();
    34  - 
    35  - INSTANCE.setContext(loggerContext);
    36  - INSTANCE.setLayout(layout);
    37  - INSTANCE.start();
    38  - 
    39  - rootLogger.addAppender(INSTANCE);
    40  - }
    41  - 
    42  - private Layout<ILoggingEvent> layout;
    43  - 
    44  - @Nullable
    45  - private ILogListener listener;
    46  - @Nullable
    47  - private ILogIssuesListener issuesListener;
    48  - private int errors = 0;
    49  - private int warnings = 0;
    50  - 
    51  - private final Queue<LogEvent> buffer = new LimitedQueue<>(BUFFER_SIZE);
    52  - 
    53  - public LogCollector() {
    54  - setName("LogCollector");
    55  - }
    56  - 
    57  - @Override
    58  - protected void append(ILoggingEvent event) {
    59  - synchronized (this) {
    60  - Level level = event.getLevel();
    61  - String msg = layout.doLayout(event);
    62  - store(level, msg);
    63  - if (listener != null && level.isGreaterOrEqual(listener.getFilterLevel())) {
    64  - listener.onAppend(msg);
    65  - }
    66  - if (level == Level.ERROR) {
    67  - errors++;
    68  - issuesUpdated();
    69  - } else if (level == Level.WARN) {
    70  - warnings++;
    71  - issuesUpdated();
    72  - }
    73  - }
    74  - }
    75  - 
    76  - private void issuesUpdated() {
    77  - if (issuesListener != null) {
    78  - issuesListener.onChange(errors, warnings);
    79  - }
    80  - }
    81  - 
    82  - private void store(Level level, String msg) {
    83  - buffer.offer(new LogEvent(level, msg));
    84  - }
    85  - 
    86  - public void setLayout(Layout<ILoggingEvent> layout) {
    87  - this.layout = layout;
    88  - }
    89  - 
    90  - public void registerListener(@NotNull ILogListener listener) {
    91  - this.listener = listener;
    92  - synchronized (this) {
    93  - listener.onAppend(init(listener.getFilterLevel()));
    94  - }
    95  - }
    96  - 
    97  - public void registerIssueListener(@NotNull ILogIssuesListener listener) {
    98  - this.issuesListener = listener;
    99  - synchronized (this) {
    100  - listener.onChange(errors, warnings);
    101  - }
    102  - }
    103  - 
    104  - public void resetListener() {
    105  - this.listener = null;
    106  - }
    107  - 
    108  - public void reset() {
    109  - buffer.clear();
    110  - errors = 0;
    111  - warnings = 0;
    112  - issuesUpdated();
    113  - }
    114  - 
    115  - public int getErrors() {
    116  - return errors;
    117  - }
    118  - 
    119  - public int getWarnings() {
    120  - return warnings;
    121  - }
    122  - 
    123  - private String init(Level filterLevel) {
    124  - StringBuilder sb = new StringBuilder();
    125  - for (LogEvent event : buffer) {
    126  - if (event.getLevel().isGreaterOrEqual(filterLevel)) {
    127  - sb.append(event.getMsg());
    128  - }
    129  - }
    130  - return sb.toString();
    131  - }
    132  -}
    133  - 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/logs/LogEvent.java
    1  -package jadx.gui.utils.logs;
    2  - 
    3  -import ch.qos.logback.classic.Level;
    4  - 
    5  -final class LogEvent {
    6  - private final Level level;
    7  - private final String msg;
    8  - 
    9  - LogEvent(Level level, String msg) {
    10  - this.level = level;
    11  - this.msg = msg;
    12  - }
    13  - 
    14  - public Level getLevel() {
    15  - return level;
    16  - }
    17  - 
    18  - public String getMsg() {
    19  - return msg;
    20  - }
    21  - 
    22  - @Override
    23  - public String toString() {
    24  - return level + ": " + msg;
    25  - }
    26  -}
    27  - 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/java/jadx/gui/utils/rx/DebounceUpdate.java
     1 +package jadx.gui.utils.rx;
     2 + 
     3 +import java.util.concurrent.TimeUnit;
     4 + 
     5 +import io.reactivex.BackpressureStrategy;
     6 +import io.reactivex.Flowable;
     7 +import io.reactivex.FlowableEmitter;
     8 +import io.reactivex.FlowableOnSubscribe;
     9 +import io.reactivex.disposables.Disposable;
     10 + 
     11 +public class DebounceUpdate {
     12 + 
     13 + private FlowableEmitter<Boolean> emitter;
     14 + private final Disposable disposable;
     15 + 
     16 + public DebounceUpdate(int timeMs, Runnable action) {
     17 + FlowableOnSubscribe<Boolean> source = emitter -> this.emitter = emitter;
     18 + disposable = Flowable.create(source, BackpressureStrategy.LATEST)
     19 + .debounce(timeMs, TimeUnit.MILLISECONDS)
     20 + .subscribe(v -> action.run());
     21 + }
     22 + 
     23 + public void requestUpdate() {
     24 + emitter.onNext(Boolean.TRUE);
     25 + }
     26 + 
     27 + public void dispose() {
     28 + disposable.dispose();
     29 + }
     30 +}
     31 + 
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_de_DE.properties
    skipped 8 lines
    9 9  menu.flatten=Codepaket erweitern
    10 10  menu.heapUsageBar=Speicherverbrauchsleiste anzeigen
    11 11  menu.alwaysSelectOpened=Immer geöffnete Datei/Klasse auswählen
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=Navigation
    13 14  menu.text_search=Textsuche
    14 15  menu.class_search=Klassen-Suche
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=Log-Anzeige
    141 142  log_viewer.log_level=Log-Level:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=Über JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_en_US.properties
    skipped 8 lines
    9 9  menu.flatten=Show flatten packages
    10 10  menu.heapUsageBar=Show memory usage bar
    11 11  menu.alwaysSelectOpened=Always Select Opened File/Class
     12 +menu.dock_log=Dock log viewer
    12 13  menu.navigation=Navigation
    13 14  menu.text_search=Text search
    14 15  menu.class_search=Class search
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=Log Viewer
    141 142  log_viewer.log_level=Log level:
     143 +log_viewer.mode=Mode:
     144 +log_viewer.modes=All|All scripts|Current script
     145 +log_viewer.hide=Hide
     146 +log_viewer.dock=Dock
     147 +log_viewer.undock=Undock
     148 +log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=About JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_es_ES.properties
    skipped 8 lines
    9 9  menu.flatten=Mostrar paquetes en vista plana
    10 10  #menu.heapUsageBar=
    11 11  #menu.alwaysSelectOpened=Always Select Opened File/Class
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=Navegación
    13 14  menu.text_search=Buscar texto
    14 15  menu.class_search=Buscar clase
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=Visor log
    141 142  log_viewer.log_level=Nivel log:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=Sobre JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties
    skipped 8 lines
    9 9  menu.flatten=플랫 패키지 표시
    10 10  menu.heapUsageBar=메모리 사용량 표시
    11 11  menu.alwaysSelectOpened=항상 열린 파일/클래스 선택
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=네비게이션
    13 14  menu.text_search=텍스트 검색
    14 15  menu.class_search=클래스 검색
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=로그 뷰어
    141 142  log_viewer.log_level=로그 레벨:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=JADX 정보
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_pt_BR.properties
    skipped 8 lines
    9 9  menu.flatten=Mostrar pacotes achatados
    10 10  menu.heapUsageBar=Mostrar uso de memória
    11 11  menu.alwaysSelectOpened=Sempre selecionar arquivo/classe aberta
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=Navegação
    13 14  menu.text_search=Buscar por texto
    14 15  menu.class_search=Buscar por classe
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=Visualizador de log
    141 142  log_viewer.log_level=Nível do log:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=Sobre o JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties
    skipped 8 lines
    9 9  menu.flatten=展开显示代码包
    10 10  menu.heapUsageBar=显示内存使用栏
    11 11  menu.alwaysSelectOpened=始终选中打开的文件/类
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=导航
    13 14  menu.text_search=文本搜索
    14 15  menu.class_search=类名搜索
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=日志查看器
    141 142  log_viewer.log_level=日志等级:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=关于 JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■ ■ ■
    jadx-gui/src/main/resources/i18n/Messages_zh_TW.properties
    skipped 8 lines
    9 9  menu.flatten=展開顯示套件
    10 10  menu.heapUsageBar=顯示記憶體使用率條
    11 11  menu.alwaysSelectOpened=總是選擇已開啟的檔案/類別
     12 +#menu.dock_log=Dock log viewer
    12 13  menu.navigation=瀏覽
    13 14  menu.text_search=文字搜尋
    14 15  menu.class_search=類別搜尋
    skipped 124 lines
    139 140   
    140 141  log_viewer.title=日誌檢視器
    141 142  log_viewer.log_level=紀錄層級:
     143 +#log_viewer.mode=Mode:
     144 +#log_viewer.modes=All|All scripts|Current script
     145 +#log_viewer.hide=Hide
     146 +#log_viewer.dock=Dock
     147 +#log_viewer.undock=Undock
     148 +#log_viewer.clear=Clear
    142 149   
    143 150  about_dialog.title=關於 JADX
    144 151   
    skipped 218 lines
  • ■ ■ ■ ■
    jadx-plugins/jadx-script/examples/scripts/gui_custom_frida.jadx.kts
    skipped 100 lines
    101 101   return """
    102 102   ${generateClassSnippet(fld.parentClass)}
    103 103   ${fld.name} = ${fld.parentClass.name}.$rawFieldName.value;
    104  - """.trimIndent()
     104 + """.trimIndent()
    105 105  }
    106 106   
    107 107  fun isOverloaded(methodNode: MethodNode): Boolean {
    skipped 14 lines
  • ■ ■ ■ ■ ■
    jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/JadxScriptTemplate.kt
    skipped 27 lines
    28 28  import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
    29 29  import kotlin.script.experimental.jvm.jvm
    30 30   
     31 +const val JADX_SCRIPT_LOG_PREFIX = "JadxScript:"
     32 + 
    31 33  @KotlinScript(
    32 34   fileExtension = "jadx.kts",
    33 35   compilationConfiguration = JadxScriptConfiguration::class
    skipped 2 lines
    36 38   private val scriptData: JadxScriptData
    37 39  ) {
    38 40   val scriptName = scriptData.scriptName
    39  - val log = KotlinLogging.logger("JadxScript:$scriptName")
     41 + val log = KotlinLogging.logger("$JADX_SCRIPT_LOG_PREFIX$scriptName")
    40 42   
    41 43   fun getJadxInstance() = JadxScriptInstance(scriptData, log)
    42 44   
    skipped 42 lines
  • ■ ■ ■ ■
    jadx-plugins/jadx-script/jadx-script-runtime/src/main/kotlin/jadx/plugins/script/runtime/Runtime.kt
    skipped 26 lines
    27 27  ) {
    28 28   val afterLoad: MutableList<() -> Unit> = ArrayList()
    29 29   
    30  - val scriptName get() = scriptFile.name.removeSuffix(".jadx.kts")
     30 + val scriptName = scriptFile.name.removeSuffix(".jadx.kts")
    31 31  }
    32 32   
    33 33  class JadxScriptInstance(
    skipped 36 lines
Please wait...
Page is in error, reload to recover