feat(gui): Quick Tabs Overhaul (PR #2241)
* restructure quick tabs code * code formatting * display open tabs * added bookmark tabs feature * fix tabs pin and bookmark not saved * fix NPE treeModel not initialized * Fix hardcoded strings * remove unused statement * fix NPE again * added bookmark overlay * preserve tabs order * fix context menu actions * remove unnecessary public modifier * save tabs in tabbedpane order * remove unreferenced tabs * Update jadx-gui/src/main/java/jadx/gui/ui/tab/TabComponent.java --------- Co-authored-by: skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,8 @@ public class TabStateViewAdapter {
|
||||
tvs.setView(new ViewPoint(viewState.getViewPoint()));
|
||||
tvs.setActive(viewState.isActive());
|
||||
tvs.setPinned(viewState.isPinned());
|
||||
tvs.setBookmarked(viewState.isBookmarked());
|
||||
tvs.setHidden(viewState.isHidden());
|
||||
return tvs;
|
||||
}
|
||||
|
||||
@@ -43,6 +45,8 @@ public class TabStateViewAdapter {
|
||||
EditorViewState viewState = new EditorViewState(node, tvs.getSubPath(), tvs.getCaret(), tvs.getView().toPoint());
|
||||
viewState.setActive(tvs.isActive());
|
||||
viewState.setPinned(tvs.isPinned());
|
||||
viewState.setBookmarked(tvs.isBookmarked());
|
||||
viewState.setHidden(tvs.isHidden());
|
||||
return viewState;
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to load tab state: " + tvs, e);
|
||||
|
||||
@@ -8,6 +8,8 @@ public class TabViewState {
|
||||
private ViewPoint view;
|
||||
boolean active;
|
||||
boolean pinned;
|
||||
boolean bookmarked;
|
||||
boolean hidden;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
@@ -64,4 +66,20 @@ public class TabViewState {
|
||||
public void setPinned(boolean pinned) {
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public boolean isBookmarked() {
|
||||
return bookmarked;
|
||||
}
|
||||
|
||||
public void setBookmarked(boolean bookmarked) {
|
||||
this.bookmarked = bookmarked;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public void setHidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public abstract class JNode extends DefaultMutableTreeNode implements Comparable
|
||||
return javaNode.getName();
|
||||
}
|
||||
|
||||
public boolean isPinnable() {
|
||||
public boolean supportsQuickTabs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ import jadx.gui.ui.panel.ProgressPanel;
|
||||
import jadx.gui.ui.popupmenu.RecentProjectsMenuListener;
|
||||
import jadx.gui.ui.tab.QuickTabsTree;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.tab.TabsController;
|
||||
import jadx.gui.ui.tab.dnd.TabDndController;
|
||||
import jadx.gui.ui.treenodes.StartPageNode;
|
||||
import jadx.gui.ui.treenodes.SummaryNode;
|
||||
@@ -208,6 +209,7 @@ 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;
|
||||
@@ -370,6 +372,7 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void saveProject() {
|
||||
saveOpenTabs();
|
||||
if (!project.isSaveFileSelected()) {
|
||||
saveProjectAs();
|
||||
} else {
|
||||
@@ -563,7 +566,7 @@ public class MainWindow extends JFrame {
|
||||
resetCache();
|
||||
LogCollector.getInstance().reset();
|
||||
wrapper.close();
|
||||
tabbedPane.closeAllTabs();
|
||||
tabsController.forceCloseAllTabs();
|
||||
UiUtils.resetClipboardOwner();
|
||||
System.gc();
|
||||
update();
|
||||
@@ -1342,7 +1345,8 @@ public class MainWindow extends JFrame {
|
||||
leftPane.add(bottomPane, BorderLayout.PAGE_END);
|
||||
treeSplitPane.setLeftComponent(leftPane);
|
||||
|
||||
tabbedPane = new TabbedPane(this);
|
||||
tabsController = new TabsController(this);
|
||||
tabbedPane = new TabbedPane(this, tabsController);
|
||||
tabbedPane.setMinimumSize(new Dimension(150, 150));
|
||||
new TabDndController(tabbedPane, settings);
|
||||
|
||||
@@ -1512,7 +1516,7 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
private void saveOpenTabs() {
|
||||
project.saveOpenTabs(tabbedPane.getEditorViewStates());
|
||||
project.saveOpenTabs(tabsController.getEditorViewStates());
|
||||
}
|
||||
|
||||
private void restoreOpenTabs(List<EditorViewState> openTabs) {
|
||||
@@ -1521,8 +1525,9 @@ public class MainWindow extends JFrame {
|
||||
return;
|
||||
}
|
||||
for (EditorViewState viewState : openTabs) {
|
||||
tabbedPane.restoreEditorViewState(viewState);
|
||||
tabsController.restoreEditorViewState(viewState);
|
||||
}
|
||||
tabsController.notifyRestoreEditorViewStateDone();
|
||||
}
|
||||
|
||||
private void preLoadOpenTabs(List<EditorViewState> openTabs) {
|
||||
@@ -1572,6 +1577,10 @@ public class MainWindow extends JFrame {
|
||||
return tabbedPane;
|
||||
}
|
||||
|
||||
public TabsController getTabsController() {
|
||||
return tabsController;
|
||||
}
|
||||
|
||||
public JadxSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
@@ -147,12 +147,14 @@ public final class ClassCodeContentPanel extends AbstractCodeContentPanel implem
|
||||
}
|
||||
|
||||
@Override
|
||||
public EditorViewState getEditorViewState() {
|
||||
public void saveEditorViewState(EditorViewState viewState) {
|
||||
CodePanel codePanel = (CodePanel) areaTabbedPane.getSelectedComponent();
|
||||
int caretPos = codePanel.getCodeArea().getCaretPosition();
|
||||
Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();
|
||||
String subPath = codePanel == javaCodePanel ? "java" : "smali";
|
||||
return new EditorViewState(getNode(), subPath, caretPos, viewPoint);
|
||||
viewState.setSubPath(subPath);
|
||||
viewState.setCaretPos(caretPos);
|
||||
viewState.setViewPoint(viewPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -56,10 +56,11 @@ public final class CodeContentPanel extends AbstractCodeContentPanel implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public EditorViewState getEditorViewState() {
|
||||
public void saveEditorViewState(EditorViewState viewState) {
|
||||
int caretPos = codePanel.getCodeArea().getCaretPosition();
|
||||
Point viewPoint = codePanel.getCodeScrollPane().getViewport().getViewPosition();
|
||||
return new EditorViewState(getNode(), "", caretPos, viewPoint);
|
||||
viewState.setCaretPos(caretPos);
|
||||
viewState.setViewPoint(viewPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,11 +8,19 @@ public class EditorViewState {
|
||||
public static final Point ZERO = new Point(0, 0);
|
||||
|
||||
private final JNode node;
|
||||
private final int caretPos;
|
||||
private final Point viewPoint;
|
||||
private final String subPath;
|
||||
private int caretPos;
|
||||
private Point viewPoint;
|
||||
private String subPath;
|
||||
|
||||
private boolean active;
|
||||
|
||||
private boolean pinned;
|
||||
private boolean bookmarked;
|
||||
private boolean hidden;
|
||||
|
||||
public EditorViewState(JNode node) {
|
||||
this(node, "", 0, EditorViewState.ZERO);
|
||||
}
|
||||
|
||||
public EditorViewState(JNode node, String subPath, int caretPos, Point viewPoint) {
|
||||
this.node = node;
|
||||
@@ -29,14 +37,26 @@ public class EditorViewState {
|
||||
return caretPos;
|
||||
}
|
||||
|
||||
public void setCaretPos(int caretPos) {
|
||||
this.caretPos = caretPos;
|
||||
}
|
||||
|
||||
public Point getViewPoint() {
|
||||
return viewPoint;
|
||||
}
|
||||
|
||||
public void setViewPoint(Point viewPoint) {
|
||||
this.viewPoint = viewPoint;
|
||||
}
|
||||
|
||||
public String getSubPath() {
|
||||
return subPath;
|
||||
}
|
||||
|
||||
public void setSubPath(String subPath) {
|
||||
this.subPath = subPath;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
@@ -53,6 +73,22 @@ public class EditorViewState {
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public boolean isBookmarked() {
|
||||
return bookmarked;
|
||||
}
|
||||
|
||||
public void setBookmarked(boolean bookmarked) {
|
||||
this.bookmarked = bookmarked;
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public void setHidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EditorViewState{node=" + node
|
||||
|
||||
@@ -15,11 +15,10 @@ public abstract class ContentPanel extends JPanel {
|
||||
|
||||
protected TabbedPane tabbedPane;
|
||||
protected JNode node;
|
||||
private boolean pinned;
|
||||
|
||||
protected ContentPanel(TabbedPane panel, JNode jnode) {
|
||||
protected ContentPanel(TabbedPane panel, JNode node) {
|
||||
tabbedPane = panel;
|
||||
node = jnode;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public abstract void loadSettings();
|
||||
@@ -40,27 +39,19 @@ public abstract class ContentPanel extends JPanel {
|
||||
*/
|
||||
@Nullable
|
||||
public String getTabTooltip() {
|
||||
JClass jClass = node.getRootClass();
|
||||
JClass jClass = getNode().getRootClass();
|
||||
if (jClass != null) {
|
||||
return jClass.getFullName();
|
||||
}
|
||||
return node.getName();
|
||||
return getNode().getName();
|
||||
}
|
||||
|
||||
public JadxSettings getSettings() {
|
||||
return tabbedPane.getMainWindow().getSettings();
|
||||
}
|
||||
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
public void setPinned(boolean pinned) {
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public boolean isPinnable() {
|
||||
return node.isPinnable();
|
||||
public boolean supportsQuickTabs() {
|
||||
return getNode().supportsQuickTabs();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import jadx.gui.ui.codearea.EditorViewState;
|
||||
|
||||
public interface IViewStateSupport {
|
||||
|
||||
EditorViewState getEditorViewState();
|
||||
void saveEditorViewState(EditorViewState viewState);
|
||||
|
||||
void restoreEditorViewState(EditorViewState viewState);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,29 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
|
||||
public interface ITabStatesListener {
|
||||
void onTabPinChange(JNode node, boolean pinned);
|
||||
void onTabOpen(TabBlueprint blueprint);
|
||||
|
||||
void onTabSelect(TabBlueprint blueprint);
|
||||
|
||||
void onTabClose(TabBlueprint blueprint);
|
||||
|
||||
void onTabPositionFirst(TabBlueprint blueprint);
|
||||
|
||||
void onTabPinChange(TabBlueprint blueprint);
|
||||
|
||||
void onTabBookmarkChange(TabBlueprint blueprint);
|
||||
|
||||
void onTabVisibilityChange(TabBlueprint blueprint);
|
||||
|
||||
void onTabRestore(TabBlueprint blueprint, EditorViewState viewState);
|
||||
|
||||
void onTabsRestoreDone();
|
||||
|
||||
void onTabsReorder(ArrayList<TabBlueprint> blueprints);
|
||||
|
||||
void onTabSave(TabBlueprint blueprint, EditorViewState viewState);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class QuickTabsBookmarkParentNode extends QuickTabsParentNode {
|
||||
protected QuickTabsBookmarkParentNode(TabsController tabsController) {
|
||||
super(tabsController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return NLS.str("tree.bookmarked_tabs");
|
||||
}
|
||||
|
||||
@Override
|
||||
Icon getIcon() {
|
||||
return Icons.BOOKMARK_DARK;
|
||||
}
|
||||
|
||||
@Override
|
||||
JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
|
||||
if (getChildCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem unbookmarkAll = new JMenuItem(NLS.str("tabs.unbookmark_all"));
|
||||
unbookmarkAll.addActionListener(e -> getTabsController().unbookmarkAllTabs());
|
||||
menu.add(unbookmarkAll);
|
||||
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
@@ -28,20 +28,37 @@ public class QuickTabsChildNode extends QuickTabsBaseNode {
|
||||
public JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
|
||||
JPopupMenu menu = node.onTreePopupMenu(mainWindow);
|
||||
|
||||
if (node.isPinnable()) {
|
||||
if (menu == null) {
|
||||
menu = new JPopupMenu();
|
||||
}
|
||||
|
||||
JMenuItem unpinAction = new JMenuItem(NLS.str("tabs.unpin"));
|
||||
unpinAction.addActionListener(e -> {
|
||||
TabComponent tabComponent = mainWindow.getTabbedPane().getTabComponentByNode(node);
|
||||
if (tabComponent != null) {
|
||||
tabComponent.togglePin();
|
||||
if (node.supportsQuickTabs()) {
|
||||
if (getParent() instanceof QuickTabsPinParentNode) {
|
||||
if (menu == null) {
|
||||
menu = new JPopupMenu();
|
||||
}
|
||||
});
|
||||
menu.add(unpinAction, 0);
|
||||
menu.add(new JPopupMenu.Separator(), 1);
|
||||
|
||||
JMenuItem closeAction = new JMenuItem(NLS.str("tabs.close"));
|
||||
closeAction.addActionListener(e -> mainWindow.getTabsController().closeTab(node, true));
|
||||
menu.add(closeAction, 0);
|
||||
menu.add(new JPopupMenu.Separator(), 1);
|
||||
}
|
||||
if (getParent() instanceof QuickTabsPinParentNode) {
|
||||
if (menu == null) {
|
||||
menu = new JPopupMenu();
|
||||
}
|
||||
|
||||
JMenuItem unpinAction = new JMenuItem(NLS.str("tabs.unpin"));
|
||||
unpinAction.addActionListener(e -> mainWindow.getTabsController().setTabPinned(node, false));
|
||||
menu.add(unpinAction, 0);
|
||||
menu.add(new JPopupMenu.Separator(), 1);
|
||||
}
|
||||
if (getParent() instanceof QuickTabsBookmarkParentNode) {
|
||||
if (menu == null) {
|
||||
menu = new JPopupMenu();
|
||||
}
|
||||
|
||||
JMenuItem unbookmarkAction = new JMenuItem(NLS.str("tabs.unbookmark"));
|
||||
unbookmarkAction.addActionListener(e -> mainWindow.getTabsController().setTabBookmarked(node, false));
|
||||
menu.add(unbookmarkAction, 0);
|
||||
menu.add(new JPopupMenu.Separator(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
return menu;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class QuickTabsOpenParentNode extends QuickTabsParentNode {
|
||||
protected QuickTabsOpenParentNode(TabsController tabsController) {
|
||||
super(tabsController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return NLS.str("tree.open_tabs");
|
||||
}
|
||||
|
||||
@Override
|
||||
Icon getIcon() {
|
||||
return Icons.FOLDER;
|
||||
}
|
||||
|
||||
@Override
|
||||
JPopupMenu onTreePopupMenu(MainWindow mainWindow) {
|
||||
if (getChildCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem closeAll = new JMenuItem(NLS.str("tabs.closeAll"));
|
||||
closeAll.addActionListener(e -> getTabsController().closeAllTabs(true));
|
||||
menu.add(closeAll);
|
||||
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
@@ -9,38 +9,44 @@ import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
|
||||
abstract class QuickTabsParentNode extends QuickTabsBaseNode {
|
||||
private final TabbedPane tabbedPane;
|
||||
private final TabsController tabsController;
|
||||
private final Map<JNode, QuickTabsChildNode> childrenMap = new HashMap<>();
|
||||
|
||||
protected QuickTabsParentNode(TabbedPane tabbedPane) {
|
||||
protected QuickTabsParentNode(TabsController tabsController) {
|
||||
super();
|
||||
|
||||
this.tabbedPane = tabbedPane;
|
||||
this.tabsController = tabsController;
|
||||
}
|
||||
|
||||
public void addJNode(JNode node) {
|
||||
public boolean addJNode(JNode node) {
|
||||
if (childrenMap.containsKey(node)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
QuickTabsChildNode childNode = new QuickTabsChildNode(node);
|
||||
childrenMap.put(node, childNode);
|
||||
add(childNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeJNode(JNode node) {
|
||||
public boolean removeJNode(JNode node) {
|
||||
QuickTabsChildNode childNode = childrenMap.remove(node);
|
||||
if (childNode == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
remove(childNode);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeAllNodes() {
|
||||
removeAllChildren();
|
||||
}
|
||||
|
||||
public QuickTabsChildNode getQuickTabsNode(JNode node) {
|
||||
return childrenMap.get(node);
|
||||
}
|
||||
|
||||
public TabbedPane getTabbedPane() {
|
||||
return tabbedPane;
|
||||
public TabsController getTabsController() {
|
||||
return tabsController;
|
||||
}
|
||||
|
||||
abstract String getTitle();
|
||||
|
||||
@@ -9,8 +9,8 @@ import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class QuickTabsPinParentNode extends QuickTabsParentNode {
|
||||
protected QuickTabsPinParentNode(TabbedPane tabbedPane) {
|
||||
super(tabbedPane);
|
||||
protected QuickTabsPinParentNode(TabsController tabsController) {
|
||||
super(tabsController);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,7 +31,7 @@ public class QuickTabsPinParentNode extends QuickTabsParentNode {
|
||||
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
JMenuItem unpinAll = new JMenuItem(NLS.str("tabs.unpin_all"));
|
||||
unpinAll.addActionListener(e -> getTabbedPane().unpinAll());
|
||||
unpinAll.addActionListener(e -> getTabsController().unpinAllTabs());
|
||||
menu.add(unpinAll);
|
||||
|
||||
return menu;
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JTree;
|
||||
@@ -19,23 +20,29 @@ 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 {
|
||||
private final MainWindow mainWindow;
|
||||
private final DefaultTreeModel treeModel;
|
||||
|
||||
private final QuickTabsParentNode openParentNode;
|
||||
private final QuickTabsParentNode pinParentNode;
|
||||
private final QuickTabsParentNode bookmarkParentNode;
|
||||
|
||||
public QuickTabsTree(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
|
||||
mainWindow.getTabbedPane().addTabStateListener(this);
|
||||
mainWindow.getTabsController().addListener(this);
|
||||
|
||||
Root root = new Root();
|
||||
pinParentNode = new QuickTabsPinParentNode(mainWindow.getTabbedPane());
|
||||
fillPinParentNode();
|
||||
pinParentNode = new QuickTabsPinParentNode(mainWindow.getTabsController());
|
||||
openParentNode = new QuickTabsOpenParentNode(mainWindow.getTabsController());
|
||||
bookmarkParentNode = new QuickTabsBookmarkParentNode(mainWindow.getTabsController());
|
||||
root.add(openParentNode);
|
||||
root.add(pinParentNode);
|
||||
root.add(bookmarkParentNode);
|
||||
|
||||
treeModel = new DefaultTreeModel(root);
|
||||
setModel(treeModel);
|
||||
@@ -69,6 +76,10 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
});
|
||||
|
||||
loadSettings();
|
||||
|
||||
fillOpenParentNode();
|
||||
fillPinParentNode();
|
||||
fillBookmarkParentNode();
|
||||
}
|
||||
|
||||
private void triggerRightClickAction(MouseEvent e) {
|
||||
@@ -91,14 +102,50 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
|
||||
if (pressedNode instanceof QuickTabsChildNode) {
|
||||
QuickTabsChildNode childNode = (QuickTabsChildNode) pressedNode;
|
||||
return mainWindow.getTabbedPane().showNode(childNode.getJNode());
|
||||
mainWindow.getTabsController().selectTab(childNode.getJNode());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void fillOpenParentNode() {
|
||||
mainWindow.getTabsController().getOpenTabs().forEach(this::onTabOpen);
|
||||
}
|
||||
|
||||
private void fillPinParentNode() {
|
||||
mainWindow.getTabbedPane().getPinnedTabs().forEach((contentPanel) -> pinParentNode.addJNode(contentPanel.getNode()));
|
||||
mainWindow.getTabsController().getPinnedTabs().forEach(this::onTabPinChange);
|
||||
}
|
||||
|
||||
private void fillBookmarkParentNode() {
|
||||
mainWindow.getTabsController().getBookmarkedTabs().forEach(this::onTabBookmarkChange);
|
||||
}
|
||||
|
||||
private void clearParentNode(QuickTabsParentNode parentNode) {
|
||||
int[] childIndices = new int[parentNode.getChildCount()];
|
||||
Object[] objects = new Object[parentNode.getChildCount()];
|
||||
for (int i = 0; i < childIndices.length; i++) {
|
||||
childIndices[i] = i;
|
||||
objects[i] = parentNode.getChildAt(i);
|
||||
}
|
||||
parentNode.removeAllNodes();
|
||||
treeModel.nodesWereRemoved(parentNode, childIndices, objects);
|
||||
}
|
||||
|
||||
private void addJNode(QuickTabsParentNode parentNode, JNode node) {
|
||||
if (parentNode.addJNode(node)) {
|
||||
treeModel.nodesWereInserted(parentNode, new int[] { parentNode.getChildCount() - 1 });
|
||||
}
|
||||
}
|
||||
|
||||
private void removeJNode(QuickTabsParentNode parentNode, JNode node) {
|
||||
QuickTabsChildNode child = parentNode.getQuickTabsNode(node);
|
||||
if (child != null) {
|
||||
int removedIndex = parentNode.getIndex(child);
|
||||
if (parentNode.removeJNode(node)) {
|
||||
treeModel.nodesWereRemoved(parentNode, new int[] { removedIndex }, new Object[] { child });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,22 +155,10 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
if (selectedNode instanceof QuickTabsChildNode) {
|
||||
QuickTabsChildNode childNode = (QuickTabsChildNode) selectedNode;
|
||||
JNode jNode = childNode.getJNode();
|
||||
TabbedPane tabbedPane = mainWindow.getTabbedPane();
|
||||
tabbedPane.selectTab(tabbedPane.getTabByNode(jNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPinChange(JNode node, boolean pinned) {
|
||||
if (pinned) {
|
||||
pinParentNode.addJNode(node);
|
||||
treeModel.nodesWereInserted(pinParentNode, new int[] { pinParentNode.getChildCount() - 1 });
|
||||
} else {
|
||||
QuickTabsChildNode child = pinParentNode.getQuickTabsNode(node);
|
||||
int removedIndex = pinParentNode.getIndex(child);
|
||||
pinParentNode.removeJNode(node);
|
||||
treeModel.nodesWereRemoved(pinParentNode, new int[] { removedIndex }, new Object[] { child });
|
||||
TabsController tabsController = mainWindow.getTabsController();
|
||||
tabsController.selectTab(jNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +170,81 @@ public class QuickTabsTree extends JTree implements ITabStatesListener, TreeSele
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
mainWindow.getTabbedPane().removeTabStateListener(this);
|
||||
mainWindow.getTabsController().removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabOpen(TabBlueprint blueprint) {
|
||||
if (!blueprint.isHidden() && blueprint.getNode().supportsQuickTabs()) {
|
||||
addJNode(openParentNode, blueprint.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabClose(TabBlueprint blueprint) {
|
||||
removeJNode(openParentNode, blueprint.getNode());
|
||||
removeJNode(pinParentNode, blueprint.getNode());
|
||||
removeJNode(bookmarkParentNode, blueprint.getNode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPositionFirst(TabBlueprint blueprint) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPinChange(TabBlueprint blueprint) {
|
||||
JNode node = blueprint.getNode();
|
||||
if (blueprint.isPinned()) {
|
||||
addJNode(pinParentNode, node);
|
||||
} else {
|
||||
removeJNode(pinParentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabBookmarkChange(TabBlueprint blueprint) {
|
||||
JNode node = blueprint.getNode();
|
||||
if (blueprint.isBookmarked()) {
|
||||
addJNode(bookmarkParentNode, node);
|
||||
} else {
|
||||
removeJNode(bookmarkParentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabVisibilityChange(TabBlueprint blueprint) {
|
||||
JNode node = blueprint.getNode();
|
||||
if (!blueprint.isHidden()) {
|
||||
addJNode(openParentNode, node);
|
||||
} else {
|
||||
removeJNode(openParentNode, node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsRestoreDone() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsReorder(ArrayList<TabBlueprint> blueprints) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
|
||||
}
|
||||
|
||||
private class Root extends DefaultMutableTreeNode {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
|
||||
public class TabBlueprint {
|
||||
private final JNode node;
|
||||
private boolean pinned;
|
||||
private boolean bookmarked;
|
||||
private boolean hidden;
|
||||
|
||||
public TabBlueprint(JNode node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public JNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
public void setPinned(boolean pinned) {
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public boolean isBookmarked() {
|
||||
return bookmarked;
|
||||
}
|
||||
|
||||
public void setBookmarked(boolean bookmarked) {
|
||||
this.bookmarked = bookmarked;
|
||||
}
|
||||
|
||||
public boolean supportsQuickTabs() {
|
||||
return node.supportsQuickTabs();
|
||||
}
|
||||
|
||||
public boolean isReferenced() {
|
||||
return isBookmarked();
|
||||
}
|
||||
|
||||
public boolean isHidden() {
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public void setHidden(boolean hidden) {
|
||||
this.hidden = hidden;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.basic.BasicButtonUI;
|
||||
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JEditableNode;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
@@ -28,6 +29,7 @@ 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.OverlayIcon;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
import jadx.gui.utils.ui.NodeLabel;
|
||||
|
||||
@@ -35,14 +37,17 @@ public class TabComponent extends JPanel {
|
||||
private static final long serialVersionUID = -8147035487543610321L;
|
||||
|
||||
private final TabbedPane tabbedPane;
|
||||
private final TabsController tabsController;
|
||||
private final ContentPanel contentPanel;
|
||||
|
||||
private OverlayIcon icon;
|
||||
private JLabel label;
|
||||
private JButton pinBtn;
|
||||
private JButton closeBtn;
|
||||
|
||||
public TabComponent(TabbedPane tabbedPane, ContentPanel contentPanel) {
|
||||
this.tabbedPane = tabbedPane;
|
||||
this.tabsController = tabbedPane.getMainWindow().getTabsController();
|
||||
this.contentPanel = contentPanel;
|
||||
|
||||
init();
|
||||
@@ -56,14 +61,16 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
|
||||
private Font getLabelFont() {
|
||||
return tabbedPane.getMainWindow().getSettings().getFont().deriveFont(Font.BOLD);
|
||||
return tabsController.getMainWindow().getSettings().getFont().deriveFont(Font.BOLD);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
setOpaque(false);
|
||||
|
||||
JNode node = contentPanel.getNode();
|
||||
JNode node = getNode();
|
||||
icon = new OverlayIcon(node.getIcon());
|
||||
|
||||
label = new NodeLabel(buildTabTitle(node), node.disableHtml());
|
||||
label.setFont(getLabelFont());
|
||||
String toolTip = contentPanel.getTabTooltip();
|
||||
@@ -71,7 +78,8 @@ public class TabComponent extends JPanel {
|
||||
setToolTipText(toolTip);
|
||||
}
|
||||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
|
||||
label.setIcon(node.getIcon());
|
||||
label.setIcon(icon);
|
||||
updateBookmarkIcon();
|
||||
if (node instanceof JEditableNode) {
|
||||
((JEditableNode) node).addChangeListener(c -> label.setText(buildTabTitle(node)));
|
||||
}
|
||||
@@ -97,20 +105,20 @@ public class TabComponent extends JPanel {
|
||||
closeBtn.setFocusable(false);
|
||||
closeBtn.setBorder(null);
|
||||
closeBtn.setBorderPainted(false);
|
||||
closeBtn.addActionListener(e -> tabbedPane.closeCodePanel(contentPanel, true));
|
||||
closeBtn.addActionListener(e -> {
|
||||
tabsController.closeTab(node, true);
|
||||
});
|
||||
|
||||
MouseAdapter clickAdapter = new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (SwingUtilities.isMiddleMouseButton(e)) {
|
||||
tabbedPane.closeCodePanel(contentPanel, true);
|
||||
tabsController.closeTab(node, true);
|
||||
} else if (SwingUtilities.isRightMouseButton(e)) {
|
||||
JPopupMenu menu = createTabPopupMenu(contentPanel);
|
||||
JPopupMenu menu = createTabPopupMenu();
|
||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
||||
} else if (SwingUtilities.isLeftMouseButton(e)) {
|
||||
if (tabbedPane.getSelectedComponent() != contentPanel) {
|
||||
tabbedPane.selectTab(contentPanel);
|
||||
}
|
||||
tabsController.selectTab(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -123,15 +131,14 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
|
||||
public void updateCloseOrPinButton() {
|
||||
if (contentPanel.isPinned()) {
|
||||
if (getBlueprint().isPinned()) {
|
||||
if (closeBtn.isShowing()) {
|
||||
remove(closeBtn);
|
||||
}
|
||||
if (!pinBtn.isShowing()) {
|
||||
add(pinBtn);
|
||||
}
|
||||
}
|
||||
if (!contentPanel.isPinned()) {
|
||||
} else {
|
||||
if (pinBtn.isShowing()) {
|
||||
remove(pinBtn);
|
||||
}
|
||||
@@ -141,13 +148,27 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
public void togglePin() {
|
||||
contentPanel.setPinned(!contentPanel.isPinned());
|
||||
updateCloseOrPinButton();
|
||||
if (contentPanel.isPinned()) {
|
||||
tabbedPane.advanceTab(this);
|
||||
public void updateBookmarkIcon() {
|
||||
icon.clear();
|
||||
|
||||
if (getBlueprint().isBookmarked()) {
|
||||
icon.add(Icons.BOOKMARK_OVERLAY_DARK);
|
||||
}
|
||||
tabbedPane.notifyTabStateChange(this, true);
|
||||
label.repaint();
|
||||
}
|
||||
|
||||
private void togglePin() {
|
||||
boolean pinned = !getBlueprint().isPinned();
|
||||
tabsController.setTabPinned(getNode(), pinned);
|
||||
|
||||
if (pinned) {
|
||||
tabsController.setTabPositionFirst(getNode());
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleBookmark() {
|
||||
boolean bookmarked = !getBlueprint().isBookmarked();
|
||||
tabsController.setTabBookmarked(getNode(), bookmarked);
|
||||
}
|
||||
|
||||
private void addListenerForDnd() {
|
||||
@@ -180,7 +201,7 @@ public class TabComponent extends JPanel {
|
||||
return tabTitle;
|
||||
}
|
||||
|
||||
private JPopupMenu createTabPopupMenu(final ContentPanel contentPanel) {
|
||||
private JPopupMenu createTabPopupMenu() {
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
String nodeFullName = getNodeFullName(contentPanel);
|
||||
@@ -191,51 +212,63 @@ public class TabComponent extends JPanel {
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
if (contentPanel.isPinnable()) {
|
||||
String pinTitle = contentPanel.isPinned() ? NLS.str("tabs.unpin") : NLS.str("tabs.pin");
|
||||
if (getBlueprint().supportsQuickTabs()) {
|
||||
String pinTitle = getBlueprint().isPinned() ? NLS.str("tabs.unpin") : NLS.str("tabs.pin");
|
||||
JMenuItem pinTab = new JMenuItem(pinTitle);
|
||||
pinTab.addActionListener(e -> togglePin());
|
||||
menu.add(pinTab);
|
||||
|
||||
JMenuItem unpinAll = new JMenuItem(NLS.str("tabs.unpin_all"));
|
||||
unpinAll.addActionListener(e -> tabbedPane.unpinAll());
|
||||
unpinAll.addActionListener(e -> tabsController.unpinAllTabs());
|
||||
menu.add(unpinAll);
|
||||
|
||||
String bookmarkTitle = getBlueprint().isBookmarked() ? NLS.str("tabs.unbookmark") : NLS.str("tabs.bookmark");
|
||||
JMenuItem bookmarkTab = new JMenuItem(bookmarkTitle);
|
||||
bookmarkTab.addActionListener(e -> toggleBookmark());
|
||||
menu.add(bookmarkTab);
|
||||
|
||||
JMenuItem unbookmarkAll = new JMenuItem(NLS.str("tabs.unbookmark_all"));
|
||||
unbookmarkAll.addActionListener(e -> tabsController.unbookmarkAllTabs());
|
||||
menu.add(unbookmarkAll);
|
||||
}
|
||||
|
||||
JMenuItem closeTab = new JMenuItem(NLS.str("tabs.close"));
|
||||
closeTab.addActionListener(e -> tabbedPane.closeCodePanel(contentPanel, true));
|
||||
if (contentPanel.isPinned()) {
|
||||
closeTab.addActionListener(e -> tabsController.closeTab(getNode(), true));
|
||||
if (getBlueprint().isPinned()) {
|
||||
closeTab.setEnabled(false);
|
||||
}
|
||||
menu.add(closeTab);
|
||||
|
||||
List<ContentPanel> tabs = tabbedPane.getTabs();
|
||||
List<TabBlueprint> tabs = tabsController.getOpenTabs();
|
||||
if (tabs.size() > 1) {
|
||||
JMenuItem closeOther = new JMenuItem(NLS.str("tabs.closeOthers"));
|
||||
closeOther.addActionListener(e -> {
|
||||
for (ContentPanel panel : tabs) {
|
||||
if (panel != contentPanel) {
|
||||
tabbedPane.closeCodePanel(panel, true);
|
||||
for (TabBlueprint tab : tabs) {
|
||||
if (tab != getBlueprint()) {
|
||||
tabsController.closeTab(getNode(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
menu.add(closeOther);
|
||||
|
||||
JMenuItem closeAll = new JMenuItem(NLS.str("tabs.closeAll"));
|
||||
closeAll.addActionListener(e -> tabbedPane.closeAllTabs(true));
|
||||
closeAll.addActionListener(e -> tabsController.closeAllTabs(true));
|
||||
menu.add(closeAll);
|
||||
|
||||
if (contentPanel != ListUtils.last(tabs)) {
|
||||
// We don't use TabsController here because tabs position is
|
||||
// specific to TabbedPane
|
||||
List<ContentPanel> contentPanels = tabbedPane.getTabs();
|
||||
if (contentPanel != ListUtils.last(contentPanels)) {
|
||||
JMenuItem closeAllRight = new JMenuItem(NLS.str("tabs.closeAllRight"));
|
||||
closeAllRight.addActionListener(e -> {
|
||||
boolean pastCurrentPanel = false;
|
||||
for (ContentPanel panel : tabs) {
|
||||
for (ContentPanel panel : contentPanels) {
|
||||
if (!pastCurrentPanel) {
|
||||
if (panel == contentPanel) {
|
||||
pastCurrentPanel = true;
|
||||
}
|
||||
} else {
|
||||
tabbedPane.closeCodePanel(panel, true);
|
||||
tabsController.closeTab(panel.getNode(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -243,15 +276,15 @@ public class TabComponent extends JPanel {
|
||||
}
|
||||
menu.addSeparator();
|
||||
|
||||
ContentPanel selectedContentPanel = tabbedPane.getSelectedContentPanel();
|
||||
for (ContentPanel tab : tabs) {
|
||||
if (tab == selectedContentPanel) {
|
||||
TabBlueprint selectedTab = tabsController.getSelectedTab();
|
||||
for (TabBlueprint tab : tabs) {
|
||||
if (tab == selectedTab) {
|
||||
continue;
|
||||
}
|
||||
JNode node = tab.getNode();
|
||||
final String clsName = node.makeLongString();
|
||||
JMenuItem item = new JMenuItem(clsName);
|
||||
item.addActionListener(e -> tabbedPane.setSelectedComponent(tab));
|
||||
item.addActionListener(e -> tabsController.selectTab(node));
|
||||
item.setIcon(node.getIcon());
|
||||
menu.add(item);
|
||||
}
|
||||
@@ -271,4 +304,17 @@ public class TabComponent extends JPanel {
|
||||
public ContentPanel getContentPanel() {
|
||||
return contentPanel;
|
||||
}
|
||||
|
||||
public TabBlueprint getBlueprint() {
|
||||
TabBlueprint blueprint = tabsController.getTabByNode(contentPanel.getNode());
|
||||
if (blueprint == null) {
|
||||
throw new JadxRuntimeException("TabComponent does not have a corresponding TabBlueprint");
|
||||
}
|
||||
|
||||
return blueprint;
|
||||
}
|
||||
|
||||
public JNode getNode() {
|
||||
return contentPanel.getNode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -42,15 +43,15 @@ import jadx.gui.utils.JumpPosition;
|
||||
import jadx.gui.utils.NLS;
|
||||
import jadx.gui.utils.UiUtils;
|
||||
|
||||
public class TabbedPane extends JTabbedPane {
|
||||
public class TabbedPane extends JTabbedPane implements ITabStatesListener {
|
||||
private static final long serialVersionUID = -8833600618794570904L;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TabbedPane.class);
|
||||
|
||||
private final transient MainWindow mainWindow;
|
||||
private final transient TabsController controller;
|
||||
private final transient Map<JNode, ContentPanel> tabsMap = new HashMap<>();
|
||||
|
||||
private final ArrayList<ITabStatesListener> tabStatesListeners = new ArrayList<>();
|
||||
private final transient JumpManager jumps = new JumpManager();
|
||||
|
||||
private transient ContentPanel curTab;
|
||||
@@ -58,9 +59,11 @@ public class TabbedPane extends JTabbedPane {
|
||||
|
||||
private transient TabDndController dnd;
|
||||
|
||||
public TabbedPane(MainWindow window) {
|
||||
public TabbedPane(MainWindow window, TabsController controller) {
|
||||
this.mainWindow = window;
|
||||
this.controller = controller;
|
||||
|
||||
controller.addListener(this);
|
||||
setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
||||
|
||||
addMouseWheelListener(event -> {
|
||||
@@ -299,10 +302,7 @@ public class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
|
||||
public void selectTab(ContentPanel contentPanel) {
|
||||
setSelectedComponent(contentPanel);
|
||||
if (mainWindow.getSettings().isAlwaysSelectOpened()) {
|
||||
mainWindow.syncWithEditor();
|
||||
}
|
||||
controller.selectTab(contentPanel.getNode());
|
||||
}
|
||||
|
||||
public void smaliJump(JClass cls, int pos, boolean debugMode) {
|
||||
@@ -334,44 +334,6 @@ public class TabbedPane extends JTabbedPane {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<EditorViewState> getEditorViewStates() {
|
||||
ContentPanel selected = getSelectedContentPanel();
|
||||
List<EditorViewState> states = new ArrayList<>();
|
||||
for (ContentPanel panel : getTabs()) {
|
||||
EditorViewState viewState;
|
||||
if (panel instanceof IViewStateSupport) {
|
||||
viewState = ((IViewStateSupport) panel).getEditorViewState();
|
||||
} else {
|
||||
viewState = new EditorViewState(panel.getNode(), "", 0, EditorViewState.ZERO);
|
||||
}
|
||||
viewState.setActive(panel == selected);
|
||||
viewState.setPinned(panel.isPinned());
|
||||
states.add(viewState);
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public void restoreEditorViewState(EditorViewState viewState) {
|
||||
ContentPanel contentPanel = getContentPanel(viewState.getNode());
|
||||
if (contentPanel instanceof IViewStateSupport) {
|
||||
((IViewStateSupport) contentPanel).restoreEditorViewState(viewState);
|
||||
}
|
||||
if (viewState.isActive()) {
|
||||
setSelectedComponent(contentPanel);
|
||||
}
|
||||
if (contentPanel != null) {
|
||||
boolean pinned = viewState.isPinned();
|
||||
contentPanel.setPinned(pinned);
|
||||
Component component = getTabComponentAt(indexOfComponent(contentPanel));
|
||||
if (component instanceof TabComponent) {
|
||||
TabComponent tabComponent = (TabComponent) component;
|
||||
JNode node = contentPanel.getNode();
|
||||
tabComponent.updateCloseOrPinButton();
|
||||
tabStatesListeners.forEach(l -> l.onTabPinChange(node, pinned));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void navBack() {
|
||||
if (jumps.size() > 1) {
|
||||
jumps.updateCurPosition(getCurrentPosition());
|
||||
@@ -404,36 +366,7 @@ public class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
|
||||
public void closeCodePanel(ContentPanel contentPanel, boolean considerPins) {
|
||||
if (considerPins && contentPanel.isPinned()) {
|
||||
return;
|
||||
}
|
||||
|
||||
tabsMap.remove(contentPanel.getNode());
|
||||
remove(contentPanel);
|
||||
contentPanel.dispose();
|
||||
}
|
||||
|
||||
public void advanceTab(TabComponent tabComponent) {
|
||||
remove(tabComponent.getContentPanel());
|
||||
add(tabComponent.getContentPanel(), 0);
|
||||
setTabComponentAt(0, tabComponent);
|
||||
selectTab(tabComponent.getContentPanel());
|
||||
}
|
||||
|
||||
public void notifyTabStateChange(TabComponent tabComponent, boolean pinChange) {
|
||||
if (pinChange) {
|
||||
JNode node = tabComponent.getContentPanel().getNode();
|
||||
boolean pinned = tabComponent.getContentPanel().isPinned();
|
||||
tabStatesListeners.forEach(l -> l.onTabPinChange(node, pinned));
|
||||
}
|
||||
}
|
||||
|
||||
public void addTabStateListener(ITabStatesListener listener) {
|
||||
tabStatesListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeTabStateListener(ITabStatesListener listener) {
|
||||
tabStatesListeners.remove(listener);
|
||||
controller.closeTab(contentPanel.getNode(), considerPins);
|
||||
}
|
||||
|
||||
public List<ContentPanel> getTabs() {
|
||||
@@ -444,28 +377,6 @@ public class TabbedPane extends JTabbedPane {
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<ContentPanel> getPinnedTabs() {
|
||||
List<ContentPanel> list = new ArrayList<>(getTabCount());
|
||||
for (int i = 0; i < getTabCount(); i++) {
|
||||
ContentPanel contentPanel = (ContentPanel) getComponentAt(i);
|
||||
if (contentPanel.isPinned()) {
|
||||
list.add(contentPanel);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<TabComponent> getPinnedTabComponents() {
|
||||
List<TabComponent> list = new ArrayList<>(getTabCount());
|
||||
for (int i = 0; i < getTabCount(); i++) {
|
||||
ContentPanel contentPanel = (ContentPanel) getComponentAt(i);
|
||||
if (contentPanel.isPinned()) {
|
||||
list.add((TabComponent) getTabComponentAt(i));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public @Nullable ContentPanel getTabByNode(JNode node) {
|
||||
return tabsMap.get(node);
|
||||
}
|
||||
@@ -480,21 +391,8 @@ public class TabbedPane extends JTabbedPane {
|
||||
}
|
||||
|
||||
private @Nullable ContentPanel getContentPanel(JNode node) {
|
||||
ContentPanel panel = getTabByNode(node);
|
||||
if (panel != null) {
|
||||
return panel;
|
||||
}
|
||||
ContentPanel newPanel = node.getContentPanel(this);
|
||||
if (newPanel == null) {
|
||||
return null;
|
||||
}
|
||||
FocusManager.listen(newPanel);
|
||||
addContentPanel(newPanel);
|
||||
return newPanel;
|
||||
}
|
||||
|
||||
public void unpinAll() {
|
||||
getPinnedTabComponents().forEach(TabComponent::togglePin);
|
||||
controller.openTab(node);
|
||||
return getTabByNode(node);
|
||||
}
|
||||
|
||||
public void refresh(JNode node) {
|
||||
@@ -517,19 +415,18 @@ public class TabbedPane extends JTabbedPane {
|
||||
continue;
|
||||
}
|
||||
ContentPanel oldPanel = (ContentPanel) getComponentAt(i);
|
||||
EditorViewState viewState = null;
|
||||
if (oldPanel instanceof IViewStateSupport) {
|
||||
viewState = ((IViewStateSupport) oldPanel).getEditorViewState();
|
||||
TabBlueprint tab = controller.getTabByNode(oldPanel.getNode());
|
||||
if (tab == null) {
|
||||
continue;
|
||||
}
|
||||
EditorViewState viewState = controller.getEditorViewState(tab);
|
||||
JNode node = oldPanel.getNode();
|
||||
ContentPanel panel = node.getContentPanel(this);
|
||||
if (viewState != null && panel instanceof IViewStateSupport) {
|
||||
((IViewStateSupport) panel).restoreEditorViewState(viewState);
|
||||
}
|
||||
FocusManager.listen(panel);
|
||||
tabsMap.put(node, panel);
|
||||
setComponentAt(i, panel);
|
||||
setTabComponentAt(i, makeTabComponent(panel));
|
||||
controller.restoreEditorViewState(viewState);
|
||||
}
|
||||
fireStateChanged();
|
||||
}
|
||||
@@ -582,6 +479,123 @@ public class TabbedPane extends JTabbedPane {
|
||||
this.dnd = dnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabOpen(TabBlueprint blueprint) {
|
||||
if (blueprint.isHidden()) {
|
||||
return;
|
||||
}
|
||||
ContentPanel newPanel = blueprint.getNode().getContentPanel(this);
|
||||
FocusManager.listen(newPanel);
|
||||
addContentPanel(newPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelect(TabBlueprint blueprint) {
|
||||
ContentPanel contentPanel = getContentPanel(blueprint.getNode());
|
||||
setSelectedComponent(contentPanel);
|
||||
if (mainWindow.getSettings().isAlwaysSelectOpened()) {
|
||||
mainWindow.syncWithEditor();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabClose(TabBlueprint blueprint) {
|
||||
ContentPanel contentPanel = getTabByNode(blueprint.getNode());
|
||||
if (contentPanel == null) {
|
||||
return;
|
||||
}
|
||||
tabsMap.remove(contentPanel.getNode());
|
||||
remove(contentPanel);
|
||||
contentPanel.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPositionFirst(TabBlueprint blueprint) {
|
||||
ContentPanel contentPanel = getTabByNode(blueprint.getNode());
|
||||
if (contentPanel == null) {
|
||||
return;
|
||||
}
|
||||
setTabPosition(contentPanel, 0);
|
||||
}
|
||||
|
||||
private void setTabPosition(ContentPanel contentPanel, int position) {
|
||||
TabComponent tabComponent = getTabComponentByNode(contentPanel.getNode());
|
||||
if (tabComponent == null) {
|
||||
return;
|
||||
}
|
||||
remove(contentPanel);
|
||||
add(contentPanel, position);
|
||||
setTabComponentAt(position, tabComponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabPinChange(TabBlueprint blueprint) {
|
||||
TabComponent tabComponent = getTabComponentByNode(blueprint.getNode());
|
||||
if (tabComponent == null) {
|
||||
return;
|
||||
}
|
||||
tabComponent.updateCloseOrPinButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabBookmarkChange(TabBlueprint blueprint) {
|
||||
TabComponent tabComponent = getTabComponentByNode(blueprint.getNode());
|
||||
if (tabComponent == null) {
|
||||
return;
|
||||
}
|
||||
tabComponent.updateBookmarkIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabVisibilityChange(TabBlueprint blueprint) {
|
||||
if (!blueprint.isHidden() && !tabsMap.containsKey(blueprint.getNode())) {
|
||||
onTabOpen(blueprint);
|
||||
}
|
||||
if (blueprint.isHidden() && tabsMap.containsKey(blueprint.getNode())) {
|
||||
onTabClose(blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabRestore(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
ContentPanel contentPanel = getTabByNode(blueprint.getNode());
|
||||
if (contentPanel instanceof IViewStateSupport) {
|
||||
((IViewStateSupport) contentPanel).restoreEditorViewState(viewState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsRestoreDone() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabsReorder(ArrayList<TabBlueprint> blueprints) {
|
||||
ArrayList<TabBlueprint> newBlueprints = new ArrayList<>();
|
||||
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);
|
||||
newBlueprints.add(blueprint);
|
||||
}
|
||||
}
|
||||
// Add back hidden tabs
|
||||
newBlueprints.addAll(blueprints);
|
||||
|
||||
blueprints.clear();
|
||||
blueprints.addAll(newBlueprints);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSave(TabBlueprint blueprint, EditorViewState viewState) {
|
||||
ContentPanel contentPanel = getTabByNode(blueprint.getNode());
|
||||
if (contentPanel instanceof IViewStateSupport) {
|
||||
((IViewStateSupport) contentPanel).saveEditorViewState(viewState);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FocusManager implements FocusListener {
|
||||
private static final FocusManager INSTANCE = new FocusManager();
|
||||
private static @Nullable Component focusedComp;
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
package jadx.gui.ui.tab;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
import jadx.gui.ui.codearea.EditorViewState;
|
||||
|
||||
public class TabsController {
|
||||
private final transient MainWindow mainWindow;
|
||||
private final Map<JNode, TabBlueprint> tabsMap = new HashMap<>();
|
||||
private final ArrayList<ITabStatesListener> listeners = new ArrayList<>();
|
||||
|
||||
private boolean forceClose;
|
||||
|
||||
private TabBlueprint selectedTab = null;
|
||||
|
||||
public TabsController(MainWindow mainWindow) {
|
||||
this.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
public MainWindow getMainWindow() {
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
public void addListener(ITabStatesListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(ITabStatesListener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public @Nullable TabBlueprint getTabByNode(JNode node) {
|
||||
return tabsMap.get(node);
|
||||
}
|
||||
|
||||
public TabBlueprint openTab(JNode node) {
|
||||
return openTab(node, false);
|
||||
}
|
||||
|
||||
public TabBlueprint openTab(JNode node, boolean hidden) {
|
||||
TabBlueprint blueprint = getTabByNode(node);
|
||||
if (blueprint == null) {
|
||||
TabBlueprint newBlueprint = new TabBlueprint(node);
|
||||
tabsMap.put(node, newBlueprint);
|
||||
newBlueprint.setHidden(hidden);
|
||||
listeners.forEach(l -> l.onTabOpen(newBlueprint));
|
||||
if (hidden) {
|
||||
listeners.forEach(l -> l.onTabVisibilityChange(newBlueprint));
|
||||
}
|
||||
blueprint = newBlueprint;
|
||||
}
|
||||
|
||||
setTabHiddenInternal(blueprint, hidden);
|
||||
return blueprint;
|
||||
}
|
||||
|
||||
public void selectTab(JNode node) {
|
||||
TabBlueprint blueprint = openTab(node);
|
||||
selectedTab = blueprint;
|
||||
|
||||
listeners.forEach(l -> l.onTabSelect(blueprint));
|
||||
}
|
||||
|
||||
public void closeTab(JNode node) {
|
||||
closeTab(node, false);
|
||||
}
|
||||
|
||||
public void closeTab(JNode node, boolean considerPins) {
|
||||
TabBlueprint blueprint = getTabByNode(node);
|
||||
|
||||
if (blueprint == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (forceClose) {
|
||||
closeTabForce(blueprint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!considerPins || !blueprint.isPinned()) {
|
||||
if (!blueprint.isReferenced()) {
|
||||
closeTabForce(blueprint);
|
||||
} else {
|
||||
closeTabSoft(blueprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes Tab from everywhere
|
||||
*/
|
||||
private void closeTabForce(TabBlueprint blueprint) {
|
||||
listeners.forEach(l -> l.onTabClose(blueprint));
|
||||
tabsMap.remove(blueprint.getNode());
|
||||
}
|
||||
|
||||
/*
|
||||
* Hides Tab from TabbedPane
|
||||
*/
|
||||
private void closeTabSoft(TabBlueprint blueprint) {
|
||||
setTabHidden(blueprint.getNode(), true);
|
||||
}
|
||||
|
||||
public void setTabPositionFirst(JNode node) {
|
||||
TabBlueprint blueprint = openTab(node);
|
||||
listeners.forEach(l -> l.onTabPositionFirst(blueprint));
|
||||
}
|
||||
|
||||
public void setTabPinned(JNode node, boolean pinned) {
|
||||
TabBlueprint blueprint = openTab(node);
|
||||
setTabPinnedInternal(blueprint, pinned);
|
||||
}
|
||||
|
||||
public void setTabPinnedInternal(TabBlueprint blueprint, boolean pinned) {
|
||||
if (blueprint.isPinned() != pinned) {
|
||||
blueprint.setPinned(pinned);
|
||||
listeners.forEach(l -> l.onTabPinChange(blueprint));
|
||||
}
|
||||
}
|
||||
|
||||
public void setTabBookmarked(JNode node, boolean bookmarked) {
|
||||
TabBlueprint blueprint = openTab(node);
|
||||
setTabBookmarkedInternal(blueprint, bookmarked);
|
||||
}
|
||||
|
||||
private void setTabBookmarkedInternal(TabBlueprint blueprint, boolean bookmarked) {
|
||||
if (blueprint.isBookmarked() != bookmarked) {
|
||||
blueprint.setBookmarked(bookmarked);
|
||||
listeners.forEach(l -> l.onTabBookmarkChange(blueprint));
|
||||
removeTabIfNotReferenced(blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTabHidden(JNode node, boolean hidden) {
|
||||
TabBlueprint blueprint = getTabByNode(node);
|
||||
setTabHiddenInternal(blueprint, hidden);
|
||||
}
|
||||
|
||||
private void setTabHiddenInternal(TabBlueprint blueprint, boolean hidden) {
|
||||
if (blueprint != null && blueprint.isHidden() != hidden) {
|
||||
blueprint.setHidden(hidden);
|
||||
listeners.forEach(l -> l.onTabVisibilityChange(blueprint));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTabIfNotReferenced(TabBlueprint blueprint) {
|
||||
if (blueprint.isHidden() && !blueprint.isReferenced()) {
|
||||
tabsMap.remove(blueprint.getNode());
|
||||
}
|
||||
}
|
||||
|
||||
public void closeAllTabs() {
|
||||
closeAllTabs(false);
|
||||
}
|
||||
|
||||
public void forceCloseAllTabs() {
|
||||
forceClose = true;
|
||||
closeAllTabs();
|
||||
forceClose = false;
|
||||
}
|
||||
|
||||
public boolean isForceClose() {
|
||||
return forceClose;
|
||||
}
|
||||
|
||||
public void closeAllTabs(boolean considerPins) {
|
||||
List.copyOf(tabsMap.values()).forEach(t -> closeTab(t.getNode(), considerPins));
|
||||
}
|
||||
|
||||
public void unpinAllTabs() {
|
||||
tabsMap.values().forEach(t -> setTabPinned(t.getNode(), false));
|
||||
}
|
||||
|
||||
public void unbookmarkAllTabs() {
|
||||
tabsMap.values().forEach(t -> setTabBookmarked(t.getNode(), false));
|
||||
}
|
||||
|
||||
public TabBlueprint getSelectedTab() {
|
||||
return selectedTab;
|
||||
}
|
||||
|
||||
public List<TabBlueprint> getTabs() {
|
||||
return List.copyOf(tabsMap.values());
|
||||
}
|
||||
|
||||
public List<TabBlueprint> getOpenTabs() {
|
||||
return List.copyOf(tabsMap.values());
|
||||
}
|
||||
|
||||
public List<TabBlueprint> getPinnedTabs() {
|
||||
return tabsMap.values().stream()
|
||||
.filter(TabBlueprint::isPinned)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public List<TabBlueprint> getBookmarkedTabs() {
|
||||
return tabsMap.values().stream()
|
||||
.filter(TabBlueprint::isBookmarked)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
public void restoreEditorViewState(EditorViewState viewState) {
|
||||
JNode node = viewState.getNode();
|
||||
TabBlueprint blueprint = openTab(node, viewState.isHidden());
|
||||
setTabPinnedInternal(blueprint, viewState.isPinned());
|
||||
setTabBookmarkedInternal(blueprint, viewState.isBookmarked());
|
||||
listeners.forEach(l -> l.onTabRestore(blueprint, viewState));
|
||||
if (viewState.isActive()) {
|
||||
selectTab(node);
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyRestoreEditorViewStateDone() {
|
||||
listeners.forEach(ITabStatesListener::onTabsRestoreDone);
|
||||
}
|
||||
|
||||
public List<EditorViewState> getEditorViewStates() {
|
||||
ArrayList<TabBlueprint> reorderedTabs = new ArrayList<>(tabsMap.values());
|
||||
listeners.forEach(l -> l.onTabsReorder(reorderedTabs));
|
||||
List<EditorViewState> states = new ArrayList<>();
|
||||
for (TabBlueprint blueprint : reorderedTabs) {
|
||||
states.add(getEditorViewState(blueprint));
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
public EditorViewState getEditorViewState(TabBlueprint blueprint) {
|
||||
EditorViewState viewState = new EditorViewState(blueprint.getNode());
|
||||
listeners.forEach(l -> l.onTabSave(blueprint, viewState));
|
||||
viewState.setActive(blueprint == selectedTab);
|
||||
viewState.setPinned(blueprint.isPinned());
|
||||
viewState.setBookmarked(blueprint.isBookmarked());
|
||||
viewState.setHidden(blueprint.isHidden());
|
||||
return viewState;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public class StartPageNode extends JNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPinnable() {
|
||||
public boolean supportsQuickTabs() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ public class Icons {
|
||||
public static final ImageIcon PIN_DARK = UiUtils.openSvgIcon("nodes/pin_dark");
|
||||
public static final ImageIcon PIN_HOVERED = UiUtils.openSvgIcon("nodes/pinHovered");
|
||||
public static final ImageIcon PIN_HOVERED_DARK = UiUtils.openSvgIcon("nodes/pinHovered_dark");
|
||||
public static final ImageIcon BOOKMARK = UiUtils.openSvgIcon("nodes/bookmark");
|
||||
public static final ImageIcon BOOKMARK_OVERLAY = UiUtils.openSvgIcon("nodes/bookmark_overlay");
|
||||
public static final ImageIcon BOOKMARK_DARK = UiUtils.openSvgIcon("nodes/bookmark_dark");
|
||||
public static final ImageIcon BOOKMARK_OVERLAY_DARK = UiUtils.openSvgIcon("nodes/bookmark_overlay_dark");
|
||||
|
||||
public static final ImageIcon STATIC = openSvgIcon("nodes/staticMark");
|
||||
public static final ImageIcon FINAL = openSvgIcon("nodes/finalMark");
|
||||
|
||||
@@ -54,6 +54,14 @@ public class OverlayIcon implements Icon {
|
||||
icons.add(icon);
|
||||
}
|
||||
|
||||
public void remove(Icon icon) {
|
||||
icons.remove(icon);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
icons.clear();
|
||||
}
|
||||
|
||||
public List<Icon> getIcons() {
|
||||
return icons;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Quelltexte
|
||||
tree.resources_title=Ressourcen
|
||||
tree.loading=Laden…
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Laden
|
||||
progress.save_mappings=Zuordnungen exportieren
|
||||
@@ -83,6 +85,9 @@ tabs.close=Schließen
|
||||
tabs.closeOthers=Andere schließen
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=Alles schließen
|
||||
tabs.closeAllRight=Schließe alles rechts
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Source code
|
||||
tree.resources_title=Resources
|
||||
tree.loading=Loading...
|
||||
tree.pinned_tabs=Pinned Tabs
|
||||
tree.open_tabs=Open Tabs
|
||||
tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Loading
|
||||
progress.save_mappings=Saving mappings
|
||||
@@ -83,6 +85,9 @@ tabs.close=Close
|
||||
tabs.closeOthers=Close Others
|
||||
tabs.unpin=Unpin
|
||||
tabs.unpin_all=Unpin All
|
||||
tabs.bookmark=Bookmark
|
||||
tabs.unbookmark=Unbookmark
|
||||
tabs.unbookmark_all=Unbookmark All
|
||||
tabs.pin=Pin
|
||||
tabs.closeAll=Close All
|
||||
tabs.closeAllRight=Close All Right
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Código fuente
|
||||
tree.resources_title=Recursos
|
||||
tree.loading=Cargando...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Cargando
|
||||
#progress.save_mappings=
|
||||
@@ -83,6 +85,9 @@ tabs.close=Cerrar
|
||||
tabs.closeOthers=Cerrar otros
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=Cerrar todo
|
||||
tabs.closeAllRight=Cierra todo a la derecha
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Kode Sumber
|
||||
tree.resources_title=Sumber Daya
|
||||
tree.loading=Memuat...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Memuat
|
||||
progress.save_mappings=Menyimpan pemetaan
|
||||
@@ -83,6 +85,9 @@ tabs.close=Tutup
|
||||
tabs.closeOthers=Tutup yang Lain
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=Tutup Semua
|
||||
tabs.closeAllRight=Tutup Semua yang Kanan
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=소스코드
|
||||
tree.resources_title=리소스
|
||||
tree.loading=로딩중...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=로딩중
|
||||
progress.save_mappings=매핑 내보내는 중
|
||||
@@ -83,6 +85,9 @@ tabs.close=닫기
|
||||
tabs.closeOthers=이 탭을 제외하고 닫기
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=모두 닫기
|
||||
tabs.closeAllRight=오른쪽의 모든 것을 닫으십시오
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Código fonte
|
||||
tree.resources_title=Recursos
|
||||
tree.loading=Carregando...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Carregando
|
||||
#progress.save_mappings=Saving mappings
|
||||
@@ -83,6 +85,9 @@ tabs.close=Fechar
|
||||
tabs.closeOthers=Fechar outros
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=Fechar todos
|
||||
tabs.closeAllRight=Feche tudo à direita
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=Исходный код
|
||||
tree.resources_title=Ресурсы
|
||||
tree.loading=Загрузка...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=Загрузка
|
||||
progress.save_mappings=Сохранить маппинги
|
||||
@@ -83,6 +85,9 @@ tabs.close=Закрыть
|
||||
tabs.closeOthers=Закрыть другие
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=Закрыть все
|
||||
tabs.closeAllRight=Закройте все справа
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=源代码
|
||||
tree.resources_title=资源文件
|
||||
tree.loading=加载中…
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=正在加载
|
||||
progress.save_mappings=导出映射
|
||||
@@ -83,6 +85,9 @@ tabs.close=关闭
|
||||
tabs.closeOthers=关闭其他
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=关闭全部
|
||||
tabs.closeAllRight=关闭右边的所有
|
||||
|
||||
@@ -60,6 +60,8 @@ tree.sources_title=原始碼
|
||||
tree.resources_title=資源
|
||||
tree.loading=載入中...
|
||||
#tree.pinned_tabs=Pinned Tabs
|
||||
#tree.open_tabs=Open Tabs
|
||||
#tree.bookmarked_tabs=Bookmarked Tabs
|
||||
|
||||
progress.load=載入中
|
||||
progress.save_mappings=正在匯出對應
|
||||
@@ -83,6 +85,9 @@ tabs.close=關閉
|
||||
tabs.closeOthers=關閉其他
|
||||
#tabs.unpin=Unpin
|
||||
#tabs.unpin_all=Unpin All
|
||||
#tabs.bookmark=Bookmark
|
||||
#tabs.unbookmark=Unbookmark
|
||||
#tabs.unbookmark_all=Unbookmark All
|
||||
#tabs.pin=Pin
|
||||
tabs.closeAll=關閉全部
|
||||
tabs.closeAllRight=關閉右邊的所有
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3.5C3 2.39543 3.89543 1.5 5 1.5H10.9989C12.1034 1.5 12.9989 2.39543 12.9989 3.5V14.9184C12.9989 15.3379 12.5134 15.5709 12.1861 15.3085L7.99943 11.9524L3.81273 15.3085C3.48543 15.5709 3 15.3379 3 14.9184V3.5Z" fill="#FFAF0F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,4 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3.5C3 2.39543 3.89543 1.5 5 1.5H10.9989C12.1034 1.5 12.9989 2.39543 12.9989 3.5V14.9184C12.9989 15.3379 12.5134 15.5709 12.1861 15.3085L7.99943 11.9524L3.81273 15.3085C3.48543 15.5709 3 15.3379 3 14.9184V3.5Z" fill="#F2C55C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,4 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3.5C3 2.39543 3.89543 1.5 5 1.5H10.9989C12.1034 1.5 12.9989 2.39543 12.9989 3.5V14.9184C12.9989 15.3379 12.5134 15.5709 12.1861 15.3085L7.99943 11.9524L3.81273 15.3085C3.48543 15.5709 3 15.3379 3 14.9184V3.5Z" fill="#FFAF0F"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
@@ -0,0 +1,4 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3.5C3 2.39543 3.89543 1.5 5 1.5H10.9989C12.1034 1.5 12.9989 2.39543 12.9989 3.5V14.9184C12.9989 15.3379 12.5134 15.5709 12.1861 15.3085L7.99943 11.9524L3.81273 15.3085C3.48543 15.5709 3 15.3379 3 14.9184V3.5Z" fill="#F2C55C"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 469 B |
Reference in New Issue
Block a user