2021-06-26 01:13:46 +00:00
|
|
|
package the.bytecode.club.bytecodeviewer.gui.resourceviewer;
|
|
|
|
|
|
|
|
import java.awt.Color;
|
|
|
|
import java.awt.Component;
|
|
|
|
import java.awt.FlowLayout;
|
|
|
|
import java.awt.Rectangle;
|
|
|
|
import java.awt.event.InputEvent;
|
|
|
|
import java.awt.event.MouseAdapter;
|
|
|
|
import java.awt.event.MouseEvent;
|
|
|
|
import java.awt.event.MouseListener;
|
2021-08-26 21:59:07 +00:00
|
|
|
import javax.swing.BorderFactory;
|
|
|
|
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;
|
2021-06-26 01:13:46 +00:00
|
|
|
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
|
|
|
|
import the.bytecode.club.bytecodeviewer.gui.components.ButtonHoverAnimation;
|
|
|
|
import the.bytecode.club.bytecodeviewer.gui.components.MaxWidthJLabel;
|
|
|
|
import the.bytecode.club.bytecodeviewer.gui.components.listeners.MouseClickedListener;
|
2021-06-26 12:26:12 +00:00
|
|
|
import the.bytecode.club.bytecodeviewer.gui.resourceviewer.viewer.ResourceViewer;
|
2021-06-26 01:13:46 +00:00
|
|
|
import the.bytecode.club.bytecodeviewer.gui.util.DelayTabbedPaneThread;
|
2021-07-21 21:26:22 +00:00
|
|
|
import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings;
|
2021-06-26 01:13:46 +00:00
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
|
|
|
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
|
|
|
|
* *
|
|
|
|
* This program is free software: you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Component to be used as tabComponent; Contains a JLabel to show the text and a JButton to close the tab it belongs to
|
|
|
|
*
|
|
|
|
* @author Konloch
|
|
|
|
* @author WaterWolf
|
2021-07-04 07:04:08 +00:00
|
|
|
* @since 09/26/2011
|
2021-06-26 01:13:46 +00:00
|
|
|
*/
|
|
|
|
public class TabbedPane extends JPanel
|
|
|
|
{
|
|
|
|
public final JTabbedPane tabs;
|
|
|
|
public final JLabel label;
|
|
|
|
private DelayTabbedPaneThread probablyABadIdea;
|
|
|
|
private long startedDragging = 0;
|
|
|
|
public final String tabName;
|
|
|
|
public final String fileContainerName;
|
2021-06-26 12:26:12 +00:00
|
|
|
public final ResourceViewer resource;
|
2021-06-26 01:13:46 +00:00
|
|
|
private static long lastMouseClick = System.currentTimeMillis();
|
|
|
|
public final static MouseListener buttonHoverAnimation = new ButtonHoverAnimation();
|
|
|
|
public static final Color BLANK_COLOR = new Color(0, 0, 0, 0);
|
|
|
|
|
2021-06-26 12:26:12 +00:00
|
|
|
public TabbedPane(int tabIndex, String tabWorkingName, String fileContainerName, String name, final JTabbedPane existingTabs, ResourceViewer resource)
|
2021-06-26 01:13:46 +00:00
|
|
|
{
|
|
|
|
// unset default FlowLayout' gaps
|
|
|
|
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
|
|
|
|
|
|
|
this.tabName = name;
|
|
|
|
this.fileContainerName = fileContainerName;
|
2021-06-26 12:26:12 +00:00
|
|
|
this.resource = resource;
|
|
|
|
|
2021-06-26 01:13:46 +00:00
|
|
|
if (existingTabs == null)
|
|
|
|
throw new NullPointerException("TabbedPane is null");
|
|
|
|
|
|
|
|
this.tabs = existingTabs;
|
|
|
|
setOpaque(false);
|
|
|
|
|
|
|
|
// make JLabel read titles from JTabbedPane
|
|
|
|
label = new MaxWidthJLabel(tabName, 400, 20);
|
|
|
|
|
|
|
|
this.add(label);
|
|
|
|
// add more space between the label and the button
|
|
|
|
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
|
|
|
|
// tab button
|
2021-06-26 02:11:43 +00:00
|
|
|
JButton exitButton = new TabExitButton(this, tabIndex, tabWorkingName);
|
|
|
|
this.add(exitButton);
|
2021-06-26 01:13:46 +00:00
|
|
|
// add more space to the top of the component
|
|
|
|
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
|
|
|
|
|
|
|
|
//define the right click pop-up menu
|
|
|
|
JPopupMenu rightClickMenu = new JPopupMenu();
|
2021-12-19 23:24:17 +00:00
|
|
|
JMenuItem closeAllTabs = new JMenuItem(TranslatedStrings.CLOSE_ALL_BUT_THIS + ": " + name);
|
|
|
|
JMenuItem closeTab = new JMenuItem(TranslatedStrings.CLOSE_TAB + ": " + name);
|
2021-06-26 01:13:46 +00:00
|
|
|
|
|
|
|
rightClickMenu.add(closeAllTabs);
|
|
|
|
rightClickMenu.add(closeTab);
|
|
|
|
//setComponentPopupMenu(rightClickMenu);
|
|
|
|
|
2021-06-26 02:11:43 +00:00
|
|
|
exitButton.setComponentPopupMenu(rightClickMenu);
|
|
|
|
exitButton.addMouseListener(new MouseClickedListener(e ->
|
2021-06-26 01:13:46 +00:00
|
|
|
{
|
2021-09-16 11:55:56 +00:00
|
|
|
if (e.getModifiersEx() != InputEvent.ALT_DOWN_MASK || System.currentTimeMillis() - lastMouseClick < 100)
|
2021-06-26 01:13:46 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
lastMouseClick = System.currentTimeMillis();
|
|
|
|
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
|
|
|
|
if (i != -1)
|
|
|
|
existingTabs.remove(i);
|
|
|
|
}));
|
|
|
|
|
|
|
|
closeTab.addActionListener(e ->
|
|
|
|
{
|
2021-06-26 02:11:43 +00:00
|
|
|
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
|
|
|
|
final int index = tabExitButton.getTabIndex();
|
2021-06-26 01:13:46 +00:00
|
|
|
|
|
|
|
if (index != -1)
|
|
|
|
existingTabs.remove(index);
|
|
|
|
});
|
|
|
|
closeAllTabs.addActionListener(e ->
|
|
|
|
{
|
2021-06-26 02:11:43 +00:00
|
|
|
TabExitButton tabExitButton = (TabExitButton) ((JPopupMenu)((JMenuItem) e.getSource()).getParent()).getInvoker();
|
|
|
|
final int index = tabExitButton.getTabIndex();
|
2021-06-26 01:13:46 +00:00
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
if (existingTabs.getTabCount() <= 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (index != 0)
|
|
|
|
existingTabs.remove(0);
|
|
|
|
else
|
|
|
|
existingTabs.remove(1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
//tab dragging
|
|
|
|
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
|
|
|
|
{
|
|
|
|
/*label.addMouseListener(new MouseListener() {
|
|
|
|
@Override public void mouseClicked(MouseEvent e) {}
|
|
|
|
@Override public void mouseEntered(MouseEvent arg0) {
|
|
|
|
}
|
|
|
|
@Override public void mouseExited(MouseEvent arg0) {
|
|
|
|
}
|
|
|
|
@Override public void mousePressed(MouseEvent e) {
|
|
|
|
onMousePressed(e);
|
|
|
|
}
|
|
|
|
@Override public void mouseReleased(MouseEvent e) {
|
|
|
|
stopDragging(e.getX(), e.getY());
|
|
|
|
}
|
|
|
|
});*/
|
|
|
|
|
|
|
|
this.addMouseListener(new MouseListener() {
|
|
|
|
@Override public void mouseClicked(MouseEvent e) {}
|
|
|
|
@Override public void mouseEntered(MouseEvent arg0) {}
|
|
|
|
@Override public void mouseExited(MouseEvent arg0) {}
|
|
|
|
@Override public void mousePressed(MouseEvent e) {
|
|
|
|
onMousePressed(e);
|
|
|
|
}
|
|
|
|
@Override public void mouseReleased(MouseEvent e) {
|
|
|
|
stopDragging(e.getX(), e.getY());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
//middle click close
|
|
|
|
if(BytecodeViewer.EXPERIMENTAL_TAB_CODE)
|
|
|
|
{
|
|
|
|
this.addMouseListener(new MouseAdapter()
|
|
|
|
{
|
|
|
|
@Override
|
|
|
|
public void mouseReleased(MouseEvent e)
|
|
|
|
{
|
|
|
|
if (e.getButton() != MouseEvent.BUTTON2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
final int i = existingTabs.indexOfTabComponent(TabbedPane.this);
|
|
|
|
if (i != -1)
|
|
|
|
existingTabs.remove(i);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void stopDragging(int mouseX, int mouseY)
|
|
|
|
{
|
|
|
|
if (System.currentTimeMillis() - startedDragging >= 210)
|
|
|
|
{
|
|
|
|
Rectangle bounds = new Rectangle(1, 1, mouseX, mouseY);
|
|
|
|
System.out.println("debug-5: " + mouseX + ", " + mouseY);
|
|
|
|
|
|
|
|
int totalTabs = BytecodeViewer.viewer.workPane.tabs.getTabCount();
|
|
|
|
int index = -1;
|
|
|
|
for (int i = 0; i < totalTabs; i++)
|
|
|
|
{
|
|
|
|
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
|
|
|
|
|
|
|
|
if (c != null && bounds.intersects(c.getBounds()))
|
|
|
|
index = i; //replace this tabs position
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index == -1)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < totalTabs; i++)
|
|
|
|
{
|
|
|
|
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
|
|
|
|
//do some check to see if it's past the X or Y
|
|
|
|
if (c != null) {
|
|
|
|
System.out.println("debug-6: " + c.getBounds());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index != -1)
|
|
|
|
{
|
|
|
|
BytecodeViewer.viewer.workPane.tabs.remove(this);
|
|
|
|
BytecodeViewer.viewer.workPane.tabs.setTabComponentAt(index, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SwingUtilities.invokeLater(() ->
|
|
|
|
{
|
|
|
|
label.setBackground(BLANK_COLOR);
|
|
|
|
label.updateUI();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onMousePressed(MouseEvent e)
|
|
|
|
{
|
|
|
|
BytecodeViewer.viewer.workPane.tabs.dispatchEvent(e);
|
|
|
|
|
|
|
|
if(e.getButton() == 1)
|
|
|
|
{
|
|
|
|
startedDragging = System.currentTimeMillis();
|
|
|
|
//dragging = true;
|
|
|
|
if (probablyABadIdea != null)
|
|
|
|
probablyABadIdea.stopped = true;
|
|
|
|
|
|
|
|
probablyABadIdea = new DelayTabbedPaneThread(TabbedPane.this);
|
|
|
|
probablyABadIdea.start();
|
|
|
|
repaint();
|
|
|
|
System.out.println(e.getX()+", "+e.getY());
|
|
|
|
Rectangle bounds = new Rectangle(1, 1, e.getX(), e.getY());
|
|
|
|
for(int i = 0; i < BytecodeViewer.viewer.workPane.tabs.getTabCount(); i++)
|
|
|
|
{
|
|
|
|
Component c = BytecodeViewer.viewer.workPane.tabs.getTabComponentAt(i);
|
|
|
|
if(c != null && bounds.intersects(c.getBounds()))
|
|
|
|
BytecodeViewer.viewer.workPane.tabs.setSelectedIndex(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
stopDragging(e.getX(), e.getY());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final long serialVersionUID = -4774885688297538774L;
|
|
|
|
|
2021-08-26 21:59:07 +00:00
|
|
|
}
|