package jadx.gui.ui;
import jadx.api.ResourceFile;
import jadx.api.ResourceType;
import jadx.gui.treemodel.JNode;
import jadx.gui.treemodel.JResource;
import jadx.gui.utils.JumpManager;
import jadx.gui.utils.NLS;
import jadx.gui.utils.Position;
import jadx.gui.utils.Utils;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicButtonUI;
import javax.swing.text.BadLocationException;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class TabbedPane extends JTabbedPane {
private static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class);
private static final long serialVersionUID = -8833600618794570904L;
private static final ImageIcon ICON_CLOSE = Utils.openIcon("cross");
private static final ImageIcon ICON_CLOSE_INACTIVE = Utils.openIcon("cross_grayed");
private final MainWindow mainWindow;
private final Map<JNode, ContentPanel> openTabs = new LinkedHashMap<JNode, ContentPanel>();
private JumpManager jumps = new JumpManager();
TabbedPane(MainWindow window) {
mainWindow = window;
setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
addMouseWheelListener(new MouseWheelListener() {
public void mouseWheelMoved(MouseWheelEvent e) {
int direction = e.getWheelRotation();
int index = getSelectedIndex();
int maxIndex = getTabCount() - 1;
if ((index == 0 && direction < 0)
|| (index == maxIndex && direction > 0)) {
index = maxIndex - index;
} else {
index += direction;
}
setSelectedIndex(index);
}
});
}
MainWindow getMainWindow() {
return mainWindow;
}
private void showCode(final Position pos) {
final CodePanel contentPanel = (CodePanel) getContentPanel(pos.getNode());
if (contentPanel == null) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setSelectedComponent(contentPanel);
CodeArea codeArea = contentPanel.getCodeArea();
int line = pos.getLine();
if (line < 0) {
try {
line = 1 + codeArea.getLineOfOffset(-line);
} catch (BadLocationException e) {
LOG.error("Can't get line for: {}", pos, e);
line = pos.getNode().getLine();
}
}
codeArea.scrollToLine(line);
codeArea.requestFocus();
}
});
}
public void showResource(JResource res) {
final ContentPanel contentPanel = getContentPanel(res);
if (contentPanel == null) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setSelectedComponent(contentPanel);
}
});
}
public void codeJump(Position pos) {
Position curPos = getCurrentPosition();
if (curPos != null) {
jumps.addPosition(curPos);
jumps.addPosition(pos);
}
showCode(pos);
}
@Nullable
private Position getCurrentPosition() {
ContentPanel selectedCodePanel = getSelectedCodePanel();
if (selectedCodePanel instanceof CodePanel) {
return ((CodePanel) selectedCodePanel).getCodeArea().getCurrentPosition();
}
return null;
}
public void navBack() {
Position pos = jumps.getPrev();
if (pos != null) {
showCode(pos);
}
}
public void navForward() {
Position pos = jumps.getNext();
if (pos != null) {
showCode(pos);
}
}
private void addContentPanel(ContentPanel contentPanel) {
openTabs.put(contentPanel.getNode(), contentPanel);
add(contentPanel);
}
private void closeCodePanel(ContentPanel contentPanel) {
openTabs.remove(contentPanel.getNode());
remove(contentPanel);
}
@Nullable
private ContentPanel getContentPanel(JNode node) {
ContentPanel panel = openTabs.get(node);
if (panel == null) {
panel = makeContentPanel(node);
if (panel == null) {
return null;
}
addContentPanel(panel);
setTabComponentAt(indexOfComponent(panel), makeTabComponent(panel));
}
return panel;
}
@Nullable
private ContentPanel makeContentPanel(JNode node) {
if (node instanceof JResource) {
JResource res = (JResource) node;
ResourceFile resFile = res.getResFile();
if (resFile != null) {
if (resFile.getType() == ResourceType.IMG) {
return new ImagePanel(this, res);
}
} else {
return null;
}
}
return new CodePanel(this, node);
}
@Nullable
ContentPanel getSelectedCodePanel() {
return (ContentPanel) getSelectedComponent();
}
private Component makeTabComponent(final ContentPanel contentPanel) {
JNode node = contentPanel.getNode();
String name = node.makeLongString();
final JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 3, 0));
panel.setOpaque(false);
final JLabel label = new JLabel(name);
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
label.setIcon(node.getIcon());
final JButton button = new JButton();
button.setIcon(ICON_CLOSE_INACTIVE);
button.setRolloverIcon(ICON_CLOSE);
button.setRolloverEnabled(true);
button.setOpaque(false);
button.setUI(new BasicButtonUI());
button.setContentAreaFilled(false);
button.setFocusable(false);
button.setBorder(null);
button.setBorderPainted(false);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeCodePanel(contentPanel);
}
});
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isMiddleMouseButton(e)) {
closeCodePanel(contentPanel);
} else if (SwingUtilities.isRightMouseButton(e)) {
JPopupMenu menu = createTabPopupMenu(contentPanel);
menu.show(panel, e.getX(), e.getY());
} else {
// TODO: make correct event delegation to tabbed pane
setSelectedComponent(contentPanel);
}
}
});
panel.add(label);
panel.add(button);
panel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
return panel;
}
private JPopupMenu createTabPopupMenu(final ContentPanel contentPanel) {
JPopupMenu menu = new JPopupMenu();
JMenuItem closeTab = new JMenuItem(NLS.str("tabs.close"));
closeTab.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeCodePanel(contentPanel);
}
});
menu.add(closeTab);
if (openTabs.size() > 1) {
JMenuItem closeOther = new JMenuItem(NLS.str("tabs.closeOthers"));
closeOther.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
for (ContentPanel panel : contentPanels) {
if (panel != contentPanel) {
closeCodePanel(panel);
}
}
}
});
menu.add(closeOther);
JMenuItem closeAll = new JMenuItem(NLS.str("tabs.closeAll"));
closeAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
closeAllTabs();
}
});
menu.add(closeAll);
menu.addSeparator();
ContentPanel selectedContentPanel = getSelectedCodePanel();
for (final Map.Entry<JNode, ContentPanel> entry : openTabs.entrySet()) {
final ContentPanel cp = entry.getValue();
if (cp == selectedContentPanel) {
continue;
}
JNode node = entry.getKey();
final String clsName = node.makeLongString();
JMenuItem item = new JMenuItem(clsName);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setSelectedComponent(cp);
}
});
item.setIcon(node.getIcon());
menu.add(item);
}
}
return menu;
}
public void closeAllTabs() {
List<ContentPanel> contentPanels = new ArrayList<ContentPanel>(openTabs.values());
for (ContentPanel panel : contentPanels) {
closeCodePanel(panel);
}
}
public void loadSettings() {
for (ContentPanel panel : openTabs.values()) {
panel.loadSettings();
}
}
}