committed by
GitHub
parent
d51362ed50
commit
d7ec35791b
@@ -27,10 +27,10 @@ import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JRenameNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeArea;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
@@ -18,9 +18,9 @@ import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JEditableNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.CodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.ui.SimpleMenuItem;
|
||||
|
||||
@@ -30,10 +30,10 @@ import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JRoot;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.filedialog.FileDialogWrapper;
|
||||
import jadx.gui.ui.filedialog.FileOpenMode;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.ui.ActionHandler;
|
||||
|
||||
@@ -19,9 +19,9 @@ import jadx.api.ICodeInfo;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.HtmlPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class QuarkReportNode extends JNode {
|
||||
|
||||
@@ -41,8 +41,8 @@ import jadx.core.utils.Utils;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.treemodel.JMethod;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.ui.NodeLabel;
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.treemodel.JInputScript;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.action.ActionModel;
|
||||
import jadx.gui.ui.action.JadxGuiAction;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.SearchBar;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
@@ -21,9 +21,9 @@ import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.HtmlPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.CertificateManager;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
@@ -23,10 +23,10 @@ import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.nodes.ICodeNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
import jadx.gui.ui.dialog.RenameDialog;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
@@ -17,8 +17,8 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.plugins.script.ScriptContentPanel;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.ui.SimpleMenuItem;
|
||||
|
||||
@@ -16,8 +16,8 @@ import jadx.api.ICodeInfo;
|
||||
import jadx.api.JavaNode;
|
||||
import jadx.api.metadata.ICodeNodeRef;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public abstract class JNode extends DefaultMutableTreeNode implements Comparable<JNode> {
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ import jadx.api.impl.SimpleCodeInfo;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.BinaryContentPanel;
|
||||
import jadx.gui.ui.codearea.CodeContentPanel;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.ImagePanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
@@ -140,6 +140,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.TabbedPane;
|
||||
import jadx.gui.ui.tab.dnd.TabDndController;
|
||||
import jadx.gui.ui.treenodes.StartPageNode;
|
||||
import jadx.gui.ui.treenodes.SummaryNode;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
@@ -1304,6 +1306,7 @@ public class MainWindow extends JFrame {
|
||||
|
||||
tabbedPane = new TabbedPane(this);
|
||||
tabbedPane.setMinimumSize(new Dimension(150, 150));
|
||||
new TabDndController(tabbedPane);
|
||||
|
||||
rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
rightSplitPane.setTopComponent(tabbedPane);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package jadx.gui.ui.codearea;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
/**
|
||||
* The abstract base class for a content panel that show text based code or a.g. a resource
|
||||
|
||||
@@ -10,7 +10,7 @@ import javax.swing.border.EmptyBorder;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public class BinaryContentPanel extends AbstractCodeContentPanel {
|
||||
private final transient CodePanel textCodePanel;
|
||||
|
||||
@@ -17,9 +17,9 @@ import org.slf4j.LoggerFactory;
|
||||
import jadx.api.DecompilationMode;
|
||||
import jadx.gui.jobs.BackgroundExecutor;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.mode.JCodeMode;
|
||||
import jadx.gui.ui.panel.IViewStateSupport;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.TABBED_PANE_TRAILING_COMPONENT;
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.IViewStateSupport;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public final class CodeContentPanel extends AbstractCodeContentPanel implements IViewStateSupport {
|
||||
private static final long serialVersionUID = 5310536092010045565L;
|
||||
|
||||
@@ -50,9 +50,9 @@ import jadx.gui.logs.LogOptions;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.treemodel.JResSearchNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.panel.ProgressPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.JNodeCache;
|
||||
import jadx.gui.utils.JumpPosition;
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public abstract class ContentPanel extends JPanel {
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import javax.swing.JScrollPane;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.ui.ZoomActions;
|
||||
|
||||
public final class HtmlPanel extends ContentPanel {
|
||||
|
||||
@@ -17,8 +17,8 @@ import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.gui.treemodel.JResource;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public class ImagePanel extends ContentPanel {
|
||||
private static final long serialVersionUID = 4071356367073142688L;
|
||||
|
||||
@@ -18,7 +18,7 @@ import javax.swing.border.TitledBorder;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.treenodes.StartPageNode;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
+27
-4
@@ -1,7 +1,12 @@
|
||||
package jadx.gui.ui;
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
@@ -20,6 +25,7 @@ import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JEditableNode;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.tab.dnd.TabDndGestureListener;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
@@ -42,6 +48,9 @@ public class TabComponent extends JPanel {
|
||||
|
||||
public void loadSettings() {
|
||||
label.setFont(getLabelFont());
|
||||
if (tabbedPane.getDnd() != null) {
|
||||
tabbedPane.getDnd().loadSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private Font getLabelFont() {
|
||||
@@ -57,7 +66,7 @@ public class TabComponent extends JPanel {
|
||||
label.setFont(getLabelFont());
|
||||
String toolTip = contentPanel.getTabTooltip();
|
||||
if (toolTip != null) {
|
||||
label.setToolTipText(toolTip);
|
||||
setToolTipText(toolTip);
|
||||
}
|
||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
|
||||
label.setIcon(node.getIcon());
|
||||
@@ -93,14 +102,28 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
};
|
||||
addMouseListener(clickAdapter);
|
||||
label.addMouseListener(clickAdapter);
|
||||
closeBtn.addMouseListener(clickAdapter);
|
||||
addListenerForDnd();
|
||||
|
||||
add(label);
|
||||
add(closeBtn);
|
||||
setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
private void addListenerForDnd() {
|
||||
if (tabbedPane.getDnd() == null) {
|
||||
return;
|
||||
}
|
||||
TabComponent comp = this;
|
||||
DragGestureListener dgl = new TabDndGestureListener(tabbedPane.getDnd()) {
|
||||
@Override
|
||||
protected Point getDragOrigin(DragGestureEvent e) {
|
||||
return SwingUtilities.convertPoint(comp, e.getDragOrigin(), tabbedPane);
|
||||
}
|
||||
};
|
||||
DragSource.getDefaultDragSource()
|
||||
.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, dgl);
|
||||
}
|
||||
|
||||
private String buildTabTitle(JNode node) {
|
||||
String tabTitle;
|
||||
if (node.getRootClass() != null) {
|
||||
+17
-2
@@ -1,4 +1,4 @@
|
||||
package jadx.gui.ui;
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.KeyEventDispatcher;
|
||||
@@ -26,6 +26,7 @@ import jadx.api.metadata.annotations.NodeDeclareRef;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.codearea.AbstractCodeContentPanel;
|
||||
import jadx.gui.ui.codearea.ClassCodeContentPanel;
|
||||
@@ -35,6 +36,7 @@ import jadx.gui.ui.panel.ContentPanel;
|
||||
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;
|
||||
@@ -52,12 +54,17 @@ public class TabbedPane extends JTabbedPane {
|
||||
private transient ContentPanel curTab;
|
||||
private transient ContentPanel lastTab;
|
||||
|
||||
TabbedPane(MainWindow window) {
|
||||
private transient TabDndController dnd;
|
||||
|
||||
public TabbedPane(MainWindow window) {
|
||||
this.mainWindow = window;
|
||||
|
||||
setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
addMouseWheelListener(event -> {
|
||||
if (dnd != null && dnd.isDragging()) {
|
||||
return;
|
||||
}
|
||||
int direction = event.getWheelRotation();
|
||||
if (getTabCount() == 0 || direction == 0) {
|
||||
return;
|
||||
@@ -483,6 +490,14 @@ public class TabbedPane extends JTabbedPane {
|
||||
return FocusManager.getFocusedComp();
|
||||
}
|
||||
|
||||
public TabDndController getDnd() {
|
||||
return dnd;
|
||||
}
|
||||
|
||||
public void setDnd(TabDndController dnd) {
|
||||
this.dnd = dnd;
|
||||
}
|
||||
|
||||
private static class FocusManager implements FocusListener {
|
||||
private static final FocusManager INSTANCE = new FocusManager();
|
||||
private static @Nullable Component focusedComp;
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.dnd.DnDConstants;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.DropTarget;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.metal.MetalTabbedPaneUI;
|
||||
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
|
||||
public class TabDndController {
|
||||
|
||||
private final transient JTabbedPane pane;
|
||||
|
||||
private static final int DROP_TARGET_MARK_SIZE = 4;
|
||||
private static final int SCROLL_AREA_SIZE = 30;
|
||||
private static final int SCROLL_AREA_EXTRA = 30; // Making area with scroll buttons a bit bigger.
|
||||
private static final String ACTION_SCROLL_FORWARD = "scrollTabsForwardAction";
|
||||
private static final String ACTION_SCROLL_BACKWARD = "scrollTabsBackwardAction";
|
||||
|
||||
private final transient TabDndGhostPane tabDndGhostPane;
|
||||
protected int dragTabIndex = -1;
|
||||
|
||||
protected boolean drawGhost = true; // Semi-transparent tab copy moving along with cursor.
|
||||
protected boolean paintScrollTriggerAreas = false; // For debug purposes.
|
||||
|
||||
protected Rectangle rectBackward = new Rectangle();
|
||||
protected Rectangle rectForward = new Rectangle();
|
||||
private boolean isDragging = false;
|
||||
|
||||
public TabDndController(TabbedPane pane) {
|
||||
pane.setDnd(this);
|
||||
this.pane = pane;
|
||||
|
||||
tabDndGhostPane = new TabDndGhostPane(this);
|
||||
|
||||
new DropTarget(tabDndGhostPane, DnDConstants.ACTION_COPY_OR_MOVE, new TabDndTargetListener(this), true);
|
||||
DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(pane,
|
||||
DnDConstants.ACTION_COPY_OR_MOVE,
|
||||
new TabDndGestureListener(this));
|
||||
}
|
||||
|
||||
public static boolean isHorizontalTabPlacement(int tabPlacement) {
|
||||
return tabPlacement == JTabbedPane.TOP || tabPlacement == JTabbedPane.BOTTOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if dragging near edges and scroll to according
|
||||
* direction through programmatically clicking system's scroll buttons.
|
||||
*
|
||||
* @param glassPt Cursor position in TabbedPane coordinates.
|
||||
*/
|
||||
public void scrollIfNeeded(Point glassPt) {
|
||||
Rectangle r = getTabAreaBounds();
|
||||
boolean isHorizontal = isHorizontalTabPlacement(pane.getTabPlacement());
|
||||
|
||||
// Trying to avoid calculating two directions simultaneously. Forward first.
|
||||
if (isHorizontal) {
|
||||
rectForward.setBounds(r.x + r.width - SCROLL_AREA_SIZE - SCROLL_AREA_EXTRA,
|
||||
r.y,
|
||||
SCROLL_AREA_SIZE + SCROLL_AREA_EXTRA,
|
||||
r.height);
|
||||
} else {
|
||||
rectForward.setBounds(r.x,
|
||||
r.y + r.height - SCROLL_AREA_SIZE - SCROLL_AREA_EXTRA,
|
||||
r.width,
|
||||
SCROLL_AREA_SIZE + SCROLL_AREA_EXTRA);
|
||||
}
|
||||
rectForward = SwingUtilities.convertRectangle(pane.getParent(), rectForward, tabDndGhostPane);
|
||||
if (rectForward.contains(glassPt)) {
|
||||
clickScrollButton(ACTION_SCROLL_FORWARD);
|
||||
}
|
||||
|
||||
// Backward.
|
||||
if (isHorizontal) {
|
||||
rectBackward.setBounds(r.x, r.y, SCROLL_AREA_SIZE, r.height);
|
||||
} else {
|
||||
rectBackward.setBounds(r.x, r.y, r.width, SCROLL_AREA_SIZE);
|
||||
}
|
||||
rectBackward = SwingUtilities.convertRectangle(pane.getParent(), rectBackward, tabDndGhostPane);
|
||||
if (rectBackward.contains(glassPt)) {
|
||||
clickScrollButton(ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
}
|
||||
|
||||
private void clickScrollButton(String actionKey) {
|
||||
JButton forwardButton = null;
|
||||
JButton backwardButton = null;
|
||||
for (Component c : pane.getComponents()) {
|
||||
if (c instanceof JButton) {
|
||||
if (Objects.isNull(forwardButton)) {
|
||||
forwardButton = (JButton) c;
|
||||
} else {
|
||||
backwardButton = (JButton) c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
JButton scrollButton = ACTION_SCROLL_FORWARD.equals(actionKey) ? forwardButton : backwardButton;
|
||||
if (scrollButton != null && scrollButton.isEnabled()) {
|
||||
scrollButton.doClick();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the tab index by cursor position. If tabs first half contains the point,
|
||||
* then its index is returned. Second half means inserting at next index.
|
||||
*
|
||||
* @param glassPt Cursor position in TabbedPane coordinates.
|
||||
* @return Tab index.
|
||||
*/
|
||||
protected int getTargetTabIndex(Point glassPt) {
|
||||
Point tabPt = SwingUtilities.convertPoint(tabDndGhostPane, glassPt, pane);
|
||||
boolean isHorizontal = isHorizontalTabPlacement(pane.getTabPlacement());
|
||||
for (int i = 0; i < pane.getTabCount(); ++i) {
|
||||
Rectangle r = pane.getBoundsAt(i);
|
||||
|
||||
// First half.
|
||||
if (isHorizontal) {
|
||||
r.width = r.width / 2 + 1;
|
||||
} else {
|
||||
r.height = r.height / 2 + 1;
|
||||
}
|
||||
if (r.contains(tabPt)) {
|
||||
return i;
|
||||
}
|
||||
|
||||
// Second half.
|
||||
if (isHorizontal) {
|
||||
r.x = r.x + r.width;
|
||||
} else {
|
||||
r.y = r.y + r.height;
|
||||
}
|
||||
if (r.contains(tabPt)) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int count = pane.getTabCount();
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
Rectangle lastRect = pane.getBoundsAt(count - 1);
|
||||
Point d = isHorizontal ? new Point(1, 0) : new Point(0, 1);
|
||||
lastRect.translate(lastRect.width * d.x, lastRect.height * d.y);
|
||||
return lastRect.contains(tabPt) ? count : -1;
|
||||
}
|
||||
|
||||
protected void swapTabs(int oldIdx, int newIdx) {
|
||||
if (newIdx < 0 || oldIdx == newIdx) {
|
||||
return;
|
||||
}
|
||||
final Component cmp = pane.getComponentAt(oldIdx);
|
||||
final Component tab = pane.getTabComponentAt(oldIdx);
|
||||
final String title = pane.getTitleAt(oldIdx);
|
||||
final Icon icon = pane.getIconAt(oldIdx);
|
||||
final String tip = pane.getToolTipTextAt(oldIdx);
|
||||
final boolean isEnabled = pane.isEnabledAt(oldIdx);
|
||||
newIdx = oldIdx > newIdx ? newIdx : (newIdx - 1);
|
||||
pane.remove(oldIdx);
|
||||
pane.insertTab(title, icon, cmp, tip, newIdx);
|
||||
pane.setEnabledAt(newIdx, isEnabled);
|
||||
if (isEnabled) {
|
||||
pane.setSelectedIndex(newIdx);
|
||||
}
|
||||
pane.setTabComponentAt(newIdx, tab);
|
||||
}
|
||||
|
||||
protected void updateTargetMark(int tabIdx) {
|
||||
boolean isSideNeighbor = tabIdx < 0 || dragTabIndex == tabIdx || tabIdx == dragTabIndex + 1;
|
||||
if (isSideNeighbor) {
|
||||
tabDndGhostPane.setTargetRect(0, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
Rectangle boundsRect = pane.getBoundsAt(Math.max(0, tabIdx - 1));
|
||||
final Rectangle r = SwingUtilities.convertRectangle(pane, boundsRect, tabDndGhostPane);
|
||||
int a = Math.min(tabIdx, 1);
|
||||
if (isHorizontalTabPlacement(pane.getTabPlacement())) {
|
||||
tabDndGhostPane.setTargetRect(r.x + r.width * a - DROP_TARGET_MARK_SIZE / 2,
|
||||
r.y,
|
||||
DROP_TARGET_MARK_SIZE,
|
||||
r.height);
|
||||
} else {
|
||||
tabDndGhostPane.setTargetRect(r.x,
|
||||
r.y + r.height * a - DROP_TARGET_MARK_SIZE / 2,
|
||||
r.width,
|
||||
DROP_TARGET_MARK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initGlassPane(Point tabPt) {
|
||||
pane.getRootPane().setGlassPane(tabDndGhostPane);
|
||||
if (drawGhost) {
|
||||
Component c = pane.getTabComponentAt(dragTabIndex);
|
||||
if (c == null) {
|
||||
return;
|
||||
}
|
||||
Dimension d = c.getPreferredSize();
|
||||
switch (tabDndGhostPane.getGhostType()) {
|
||||
case IMAGE: {
|
||||
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice device = env.getDefaultScreenDevice();
|
||||
GraphicsConfiguration config = device.getDefaultConfiguration();
|
||||
BufferedImage image = config.createCompatibleImage(d.width, d.height, BufferedImage.TRANSLUCENT);
|
||||
Graphics2D g2 = image.createGraphics();
|
||||
SwingUtilities.paintComponent(g2, c, tabDndGhostPane, 0, 0, d.width, d.height);
|
||||
g2.dispose();
|
||||
tabDndGhostPane.setGhostImage(image);
|
||||
pane.setTabComponentAt(dragTabIndex, c);
|
||||
break;
|
||||
}
|
||||
case COLORFUL_RECT: {
|
||||
tabDndGhostPane.setGhostSize(d);
|
||||
break;
|
||||
}
|
||||
case NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Point glassPt = SwingUtilities.convertPoint(pane, tabPt, tabDndGhostPane);
|
||||
tabDndGhostPane.setPoint(glassPt);
|
||||
tabDndGhostPane.setVisible(true);
|
||||
}
|
||||
|
||||
protected Rectangle getTabAreaBounds() {
|
||||
Rectangle tabbedRect = pane.getBounds();
|
||||
Rectangle compRect;
|
||||
if (pane.getSelectedComponent() != null) {
|
||||
compRect = pane.getSelectedComponent().getBounds();
|
||||
} else {
|
||||
compRect = new Rectangle();
|
||||
}
|
||||
int tabPlacement = pane.getTabPlacement();
|
||||
if (isHorizontalTabPlacement(tabPlacement)) {
|
||||
tabbedRect.height = tabbedRect.height - compRect.height;
|
||||
if (tabPlacement == JTabbedPane.BOTTOM) {
|
||||
tabbedRect.y += compRect.y + compRect.height;
|
||||
}
|
||||
} else {
|
||||
tabbedRect.width = tabbedRect.width - compRect.width;
|
||||
if (tabPlacement == JTabbedPane.RIGHT) {
|
||||
tabbedRect.x += compRect.x + compRect.width;
|
||||
}
|
||||
}
|
||||
tabbedRect.grow(2, 2);
|
||||
return tabbedRect;
|
||||
}
|
||||
|
||||
public void onPaintGlassPane(Graphics2D g) {
|
||||
boolean isScrollLayout = pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT;
|
||||
if (isScrollLayout && paintScrollTriggerAreas) {
|
||||
g.setPaint(tabDndGhostPane.getColor());
|
||||
g.fill(rectBackward);
|
||||
g.fill(rectForward);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onStartDrag(Point pt) {
|
||||
setDragging(true);
|
||||
int idx = pane.indexAtLocation(pt.x, pt.y);
|
||||
int selIdx = pane.getSelectedIndex();
|
||||
boolean isTabRunsRotated =
|
||||
!(pane.getUI() instanceof MetalTabbedPaneUI) && pane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT && idx != selIdx;
|
||||
dragTabIndex = isTabRunsRotated ? selIdx : idx;
|
||||
if (dragTabIndex >= 0 && pane.isEnabledAt(dragTabIndex)) {
|
||||
initGlassPane(pt);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
tabDndGhostPane.loadSettings();
|
||||
}
|
||||
|
||||
public boolean isDragging() {
|
||||
return isDragging;
|
||||
}
|
||||
|
||||
public void setDragging(boolean dragging) {
|
||||
isDragging = dragging;
|
||||
}
|
||||
|
||||
public TabDndGhostPane getDndGhostPane() {
|
||||
return tabDndGhostPane;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.InvalidDnDOperationException;
|
||||
|
||||
public class TabDndGestureListener implements DragGestureListener {
|
||||
|
||||
private final transient TabDndController dnd;
|
||||
|
||||
public TabDndGestureListener(TabDndController dnd) {
|
||||
this.dnd = dnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragGestureRecognized(DragGestureEvent e) {
|
||||
Point tabPt = getDragOrigin(e);
|
||||
if (!dnd.onStartDrag(tabPt)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
e.startDrag(DragSource.DefaultMoveDrop, new TabDndTransferable(), new TabDndSourceListener(dnd));
|
||||
} catch (InvalidDnDOperationException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected Point getDragOrigin(DragGestureEvent e) {
|
||||
return e.getDragOrigin();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.AlphaComposite;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
public class TabDndGhostPane extends JComponent {
|
||||
|
||||
private final TabDndController dnd;
|
||||
private final Rectangle lineRect = new Rectangle();
|
||||
private final Point location = new Point();
|
||||
private transient BufferedImage ghostImage;
|
||||
private TabDndGhostType tabDndGhostType = TabDndGhostType.COLORFUL_RECT;
|
||||
private Dimension ghostSize;
|
||||
private Color ghostColor;
|
||||
private Insets insets;
|
||||
|
||||
protected TabDndGhostPane(TabDndController dnd) {
|
||||
super();
|
||||
this.dnd = dnd;
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
public void loadSettings() {
|
||||
Color systemColor = UIManager.getColor("Component.focusColor");
|
||||
Color fallbackColor = new Color(0, 100, 255);
|
||||
ghostColor = systemColor != null ? systemColor : fallbackColor;
|
||||
|
||||
Insets ins = UIManager.getInsets("TabbedPane.tabInsets");
|
||||
insets = ins != null ? ins : new Insets(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void setTargetRect(int x, int y, int width, int height) {
|
||||
lineRect.setBounds(x, y, width, height);
|
||||
}
|
||||
|
||||
public void setGhostImage(BufferedImage ghostImage) {
|
||||
this.ghostImage = ghostImage;
|
||||
}
|
||||
|
||||
public void setGhostSize(Dimension ghostSize) {
|
||||
ghostSize.setSize(ghostSize.width + insets.left + insets.right, ghostSize.height + insets.top + insets.bottom);
|
||||
this.ghostSize = ghostSize;
|
||||
}
|
||||
|
||||
public void setGhostType(TabDndGhostType tabDndGhostType) {
|
||||
this.tabDndGhostType = tabDndGhostType;
|
||||
}
|
||||
|
||||
public TabDndGhostType getGhostType() {
|
||||
return this.tabDndGhostType;
|
||||
}
|
||||
|
||||
public void setColor(Color color) {
|
||||
this.ghostColor = color;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return this.ghostColor;
|
||||
}
|
||||
|
||||
public void setPoint(Point pt) {
|
||||
this.location.setLocation(pt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean v) {
|
||||
super.setVisible(v);
|
||||
if (!v) {
|
||||
setTargetRect(0, 0, 0, 0);
|
||||
setGhostImage(null);
|
||||
setGhostSize(new Dimension());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
dnd.onPaintGlassPane(g2);
|
||||
renderMark(g2);
|
||||
renderGhost(g2);
|
||||
g2.dispose();
|
||||
}
|
||||
|
||||
private void renderGhost(Graphics2D g) {
|
||||
switch (tabDndGhostType) {
|
||||
case IMAGE: {
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f));
|
||||
if (ghostImage == null) {
|
||||
return;
|
||||
}
|
||||
double x = location.getX() - ghostImage.getWidth(this) / 2d;
|
||||
double y = location.getY() - ghostImage.getHeight(this) / 2d;
|
||||
g.drawImage(ghostImage, (int) x, (int) y, this);
|
||||
break;
|
||||
}
|
||||
case COLORFUL_RECT: {
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .2f));
|
||||
if (ghostSize == null) {
|
||||
return;
|
||||
}
|
||||
double x = location.getX() - ghostSize.getWidth() / 2d;
|
||||
double y = location.getY() - ghostSize.getHeight() / 2d;
|
||||
g.setPaint(ghostColor);
|
||||
g.fillRect((int) x, (int) y, ghostSize.width, ghostSize.height);
|
||||
break;
|
||||
}
|
||||
case NONE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void renderMark(Graphics2D g) {
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .7f));
|
||||
g.setPaint(ghostColor);
|
||||
g.fill(lineRect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
public enum TabDndGhostType {
|
||||
/**
|
||||
* Bitmap is rendered from tabs component and dragged along with cursor.
|
||||
* May be impactful on performance.
|
||||
*/
|
||||
IMAGE,
|
||||
|
||||
/**
|
||||
* Colored rect of tabs size is dragged along with cursor.
|
||||
*/
|
||||
COLORFUL_RECT,
|
||||
|
||||
/**
|
||||
* Only insert mark is rendered.
|
||||
*/
|
||||
NONE,
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.DragSourceDragEvent;
|
||||
import java.awt.dnd.DragSourceDropEvent;
|
||||
import java.awt.dnd.DragSourceEvent;
|
||||
import java.awt.dnd.DragSourceListener;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JRootPane;
|
||||
|
||||
class TabDndSourceListener implements DragSourceListener {
|
||||
|
||||
private final transient TabDndController dnd;
|
||||
|
||||
public TabDndSourceListener(TabDndController dnd) {
|
||||
this.dnd = dnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragEnter(DragSourceDragEvent e) {
|
||||
e.getDragSourceContext().setCursor(DragSource.DefaultMoveDrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragExit(DragSourceEvent e) {
|
||||
e.getDragSourceContext().setCursor(DragSource.DefaultMoveNoDrop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragOver(DragSourceDragEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragDropEnd(DragSourceDropEvent e) {
|
||||
dnd.setDragging(false);
|
||||
Component c = e.getDragSourceContext().getComponent();
|
||||
if (c instanceof JComponent) {
|
||||
JRootPane rp = ((JComponent) c).getRootPane();
|
||||
if (rp.getGlassPane() != null) {
|
||||
rp.getGlassPane().setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DragSourceDragEvent e) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.dnd.DropTargetDragEvent;
|
||||
import java.awt.dnd.DropTargetDropEvent;
|
||||
import java.awt.dnd.DropTargetEvent;
|
||||
import java.awt.dnd.DropTargetListener;
|
||||
|
||||
class TabDndTargetListener implements DropTargetListener {
|
||||
private static final Point HIDDEN_POINT = new Point(0, -1000);
|
||||
|
||||
private final transient TabDndController dnd;
|
||||
|
||||
public TabDndTargetListener(TabDndController dnd) {
|
||||
this.dnd = dnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragEnter(DropTargetDragEvent e) {
|
||||
TabDndGhostPane pane = dnd.getDndGhostPane();
|
||||
if (pane == null || e.getDropTargetContext().getComponent() != pane) {
|
||||
return;
|
||||
}
|
||||
Transferable t = e.getTransferable();
|
||||
DataFlavor[] f = e.getCurrentDataFlavors();
|
||||
if (t.isDataFlavorSupported(f[0])) {
|
||||
e.acceptDrag(e.getDropAction());
|
||||
} else {
|
||||
e.rejectDrag();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragExit(DropTargetEvent e) {
|
||||
TabDndGhostPane pane = dnd.getDndGhostPane();
|
||||
if (pane == null || e.getDropTargetContext().getComponent() != pane) {
|
||||
return;
|
||||
}
|
||||
pane.setPoint(HIDDEN_POINT);
|
||||
pane.setTargetRect(0, 0, 0, 0);
|
||||
pane.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropActionChanged(DropTargetDragEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragOver(DropTargetDragEvent e) {
|
||||
TabDndGhostPane pane = dnd.getDndGhostPane();
|
||||
if (pane == null || e.getDropTargetContext().getComponent() != pane) {
|
||||
return;
|
||||
}
|
||||
Point glassPt = e.getLocation();
|
||||
dnd.updateTargetMark(dnd.getTargetTabIndex(glassPt));
|
||||
dnd.scrollIfNeeded(glassPt); // backward and forward scrolling
|
||||
pane.setPoint(glassPt);
|
||||
pane.repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(DropTargetDropEvent e) {
|
||||
TabDndGhostPane pane = dnd.getDndGhostPane();
|
||||
if (pane == null || e.getDropTargetContext().getComponent() != pane) {
|
||||
return;
|
||||
}
|
||||
Transferable t = e.getTransferable();
|
||||
DataFlavor[] f = t.getTransferDataFlavors();
|
||||
int oldIdx = dnd.dragTabIndex;
|
||||
int newIdx = dnd.getTargetTabIndex(e.getLocation());
|
||||
if (t.isDataFlavorSupported(f[0]) && oldIdx != newIdx) {
|
||||
dnd.swapTabs(oldIdx, newIdx);
|
||||
e.dropComplete(true);
|
||||
} else {
|
||||
e.dropComplete(false);
|
||||
}
|
||||
pane.setVisible(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
* Copyright (c) 2015 TERAI Atsuhiro
|
||||
* Copyright (c) 2024 Skylot
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package jadx.gui.ui.tab.dnd;
|
||||
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
|
||||
class TabDndTransferable implements Transferable {
|
||||
private static final String NAME = "Transferable Tab";
|
||||
|
||||
@Override
|
||||
public Object getTransferData(DataFlavor flavor) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataFlavor[] getTransferDataFlavors() {
|
||||
return new DataFlavor[] { new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType, NAME) };
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataFlavorSupported(DataFlavor flavor) {
|
||||
return NAME.equals(flavor.getHumanPresentableName());
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ import javax.swing.Icon;
|
||||
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.StartPagePanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.TabbedPane;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.HtmlPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class SummaryNode extends JNode {
|
||||
|
||||
Reference in New Issue
Block a user