fix(gui): Quick Tabs Optimization (PR #2242)
* optimize tabs reorder * restructure based on quick tabs architecture * code formatting * log all exceptions from background executor * various improvements --------- Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
@@ -138,9 +138,17 @@ public class BackgroundExecutor {
|
||||
task.onDone(this);
|
||||
// treat UI task operations as part of the task to not mix with others
|
||||
UiUtils.uiRunAndWait(() -> {
|
||||
progressPane.setVisible(false);
|
||||
task.onFinish(this);
|
||||
try {
|
||||
progressPane.setVisible(false);
|
||||
task.onFinish(this);
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Task onFinish failed", e);
|
||||
status = TaskStatus.ERROR;
|
||||
}
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
LOG.error("Task onDone failed", e);
|
||||
status = TaskStatus.ERROR;
|
||||
} finally {
|
||||
taskComplete(id);
|
||||
progressPane.changeVisibility(this, false);
|
||||
|
||||
@@ -23,7 +23,7 @@ import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabBlueprint;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class LogPanel extends JPanel {
|
||||
@@ -141,9 +141,9 @@ public class LogPanel extends JPanel {
|
||||
}
|
||||
|
||||
private @Nullable String getCurrentScriptName() {
|
||||
ContentPanel selectedCodePanel = mainWindow.getTabbedPane().getSelectedContentPanel();
|
||||
if (selectedCodePanel != null) {
|
||||
JNode node = selectedCodePanel.getNode();
|
||||
TabBlueprint selectedTab = mainWindow.getTabsController().getSelectedTab();
|
||||
if (selectedTab != null) {
|
||||
JNode node = selectedTab.getNode();
|
||||
if (node instanceof JInputScript) {
|
||||
return node.getName();
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ public class GuiPluginContext implements JadxGuiContext {
|
||||
return false;
|
||||
}
|
||||
|
||||
commonContext.getMainWindow().getTabbedPane().codeJump(node);
|
||||
commonContext.getMainWindow().getTabsController().codeJump(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ public class QuarkManager {
|
||||
root.replaceCustomNode(quarkNode);
|
||||
root.update();
|
||||
mainWindow.reloadTree();
|
||||
mainWindow.getTabbedPane().showNode(quarkNode);
|
||||
mainWindow.getTabsController().selectTab(quarkNode);
|
||||
} catch (Exception e) {
|
||||
UiUtils.errorMessage(mainWindow, "Failed to load Quark report.");
|
||||
LOG.error("Failed to load Quark report.", e);
|
||||
|
||||
@@ -117,7 +117,7 @@ public class QuarkReportPanel extends ContentPanel {
|
||||
Object node = getNodeUnderMouse(tree, event);
|
||||
if (node instanceof MethodTreeNode) {
|
||||
JMethod method = ((MethodTreeNode) node).getJMethod();
|
||||
tabbedPane.codeJump(method);
|
||||
tabbedPane.getTabsController().codeJump(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public class QuarkReportPanel extends ContentPanel {
|
||||
|
||||
@Override
|
||||
public void loadSettings() {
|
||||
Font settingsFont = getTabbedPane().getMainWindow().getSettings().getFont();
|
||||
Font settingsFont = getMainWindow().getSettings().getFont();
|
||||
this.font = settingsFont.deriveFont(settingsFont.getSize2D() + 1.f);
|
||||
this.boldFont = font.deriveFont(Font.BOLD);
|
||||
header.setFont(font);
|
||||
@@ -279,7 +279,7 @@ public class QuarkReportPanel extends ContentPanel {
|
||||
String[] parts = removeQuotes(descr).split(" ", 3);
|
||||
String cls = Utils.cleanObjectName(parts[0].replace('$', '.'));
|
||||
String mth = parts[1] + parts[2].replace(" ", "");
|
||||
MainWindow mainWindow = getTabbedPane().getMainWindow();
|
||||
MainWindow mainWindow = getMainWindow();
|
||||
JadxWrapper wrapper = mainWindow.getWrapper();
|
||||
JavaClass javaClass = wrapper.searchJavaClassByRawName(cls);
|
||||
if (javaClass == null) {
|
||||
|
||||
@@ -25,8 +25,8 @@ public class ScriptCodeArea extends AbstractCodeArea {
|
||||
setCodeFoldingEnabled(true);
|
||||
setCloseCurlyBraces(true);
|
||||
|
||||
shortcutsController = contentPanel.getTabbedPane().getMainWindow().getShortcutsController();
|
||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||
shortcutsController = contentPanel.getMainWindow().getShortcutsController();
|
||||
JadxSettings settings = contentPanel.getMainWindow().getSettings();
|
||||
autoCompletion = addAutoComplete(settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ public class ScriptContentPanel extends AbstractCodeContentPanel {
|
||||
}
|
||||
|
||||
private void showScriptLog() {
|
||||
getTabbedPane().getMainWindow().showLogViewer(LogOptions.forScript(getNode().getName()));
|
||||
getMainWindow().showLogViewer(LogOptions.forScript(getNode().getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,8 +40,7 @@ public abstract class JNode extends DefaultMutableTreeNode implements Comparable
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
public @Nullable ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@ import jadx.gui.ui.panel.IssuesPanel;
|
||||
import jadx.gui.ui.panel.JDebuggerPanel;
|
||||
import jadx.gui.ui.panel.ProgressPanel;
|
||||
import jadx.gui.ui.popupmenu.RecentProjectsMenuListener;
|
||||
import jadx.gui.ui.tab.EditorSyncManager;
|
||||
import jadx.gui.ui.tab.NavigationController;
|
||||
import jadx.gui.ui.tab.QuickTabsTree;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabsController;
|
||||
@@ -195,6 +197,10 @@ public class MainWindow extends JFrame {
|
||||
private final transient CacheManager cacheManager;
|
||||
private final transient BackgroundExecutor backgroundExecutor;
|
||||
|
||||
private final TabsController tabsController;
|
||||
private final NavigationController navController;
|
||||
private final EditorSyncManager editorSyncManager;
|
||||
|
||||
private transient @NotNull JadxProject project;
|
||||
|
||||
private transient JadxGuiAction newProjectAction;
|
||||
@@ -209,7 +215,6 @@ public class MainWindow extends JFrame {
|
||||
private JTree tree;
|
||||
private DefaultTreeModel treeModel;
|
||||
private JRoot treeRoot;
|
||||
private TabsController tabsController;
|
||||
private TabbedPane tabbedPane;
|
||||
private HeapUsageBar heapUsageBar;
|
||||
private transient boolean treeReloading;
|
||||
@@ -238,7 +243,7 @@ public class MainWindow extends JFrame {
|
||||
private boolean loaded;
|
||||
private boolean settingsOpen = false;
|
||||
|
||||
private ShortcutsController shortcutsController;
|
||||
private final ShortcutsController shortcutsController;
|
||||
private JadxMenuBar menuBar;
|
||||
private JMenu pluginsMenu;
|
||||
|
||||
@@ -253,16 +258,19 @@ public class MainWindow extends JFrame {
|
||||
this.renameMappings = new RenameMappingsGui(this);
|
||||
this.cacheManager = new CacheManager(settings);
|
||||
this.shortcutsController = new ShortcutsController(settings);
|
||||
this.tabsController = new TabsController(this);
|
||||
this.navController = new NavigationController(this);
|
||||
|
||||
JadxEventQueue.register();
|
||||
resetCache();
|
||||
FontUtils.registerBundledFonts();
|
||||
setEditorTheme(settings.getEditorThemePath());
|
||||
initUI();
|
||||
this.editorSyncManager = new EditorSyncManager(this, tabbedPane);
|
||||
this.backgroundExecutor = new BackgroundExecutor(settings, progressPane);
|
||||
initMenuAndToolbar();
|
||||
UiUtils.setWindowIcons(this);
|
||||
shortcutsController.registerMouseEventListener(this);
|
||||
this.shortcutsController.registerMouseEventListener(this);
|
||||
loadSettings();
|
||||
|
||||
update();
|
||||
@@ -288,7 +296,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private void processCommandLineArgs() {
|
||||
if (settings.getFiles().isEmpty()) {
|
||||
tabbedPane.showNode(new StartPageNode());
|
||||
tabsController.selectTab(new StartPageNode());
|
||||
} else {
|
||||
open(FileUtils.fileNamesToPaths(settings.getFiles()), this::handleSelectClassOption);
|
||||
}
|
||||
@@ -306,7 +314,7 @@ public class MainWindow extends JFrame {
|
||||
NLS.str("error_dialog.title"), JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
tabbedPane.codeJump(cacheObject.getNodeCache().makeFrom(javaNode));
|
||||
tabsController.codeJump(cacheObject.getNodeCache().makeFrom(javaNode));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,7 +525,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
private void loadFiles(Runnable onFinish) {
|
||||
if (project.getFilePaths().isEmpty()) {
|
||||
tabbedPane.showNode(new StartPageNode());
|
||||
tabsController.selectTab(new StartPageNode());
|
||||
return;
|
||||
}
|
||||
AtomicReference<Exception> wrapperException = new AtomicReference<>();
|
||||
@@ -562,6 +570,8 @@ public class MainWindow extends JFrame {
|
||||
private void closeAll() {
|
||||
notifyLoadListeners(false);
|
||||
cancelBackgroundJobs();
|
||||
navController.reset();
|
||||
tabbedPane.reset();
|
||||
clearTree();
|
||||
resetCache();
|
||||
LogCollector.getInstance().reset();
|
||||
@@ -783,7 +793,6 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void clearTree() {
|
||||
tabbedPane.reset();
|
||||
treeRoot = null;
|
||||
treeModel.setRoot(null);
|
||||
treeModel.reload();
|
||||
@@ -864,15 +873,17 @@ public class MainWindow extends JFrame {
|
||||
JResource res = (JResource) obj;
|
||||
ResourceFile resFile = res.getResFile();
|
||||
if (resFile != null && JResource.isSupportedForView(resFile.getType())) {
|
||||
return tabbedPane.showNode(res);
|
||||
tabsController.selectTab(res);
|
||||
return true;
|
||||
}
|
||||
} else if (obj instanceof JNode) {
|
||||
JNode node = (JNode) obj;
|
||||
if (node.getRootClass() != null) {
|
||||
tabbedPane.codeJump(node);
|
||||
tabsController.codeJump(node);
|
||||
return true;
|
||||
}
|
||||
return tabbedPane.showNode(node);
|
||||
tabsController.selectTab(node);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Content loading error", e);
|
||||
@@ -901,12 +912,8 @@ public class MainWindow extends JFrame {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void syncWithEditor() {
|
||||
ContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();
|
||||
if (selectedContentPanel == null) {
|
||||
return;
|
||||
}
|
||||
JNode node = selectedContentPanel.getNode();
|
||||
// TODO: extract tree component into new class
|
||||
public void selectNodeInTree(JNode node) {
|
||||
if (node.getParent() == null && treeRoot != null) {
|
||||
// node not register in tree
|
||||
node = treeRoot.searchNode(node);
|
||||
@@ -962,7 +969,7 @@ public class MainWindow extends JFrame {
|
||||
if (mainActivityClass == null) {
|
||||
throw new JadxRuntimeException("Failed to find main activity class: " + results.getMainActivity());
|
||||
}
|
||||
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
||||
tabsController.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Main activity not found", e);
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
@@ -992,7 +999,7 @@ public class MainWindow extends JFrame {
|
||||
if (applicationClass == null) {
|
||||
throw new JadxRuntimeException("Failed to find application class: " + results.getApplication());
|
||||
}
|
||||
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(applicationClass));
|
||||
tabsController.codeJump(getCacheObject().getNodeCache().makeFrom(applicationClass));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Application not found", e);
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
@@ -1042,7 +1049,7 @@ public class MainWindow extends JFrame {
|
||||
alwaysSelectOpened.addActionListener(event -> {
|
||||
settings.setAlwaysSelectOpened(!settings.isAlwaysSelectOpened());
|
||||
if (settings.isAlwaysSelectOpened()) {
|
||||
this.syncWithEditor();
|
||||
this.editorSyncManager.sync();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1061,7 +1068,7 @@ public class MainWindow extends JFrame {
|
||||
setQuickTabsVisibility(true);
|
||||
}
|
||||
|
||||
JadxGuiAction syncAction = new JadxGuiAction(ActionModel.SYNC, this::syncWithEditor);
|
||||
JadxGuiAction syncAction = new JadxGuiAction(ActionModel.SYNC, this.editorSyncManager::sync);
|
||||
JadxGuiAction textSearchAction = new JadxGuiAction(ActionModel.TEXT_SEARCH, this::textSearch);
|
||||
JadxGuiAction clsSearchAction = new JadxGuiAction(ActionModel.CLASS_SEARCH,
|
||||
() -> SearchDialog.search(MainWindow.this, SearchDialog.SearchPreset.CLASS));
|
||||
@@ -1085,10 +1092,10 @@ public class MainWindow extends JFrame {
|
||||
JadxGuiAction showLogAction = new JadxGuiAction(ActionModel.SHOW_LOG,
|
||||
() -> showLogViewer(LogOptions.current()));
|
||||
JadxGuiAction aboutAction = new JadxGuiAction(ActionModel.ABOUT, () -> new AboutDialog().setVisible(true));
|
||||
JadxGuiAction backAction = new JadxGuiAction(ActionModel.BACK, tabbedPane::navBack);
|
||||
JadxGuiAction backVariantAction = new JadxGuiAction(ActionModel.BACK_V, tabbedPane::navBack);
|
||||
JadxGuiAction forwardAction = new JadxGuiAction(ActionModel.FORWARD, tabbedPane::navForward);
|
||||
JadxGuiAction forwardVariantAction = new JadxGuiAction(ActionModel.FORWARD_V, tabbedPane::navForward);
|
||||
JadxGuiAction backAction = new JadxGuiAction(ActionModel.BACK, navController::navBack);
|
||||
JadxGuiAction backVariantAction = new JadxGuiAction(ActionModel.BACK_V, navController::navBack);
|
||||
JadxGuiAction forwardAction = new JadxGuiAction(ActionModel.FORWARD, navController::navForward);
|
||||
JadxGuiAction forwardVariantAction = new JadxGuiAction(ActionModel.FORWARD_V, navController::navForward);
|
||||
JadxGuiAction quarkAction = new JadxGuiAction(ActionModel.QUARK,
|
||||
() -> new QuarkDialog(MainWindow.this).setVisible(true));
|
||||
JadxGuiAction openDeviceAction = new JadxGuiAction(ActionModel.OPEN_DEVICE,
|
||||
@@ -1345,7 +1352,6 @@ public class MainWindow extends JFrame {
|
||||
leftPane.add(bottomPane, BorderLayout.PAGE_END);
|
||||
treeSplitPane.setLeftComponent(leftPane);
|
||||
|
||||
tabsController = new TabsController(this);
|
||||
tabbedPane = new TabbedPane(this, tabsController);
|
||||
tabbedPane.setMinimumSize(new Dimension(150, 150));
|
||||
new TabDndController(tabbedPane, settings);
|
||||
@@ -1581,6 +1587,10 @@ public class MainWindow extends JFrame {
|
||||
return tabsController;
|
||||
}
|
||||
|
||||
public NavigationController getNavController() {
|
||||
return navController;
|
||||
}
|
||||
|
||||
public JadxSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
applyEditableProperties(node);
|
||||
loadSettings();
|
||||
|
||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||
JadxSettings settings = contentPanel.getMainWindow().getSettings();
|
||||
setLineWrap(settings.isCodeAreaLineWrap());
|
||||
|
||||
ZoomActions.register(this, settings, this::loadSettings);
|
||||
@@ -147,7 +147,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
}
|
||||
|
||||
private void appendWrapLineMenu(JPopupMenu popupMenu) {
|
||||
JadxSettings settings = contentPanel.getTabbedPane().getMainWindow().getSettings();
|
||||
JadxSettings settings = contentPanel.getMainWindow().getSettings();
|
||||
popupMenu.addSeparator();
|
||||
JCheckBoxMenuItem wrapItem = new JCheckBoxMenuItem(NLS.str("popup.line_wrap"), getLineWrap());
|
||||
wrapItem.setAction(new AbstractAction(NLS.str("popup.line_wrap")) {
|
||||
@@ -378,7 +378,7 @@ public abstract class AbstractCodeArea extends RSyntaxTextArea {
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
loadCommonSettings(contentPanel.getTabbedPane().getMainWindow(), this);
|
||||
loadCommonSettings(contentPanel.getMainWindow(), this);
|
||||
}
|
||||
|
||||
public void scrollToPos(int pos) {
|
||||
|
||||
@@ -87,7 +87,7 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
int offs = viewToModel2D(point);
|
||||
JNode node = getJNodeAtOffset(adjustOffsetForWordToken(offs));
|
||||
if (node != null) {
|
||||
contentPanel.getTabbedPane().codeJump(node);
|
||||
contentPanel.getTabsController().codeJump(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ public final class CodeArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
public MainWindow getMainWindow() {
|
||||
return contentPanel.getTabbedPane().getMainWindow();
|
||||
return contentPanel.getMainWindow();
|
||||
}
|
||||
|
||||
public JadxWrapper getJadxWrapper() {
|
||||
|
||||
@@ -82,7 +82,7 @@ public class CodePanel extends JPanel {
|
||||
AbstractAction globalSearchAction = new AbstractAction(NLS.str("popup.search_global", "")) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
MainWindow mainWindow = codeArea.getContentPanel().getTabbedPane().getMainWindow();
|
||||
MainWindow mainWindow = codeArea.getContentPanel().getMainWindow();
|
||||
SearchDialog.searchText(mainWindow, codeArea.getSelectedText());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,6 +12,6 @@ public final class GoToDeclarationAction extends JNodeAction {
|
||||
|
||||
@Override
|
||||
public void runAction(JNode node) {
|
||||
getCodeArea().getContentPanel().getTabbedPane().codeJump(node);
|
||||
getCodeArea().getContentPanel().getTabsController().codeJump(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,10 +85,10 @@ public class HexArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
private void applyTheme() {
|
||||
Font font = getContentPanel().getTabbedPane().getMainWindow().getSettings().getSmaliFont();
|
||||
Font font = getContentPanel().getMainWindow().getSettings().getSmaliFont();
|
||||
setFont(font);
|
||||
|
||||
Theme theme = contentPanel.getTabbedPane().getMainWindow().getEditorTheme();
|
||||
Theme theme = contentPanel.getMainWindow().getEditorTheme();
|
||||
if (hexPreviewPanel != null) {
|
||||
hexPreviewPanel.applyTheme(theme, font);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
JadxSettings settings = getContentPanel().getTabbedPane().getMainWindow().getSettings();
|
||||
JadxSettings settings = getContentPanel().getMainWindow().getSettings();
|
||||
settings.setSmaliAreaShowBytecode(!settings.getSmaliAreaShowBytecode());
|
||||
contentPanel.getTabbedPane().getTabs().forEach(v -> {
|
||||
if (v instanceof ClassCodeContentPanel) {
|
||||
@@ -127,7 +127,7 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
public void scrollToDebugPos(int pos) {
|
||||
getContentPanel().getTabbedPane().getMainWindow()
|
||||
getContentPanel().getMainWindow()
|
||||
.getSettings().setSmaliAreaShowBytecode(true); // don't sync when it's set programmatically.
|
||||
cbUseSmaliV2.setState(shouldUseSmaliPrinterV2());
|
||||
if (!(model instanceof DebugModel)) {
|
||||
@@ -151,7 +151,7 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
}
|
||||
|
||||
private boolean shouldUseSmaliPrinterV2() {
|
||||
return getContentPanel().getTabbedPane().getMainWindow().getSettings().getSmaliAreaShowBytecode();
|
||||
return getContentPanel().getMainWindow().getSettings().getSmaliAreaShowBytecode();
|
||||
}
|
||||
|
||||
private abstract class SmaliModel {
|
||||
@@ -177,7 +177,7 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
private class NormalModel extends SmaliModel {
|
||||
|
||||
public NormalModel() {
|
||||
Theme theme = getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
Theme theme = getContentPanel().getMainWindow().getEditorTheme();
|
||||
setSyntaxScheme(theme.scheme);
|
||||
setSyntaxEditingStyle(SYNTAX_STYLE_SMALI);
|
||||
}
|
||||
@@ -323,16 +323,16 @@ public final class SmaliArea extends AbstractCodeArea {
|
||||
|
||||
public SmaliV2Style(SmaliArea smaliArea) {
|
||||
super(true);
|
||||
curTheme = smaliArea.getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
curTheme = smaliArea.getContentPanel().getMainWindow().getEditorTheme();
|
||||
updateTheme();
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return getContentPanel().getTabbedPane().getMainWindow().getSettings().getSmaliFont();
|
||||
return getContentPanel().getMainWindow().getSettings().getSmaliFont();
|
||||
}
|
||||
|
||||
public boolean refreshTheme() {
|
||||
Theme theme = getContentPanel().getTabbedPane().getMainWindow().getEditorTheme();
|
||||
Theme theme = getContentPanel().getMainWindow().getEditorTheme();
|
||||
boolean refresh = theme != curTheme;
|
||||
if (refresh) {
|
||||
curTheme = theme;
|
||||
|
||||
@@ -52,7 +52,7 @@ import jadx.gui.treemodel.JResSearchNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.panel.ProgressPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabsController;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
@@ -67,7 +67,7 @@ public abstract class CommonSearchDialog extends JFrame {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CommonSearchDialog.class);
|
||||
private static final long serialVersionUID = 8939332306115370276L;
|
||||
|
||||
protected final transient TabbedPane tabbedPane;
|
||||
protected final transient TabsController tabsController;
|
||||
protected final transient CacheObject cache;
|
||||
protected final transient MainWindow mainWindow;
|
||||
protected final transient Font codeFont;
|
||||
@@ -84,7 +84,7 @@ public abstract class CommonSearchDialog extends JFrame {
|
||||
|
||||
public CommonSearchDialog(MainWindow mainWindow, String title) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.tabbedPane = mainWindow.getTabbedPane();
|
||||
this.tabsController = mainWindow.getTabsController();
|
||||
this.cache = mainWindow.getCacheObject();
|
||||
this.codeFont = mainWindow.getSettings().getFont();
|
||||
this.windowTitle = title;
|
||||
@@ -145,9 +145,9 @@ public abstract class CommonSearchDialog extends JFrame {
|
||||
protected void openItem(JNode node) {
|
||||
if (node instanceof JResSearchNode) {
|
||||
JumpPosition jmpPos = new JumpPosition(((JResSearchNode) node).getResNode(), node.getPos());
|
||||
tabbedPane.codeJump(jmpPos);
|
||||
tabsController.codeJump(jmpPos);
|
||||
} else {
|
||||
tabbedPane.codeJump(node);
|
||||
tabsController.codeJump(node);
|
||||
}
|
||||
if (!mainWindow.getSettings().getKeepCommonDialogOpen()) {
|
||||
dispose();
|
||||
|
||||
@@ -7,7 +7,9 @@ import org.jetbrains.annotations.Nullable;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabsController;
|
||||
|
||||
public abstract class ContentPanel extends JPanel {
|
||||
|
||||
@@ -27,6 +29,14 @@ public abstract class ContentPanel extends JPanel {
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
public TabsController getTabsController() {
|
||||
return tabbedPane.getTabsController();
|
||||
}
|
||||
|
||||
public MainWindow getMainWindow() {
|
||||
return tabbedPane.getMainWindow();
|
||||
}
|
||||
|
||||
public JNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public final class HtmlPanel extends ContentPanel {
|
||||
|
||||
@Override
|
||||
public void loadSettings() {
|
||||
JadxSettings settings = getTabbedPane().getMainWindow().getSettings();
|
||||
JadxSettings settings = getMainWindow().getSettings();
|
||||
textArea.setFont(settings.getFont());
|
||||
}
|
||||
|
||||
|
||||
@@ -445,7 +445,7 @@ public class JDebuggerPanel extends JPanel {
|
||||
}
|
||||
|
||||
public void scrollToSmaliLine(JClass cls, int pos, boolean debugMode) {
|
||||
SwingUtilities.invokeLater(() -> getMainWindow().getTabbedPane().smaliJump(cls, pos, debugMode));
|
||||
SwingUtilities.invokeLater(() -> getMainWindow().getTabsController().smaliJump(cls, pos, debugMode));
|
||||
}
|
||||
|
||||
public void resetAllDebuggingInfo() {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
|
||||
public class EditorSyncManager implements ITabStatesListener {
|
||||
private final MainWindow mainWindow;
|
||||
private final TabbedPane tabbedPane;
|
||||
|
||||
public EditorSyncManager(MainWindow mainWindow, TabbedPane tabbedPane) {
|
||||
this.mainWindow = mainWindow;
|
||||
this.tabbedPane = tabbedPane;
|
||||
mainWindow.getTabsController().addListener(this);
|
||||
}
|
||||
|
||||
public void sync() {
|
||||
ContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();
|
||||
if (selectedContentPanel != null) {
|
||||
mainWindow.selectNodeInTree(selectedContentPanel.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
if (mainWindow.getSettings().isAlwaysSelectOpened()) {
|
||||
// verify that tab opened for this blueprint (some nodes don't open tab with content)
|
||||
ContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();
|
||||
if (selectedContentPanel != null && selectedContentPanel.getNode().equals(blueprint.getNode())) {
|
||||
sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,47 @@ package jadx.gui.ui.tab;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
|
||||
public interface ITabStatesListener {
|
||||
void onTabOpen(TabBlueprint blueprint);
|
||||
|
||||
void onTabSelect(TabBlueprint blueprint);
|
||||
default void onTabOpen(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabClose(TabBlueprint blueprint);
|
||||
default void onTabSelect(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabPositionFirst(TabBlueprint blueprint);
|
||||
default void onTabCodeJump(TabBlueprint blueprint, JumpPosition position) {
|
||||
}
|
||||
|
||||
void onTabPinChange(TabBlueprint blueprint);
|
||||
default void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {
|
||||
}
|
||||
|
||||
void onTabBookmarkChange(TabBlueprint blueprint);
|
||||
default void onTabClose(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabVisibilityChange(TabBlueprint blueprint);
|
||||
default void onTabPositionFirst(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabRestore(TabBlueprint blueprint, EditorViewState viewState);
|
||||
default void onTabPinChange(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabsRestoreDone();
|
||||
default void onTabBookmarkChange(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
void onTabsReorder(List<TabBlueprint> blueprints);
|
||||
default void onTabVisibilityChange(TabBlueprint blueprint) {
|
||||
}
|
||||
|
||||
default void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
}
|
||||
|
||||
default void onTabsRestoreDone() {
|
||||
}
|
||||
|
||||
default void onTabsReorder(List<TabBlueprint> blueprints) {
|
||||
}
|
||||
|
||||
default void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
}
|
||||
|
||||
void onTabSave(TabBlueprint blueprint, EditorViewState viewState);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
|
||||
/**
|
||||
* Utility class to log events from TabsController by implementing ITabStatesListener.
|
||||
*/
|
||||
public class LogTabStates implements ITabStatesListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LogTabStates.class);
|
||||
|
||||
@Override
|
||||
public void onTabBookmarkChange(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabBookmarkChange: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabClose(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabClose: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabCodeJump(TabBlueprint blueprint, JumpPosition position) {
|
||||
LOG.debug("onTabCodeJump: blueprint={}, position={}", blueprint, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabOpen(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabOpen: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPinChange(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabPinChange: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPositionFirst(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabPositionFirst: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
LOG.debug("onTabRestore: blueprint={}, viewState={}", blueprint, viewState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
LOG.debug("onTabSave: blueprint={}, viewState={}", blueprint, viewState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabSelect: blueprint={}", blueprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {
|
||||
LOG.debug("onTabSmaliJump: blueprint={}, pos={}, debugMode={}", blueprint, pos, debugMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsReorder(List<TabBlueprint> blueprints) {
|
||||
LOG.debug("onTabsReorder: blueprints={}", blueprints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsRestoreDone() {
|
||||
LOG.debug("onTabsRestoreDone");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabVisibilityChange(TabBlueprint blueprint) {
|
||||
LOG.debug("onTabVisibilityChange: blueprint={}", blueprint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.JumpManager;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
|
||||
/**
|
||||
* TODO: Save jumps history into project file to restore after reload or reopen
|
||||
*/
|
||||
public class NavigationController implements ITabStatesListener {
|
||||
private final transient MainWindow mainWindow;
|
||||
|
||||
private final transient JumpManager jumps = new JumpManager();
|
||||
|
||||
public NavigationController(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
mainWindow.getTabsController().addListener(this);
|
||||
}
|
||||
|
||||
public void navBack() {
|
||||
if (jumps.size() > 1) {
|
||||
jumps.updateCurPosition(mainWindow.getTabbedPane().getCurrentPosition());
|
||||
}
|
||||
jump(jumps.getPrev());
|
||||
}
|
||||
|
||||
public void navForward() {
|
||||
if (jumps.size() > 1) {
|
||||
jumps.updateCurPosition(mainWindow.getTabbedPane().getCurrentPosition());
|
||||
}
|
||||
jump(jumps.getNext());
|
||||
}
|
||||
|
||||
private void jump(@Nullable JumpPosition pos) {
|
||||
if (pos != null) {
|
||||
mainWindow.getTabsController().codeJump(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabCodeJump(TabBlueprint blueprint, JumpPosition position) {
|
||||
if (position.equals(jumps.getCurrent())) {
|
||||
// ignore self-initiated jumps
|
||||
return;
|
||||
}
|
||||
saveCurrentPosition();
|
||||
jumps.addPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {
|
||||
saveCurrentPosition();
|
||||
// TODO: save smali jump
|
||||
}
|
||||
|
||||
private void saveCurrentPosition() {
|
||||
JumpPosition curPos = mainWindow.getTabbedPane().getCurrentPosition();
|
||||
if (curPos != null) {
|
||||
jumps.addPosition(curPos);
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
jumps.reset();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
reset();
|
||||
mainWindow.getTabsController().removeListener(this);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
@@ -20,7 +19,6 @@ import javax.swing.tree.TreeNode;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSelectionListener {
|
||||
@@ -180,11 +178,6 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabClose(TabBlueprint blueprint) {
|
||||
removeJNode(openParentNode, blueprint.getNode());
|
||||
@@ -192,11 +185,6 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
removeJNode(bookmarkParentNode, blueprint.getNode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPositionFirst(TabBlueprint blueprint) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPinChange(TabBlueprint blueprint) {
|
||||
JNode node = blueprint.getNode();
|
||||
@@ -227,26 +215,6 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsRestoreDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsReorder(List<TabBlueprint> blueprints) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
|
||||
}
|
||||
|
||||
private class Root extends DefaultMutableTreeNode {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
|
||||
public class TabBlueprint {
|
||||
@@ -9,7 +11,7 @@ public class TabBlueprint {
|
||||
private boolean hidden;
|
||||
|
||||
public TabBlueprint(JNode node) {
|
||||
this.node = node;
|
||||
this.node = Objects.requireNonNull(node);
|
||||
}
|
||||
|
||||
public JNode getNode() {
|
||||
@@ -47,4 +49,29 @@ public class TabBlueprint {
|
||||
public void setHidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TabBlueprint)) {
|
||||
return false;
|
||||
}
|
||||
return node.equals(((TabBlueprint) o).node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return node.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TabBlueprint{node=" + node
|
||||
+ ", bookmarked=" + bookmarked
|
||||
+ ", pinned=" + pinned
|
||||
+ ", hidden=" + hidden
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -20,10 +20,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.metadata.ICodeAnnotation;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
@@ -38,9 +34,7 @@ import jadx.gui.ui.panel.HtmlPanel;
|
||||
import jadx.gui.ui.panel.IViewStateSupport;
|
||||
import jadx.gui.ui.panel.ImagePanel;
|
||||
import jadx.gui.ui.tab.dnd.TabDndController;
|
||||
import jadx.gui.utils.JumpManager;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
@@ -52,8 +46,6 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
private final transient TabsController controller;
|
||||
private final transient Map<JNode, ContentPanel> tabsMap = new HashMap<>();
|
||||
|
||||
private final transient JumpManager jumps = new JumpManager();
|
||||
|
||||
private transient ContentPanel curTab;
|
||||
private transient ContentPanel lastTab;
|
||||
|
||||
@@ -212,63 +204,8 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump to node definition
|
||||
*/
|
||||
public void codeJump(JNode node) {
|
||||
JClass parentCls = node.getJParent();
|
||||
if (parentCls != null) {
|
||||
JavaClass cls = node.getJParent().getCls();
|
||||
JavaClass origTopCls = cls.getOriginalTopParentClass();
|
||||
JavaClass codeParent = cls.getTopParentClass();
|
||||
if (!Objects.equals(codeParent, origTopCls)) {
|
||||
JClass jumpCls = mainWindow.getCacheObject().getNodeCache().makeFrom(codeParent);
|
||||
mainWindow.getBackgroundExecutor().execute(
|
||||
NLS.str("progress.load"),
|
||||
jumpCls::loadNode, // load code in background
|
||||
status -> {
|
||||
// search original node in jump class
|
||||
codeParent.getCodeInfo().getCodeMetadata().searchDown(0, (pos, ann) -> {
|
||||
if (ann.getAnnType() == ICodeAnnotation.AnnType.DECLARATION) {
|
||||
ICodeNodeRef declNode = ((NodeDeclareRef) ann).getNode();
|
||||
if (declNode.equals(node.getJavaNode().getCodeNodeRef())) {
|
||||
codeJump(new JumpPosition(jumpCls, pos));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not an inline node, jump normally
|
||||
if (node.getPos() != 0 || node.getRootClass() == null) {
|
||||
codeJump(new JumpPosition(node));
|
||||
return;
|
||||
}
|
||||
// node need loading
|
||||
mainWindow.getBackgroundExecutor().execute(
|
||||
NLS.str("progress.load"),
|
||||
() -> node.getRootClass().getCodeInfo(), // run heavy loading in background
|
||||
status -> codeJump(new JumpPosition(node)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link TabbedPane#codeJump(JNode)} method
|
||||
*/
|
||||
public void codeJump(JumpPosition pos) {
|
||||
saveJump(pos);
|
||||
showCode(pos);
|
||||
}
|
||||
|
||||
private void saveJump(JumpPosition pos) {
|
||||
JumpPosition curPos = getCurrentPosition();
|
||||
if (curPos != null) {
|
||||
jumps.addPosition(curPos);
|
||||
jumps.addPosition(pos);
|
||||
}
|
||||
public TabsController getTabsController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
private @Nullable ContentPanel showCode(JumpPosition jumpPos) {
|
||||
@@ -280,15 +217,6 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
return contentPanel;
|
||||
}
|
||||
|
||||
public boolean showNode(JNode node) {
|
||||
final ContentPanel contentPanel = getContentPanel(node);
|
||||
if (contentPanel == null) {
|
||||
return false;
|
||||
}
|
||||
selectTab(contentPanel);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void scrollToPos(ContentPanel contentPanel, int pos) {
|
||||
if (pos == 0) {
|
||||
LOG.warn("Ignore zero jump!", new JadxRuntimeException());
|
||||
@@ -305,7 +233,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
controller.selectTab(contentPanel.getNode());
|
||||
}
|
||||
|
||||
public void smaliJump(JClass cls, int pos, boolean debugMode) {
|
||||
private void smaliJump(JClass cls, int pos, boolean debugMode) {
|
||||
ContentPanel panel = getTabByNode(cls);
|
||||
if (panel == null) {
|
||||
panel = showCode(new JumpPosition(cls, 1));
|
||||
@@ -325,8 +253,7 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
smaliArea.requestFocus();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public JumpPosition getCurrentPosition() {
|
||||
public @Nullable JumpPosition getCurrentPosition() {
|
||||
ContentPanel selectedCodePanel = getSelectedContentPanel();
|
||||
if (selectedCodePanel instanceof AbstractCodeContentPanel) {
|
||||
return ((AbstractCodeContentPanel) selectedCodePanel).getCodeArea().getCurrentPosition();
|
||||
@@ -334,26 +261,6 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void navBack() {
|
||||
if (jumps.size() > 1) {
|
||||
jumps.updateCurPosition(getCurrentPosition());
|
||||
}
|
||||
JumpPosition pos = jumps.getPrev();
|
||||
if (pos != null) {
|
||||
showCode(pos);
|
||||
}
|
||||
}
|
||||
|
||||
public void navForward() {
|
||||
if (jumps.size() > 1) {
|
||||
jumps.updateCurPosition(getCurrentPosition());
|
||||
}
|
||||
JumpPosition pos = jumps.getNext();
|
||||
if (pos != null) {
|
||||
showCode(pos);
|
||||
}
|
||||
}
|
||||
|
||||
private void addContentPanel(ContentPanel contentPanel) {
|
||||
tabsMap.put(contentPanel.getNode(), contentPanel);
|
||||
int tabCount = getTabCount();
|
||||
@@ -382,7 +289,15 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
}
|
||||
|
||||
public @Nullable TabComponent getTabComponentByNode(JNode node) {
|
||||
Component component = getTabComponentAt(indexOfComponent(getTabByNode(node)));
|
||||
ContentPanel contentPanel = getTabByNode(node);
|
||||
if (contentPanel == null) {
|
||||
return null;
|
||||
}
|
||||
int index = indexOfComponent(contentPanel);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
Component component = getTabComponentAt(index);
|
||||
if (!(component instanceof TabComponent)) {
|
||||
return null;
|
||||
}
|
||||
@@ -460,7 +375,6 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
public void reset() {
|
||||
closeAllTabs();
|
||||
tabsMap.clear();
|
||||
jumps.reset();
|
||||
curTab = null;
|
||||
lastTab = null;
|
||||
FocusManager.reset();
|
||||
@@ -485,16 +399,30 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
return;
|
||||
}
|
||||
ContentPanel newPanel = blueprint.getNode().getContentPanel(this);
|
||||
FocusManager.listen(newPanel);
|
||||
addContentPanel(newPanel);
|
||||
if (newPanel != null) {
|
||||
FocusManager.listen(newPanel);
|
||||
addContentPanel(newPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
ContentPanel contentPanel = getContentPanel(blueprint.getNode());
|
||||
setSelectedComponent(contentPanel);
|
||||
if (mainWindow.getSettings().isAlwaysSelectOpened()) {
|
||||
mainWindow.syncWithEditor();
|
||||
if (contentPanel != null) {
|
||||
setSelectedComponent(contentPanel);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabCodeJump(TabBlueprint blueprint, JumpPosition position) {
|
||||
showCode(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSmaliJump(TabBlueprint blueprint, int pos, boolean debugMode) {
|
||||
JNode node = blueprint.getNode();
|
||||
if (node instanceof JClass) {
|
||||
smaliJump((JClass) node, pos, debugMode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -564,25 +492,19 @@ public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsRestoreDone() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsReorder(List<TabBlueprint> blueprints) {
|
||||
List<TabBlueprint> newBlueprints = new ArrayList<>();
|
||||
List<TabBlueprint> newBlueprints = new ArrayList<>(blueprints.size());
|
||||
for (ContentPanel contentPanel : getTabs()) {
|
||||
Optional<TabBlueprint> blueprintFindResult = blueprints.stream()
|
||||
.filter(b -> b.getNode() == contentPanel.getNode())
|
||||
.findFirst();
|
||||
if (blueprintFindResult.isPresent()) {
|
||||
TabBlueprint blueprint = blueprintFindResult.get();
|
||||
blueprints.remove(blueprint);
|
||||
TabBlueprint blueprint = controller.getTabByNode(contentPanel.getNode());
|
||||
if (blueprint != null) {
|
||||
newBlueprints.add(blueprint);
|
||||
}
|
||||
}
|
||||
// Add back hidden tabs
|
||||
newBlueprints.addAll(blueprints);
|
||||
Set<TabBlueprint> set = new LinkedHashSet<>(blueprints);
|
||||
newBlueprints.forEach(set::remove);
|
||||
newBlueprints.addAll(set);
|
||||
|
||||
blueprints.clear();
|
||||
blueprints.addAll(newBlueprints);
|
||||
|
||||
@@ -4,22 +4,34 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.metadata.ICodeAnnotation;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class TabsController {
|
||||
private final transient MainWindow mainWindow;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TabsController.class);
|
||||
|
||||
private final MainWindow mainWindow;
|
||||
private final Map<JNode, TabBlueprint> tabsMap = new HashMap<>();
|
||||
private final List<ITabStatesListener> listeners = new ArrayList<>();
|
||||
|
||||
private boolean forceClose;
|
||||
|
||||
private TabBlueprint selectedTab = null;
|
||||
private @Nullable TabBlueprint selectedTab;
|
||||
|
||||
public TabsController(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -57,7 +69,6 @@ public class TabsController {
|
||||
}
|
||||
blueprint = newBlueprint;
|
||||
}
|
||||
|
||||
setTabHiddenInternal(blueprint, hidden);
|
||||
return blueprint;
|
||||
}
|
||||
@@ -65,10 +76,68 @@ public class TabsController {
|
||||
public void selectTab(JNode node) {
|
||||
TabBlueprint blueprint = openTab(node);
|
||||
selectedTab = blueprint;
|
||||
|
||||
listeners.forEach(l -> l.onTabSelect(blueprint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump to node definition
|
||||
*/
|
||||
public void codeJump(JNode node) {
|
||||
JClass parentCls = node.getJParent();
|
||||
if (parentCls != null) {
|
||||
JavaClass cls = node.getJParent().getCls();
|
||||
JavaClass origTopCls = cls.getOriginalTopParentClass();
|
||||
JavaClass codeParent = cls.getTopParentClass();
|
||||
if (!Objects.equals(codeParent, origTopCls)) {
|
||||
JClass jumpCls = mainWindow.getCacheObject().getNodeCache().makeFrom(codeParent);
|
||||
mainWindow.getBackgroundExecutor().execute(
|
||||
NLS.str("progress.load"),
|
||||
jumpCls::loadNode, // load code in background
|
||||
status -> {
|
||||
// search original node in jump class
|
||||
codeParent.getCodeInfo().getCodeMetadata().searchDown(0, (pos, ann) -> {
|
||||
if (ann.getAnnType() == ICodeAnnotation.AnnType.DECLARATION) {
|
||||
ICodeNodeRef declNode = ((NodeDeclareRef) ann).getNode();
|
||||
if (declNode.equals(node.getJavaNode().getCodeNodeRef())) {
|
||||
codeJump(new JumpPosition(jumpCls, pos));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not an inline node, jump normally
|
||||
if (node.getPos() != 0 || node.getRootClass() == null) {
|
||||
codeJump(new JumpPosition(node));
|
||||
return;
|
||||
}
|
||||
// node need loading
|
||||
mainWindow.getBackgroundExecutor().execute(
|
||||
NLS.str("progress.load"),
|
||||
() -> node.getRootClass().getCodeInfo(), // run heavy loading in background
|
||||
status -> codeJump(new JumpPosition(node)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link TabsController#codeJump(JNode)} method
|
||||
*/
|
||||
public void codeJump(JumpPosition pos) {
|
||||
if (selectedTab == null) {
|
||||
LOG.warn("Cannot codeJump because selectedTab is null");
|
||||
return;
|
||||
}
|
||||
listeners.forEach(l -> l.onTabCodeJump(selectedTab, pos));
|
||||
}
|
||||
|
||||
public void smaliJump(JClass cls, int pos, boolean debugMode) {
|
||||
TabBlueprint blueprint = openTab(cls);
|
||||
listeners.forEach(l -> l.onTabSmaliJump(blueprint, pos, debugMode));
|
||||
}
|
||||
|
||||
public void closeTab(JNode node) {
|
||||
closeTab(node, false);
|
||||
}
|
||||
@@ -219,6 +288,11 @@ public class TabsController {
|
||||
}
|
||||
|
||||
public void notifyRestoreEditorViewStateDone() {
|
||||
if (selectedTab == null && !tabsMap.isEmpty()) {
|
||||
JNode node = tabsMap.values().iterator().next().getNode();
|
||||
LOG.warn("No active tab found, select {}", node); // TODO: find the reason of this issue
|
||||
selectTab(node);
|
||||
}
|
||||
listeners.forEach(ITabStatesListener::onTabsRestoreDone);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,8 +43,7 @@ public class JumpManager {
|
||||
return pos.equals(current);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JumpPosition getCurrent() {
|
||||
public @Nullable JumpPosition getCurrent() {
|
||||
if (currentPos >= 0 && currentPos < list.size()) {
|
||||
return list.get(currentPos);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user