| 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 |