Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

entry navigator #146

Merged
merged 7 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,9 @@ private void applyChange0(ValidationContext vc, EntryChange<?> change, boolean u
if (vc.canProceed()) {
boolean renamed = !change.getDeobfName().isUnchanged();
this.gui.updateStructure(this.gui.getActiveEditor());
if (this.gui.getActiveEditor() != null) {
this.gui.getActiveEditor().onRename(target);
}

if (!Objects.equals(prev.targetName(), mapping.targetName())) {
this.chp.invalidateMapped();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cuchaz.enigma.gui;

import cuchaz.enigma.gui.docker.NotificationsDocker;
import cuchaz.enigma.gui.util.GuiUtil;
import cuchaz.enigma.utils.I18n;
import cuchaz.enigma.utils.validation.Message;
import cuchaz.enigma.utils.validation.ParameterizedMessage;
Expand Down Expand Up @@ -101,13 +102,7 @@ public void notify(ParameterizedMessage message) {

JPanel glass = (JPanel) this.gui.getFrame().getGlassPane();
this.glassPane = glass;

// set up glass pane to actually display elements
glass.setOpaque(false);
glass.setVisible(true);
glass.setLayout(null);
glass.revalidate();

GuiUtil.setUpGlassPane(glass);
glass.add(notificationPanel);

// set up notification panel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public final class KeyBinds {
public static final KeyBind EDITOR_RELOAD_CLASS = KeyBind.builder("reload_class", EDITOR_CATEGORY).mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_F5).build();
public static final KeyBind EDITOR_QUICK_FIND = KeyBind.builder("quick_find", EDITOR_CATEGORY).mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_F).build();

public static final KeyBind ENTRY_NAVIGATOR_NEXT = KeyBind.builder("navigate_next", EDITOR_CATEGORY).mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_DOWN).build();
public static final KeyBind ENTRY_NAVIGATOR_LAST = KeyBind.builder("navigate_last", EDITOR_CATEGORY).mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_UP).build();

public static final KeyBind SAVE_MAPPINGS = KeyBind.builder("save", MENU_CATEGORY).mod(InputEvent.CTRL_DOWN_MASK).key(KeyEvent.VK_S).build();
public static final KeyBind DROP_MAPPINGS = KeyBind.builder("drop_mappings", MENU_CATEGORY).build();
public static final KeyBind RELOAD_MAPPINGS = KeyBind.builder("reload_mappings", MENU_CATEGORY).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ public class EditorTabbedPane {

private final EditorTabPopupMenu editorTabPopupMenu;
private final Gui gui;
private final NavigatorPanel navigator;

public EditorTabbedPane(Gui gui) {
this.gui = gui;
this.editorTabPopupMenu = new EditorTabPopupMenu(this);
this.navigator = new NavigatorPanel(this.gui);

this.openFiles.addMouseListener(GuiUtil.onMousePress(this::onTabPressed));
}
Expand All @@ -39,7 +41,8 @@ public EditorPanel openClass(ClassEntry entry) {
EditorPanel editorPanel = this.editors.computeIfAbsent(entry, e -> {
ClassHandle ch = this.gui.getController().getClassHandleProvider().openClass(entry);
if (ch == null) return null;
EditorPanel ed = new EditorPanel(this.gui);
this.navigator.clear();
EditorPanel ed = new EditorPanel(this.gui, this.navigator);
ed.setup();
ed.setClassHandle(ch);
this.openFiles.addTab(ed.getFileName(), ed.getUi());
Expand Down Expand Up @@ -71,6 +74,10 @@ public void onTitleChanged(EditorPanel editor, String title) {
ed.getEditor().addKeyListener(GuiUtil.onKeyPress(keyEvent -> {
if (KeyBinds.EDITOR_CLOSE_TAB.matches(keyEvent)) {
this.closeEditor(ed);
} else if (KeyBinds.ENTRY_NAVIGATOR_NEXT.matches(keyEvent)) {
this.navigator.navigateDown();
} else if (KeyBinds.ENTRY_NAVIGATOR_LAST.matches(keyEvent)) {
this.navigator.navigateUp();
}
}));

Expand Down
ix0rai marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package cuchaz.enigma.gui.elements;

import cuchaz.enigma.EnigmaProject;
import cuchaz.enigma.gui.Gui;
import cuchaz.enigma.source.RenamableTokenType;
import cuchaz.enigma.translation.representation.entry.Entry;

import javax.annotation.Nullable;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.ItemEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* A panel with buttons to navigate to the next and previous items in its entry collection.
*/
public class NavigatorPanel extends JPanel {
private static final RenamableTokenType[] SUPPORTED_TOKEN_TYPES = {RenamableTokenType.OBFUSCATED, RenamableTokenType.PROPOSED, RenamableTokenType.DEOBFUSCATED};

private final Gui gui;
private final JLabel statsLabel;
private final Map<RenamableTokenType, List<Entry<?>>> entries = new HashMap<>();
private final Map<Entry<?>, RenamableTokenType> tokenTypes = new HashMap<>();

private int currentIndex = 0;
private RenamableTokenType selectedType;

/**
* Creates a new navigator panel.
* @param gui the parent gui
*/
public NavigatorPanel(Gui gui) {
super();
this.gui = gui;
this.statsLabel = new JLabel("0/0");

JComboBox<RenamableTokenType> typeSelector = new JComboBox<>(SUPPORTED_TOKEN_TYPES);
typeSelector.addItemListener(event -> {
if (event.getStateChange() == ItemEvent.SELECTED) {
this.selectedType = (RenamableTokenType) event.getItem();
this.onTypeChange();
}
});
this.selectedType = RenamableTokenType.OBFUSCATED;

for (RenamableTokenType type : SUPPORTED_TOKEN_TYPES) {
this.entries.put(type, new ArrayList<>());
}

JButton up = new JButton("⋀");
up.addActionListener(event -> this.navigateUp());
JButton down = new JButton("⋁");
down.addActionListener(event -> this.navigateDown());

this.add(typeSelector);
this.add(up);
this.add(down);
this.add(this.statsLabel);

// transparent background
this.setBackground(new Color(0, 0, 0, 0));
}

/**
* Navigates to the next entry matching the current filter.
*/
public void navigateDown() {
List<Entry<?>> currentEntrySet = this.entries.get(this.selectedType);
if (!currentEntrySet.isEmpty()) {
this.currentIndex++;
this.wrapIndex();

this.tryNavigate();
}
}

/**
* Navigates to the last entry matching the current filter.
*/
public void navigateUp() {
List<Entry<?>> currentEntrySet = this.entries.get(this.selectedType);
if (!currentEntrySet.isEmpty()) {
this.currentIndex--;
this.wrapIndex();

this.tryNavigate();
}
}

private void onTypeChange() {
this.wrapIndex();
this.updateStatsLabel();
}

private void wrapIndex() {
List<Entry<?>> currentEntrySet = this.entries.get(this.selectedType);
if (this.currentIndex < 0) {
this.currentIndex = currentEntrySet.size() - 1;
} else if (this.currentIndex >= currentEntrySet.size()) {
this.currentIndex = 0;
}
}

private void tryNavigate() {
this.updateTokenType(this.entries.get(this.selectedType).get(this.currentIndex));
this.updateStatsLabel();
this.gui.getController().navigateTo(this.entries.get(this.selectedType).get(this.currentIndex));
}

/**
* Removes all data from this navigator and updates its UI.
* Keeps selected type intact.
*/
public void clear() {
this.tokenTypes.clear();
for (var list : this.entries.values()) {
list.clear();
}

this.currentIndex = 0;
}

/**
* Adds the provided entry to this navigator's pool and sorts it.
* @param entry the entry to add
*/
public void addEntry(@Nullable Entry<?> entry) {
EnigmaProject project = this.gui.getController().getProject();
if (entry != null && project.isRenamable(entry) && project.isNavigable(entry)) {
RenamableTokenType tokenType = this.getTokenType(entry);
List<Entry<?>> entries = this.entries.get(tokenType);

if (!entries.contains(entry)) {
entries.add(entry);
this.tokenTypes.put(entry, tokenType);
this.updateStatsLabel();
}
}
}

/**
* Checks if the entry should be moved to a different token type, and updates it if so.
* Assumes that the entry's old token type matches the currently selected token type.
* @param target the entry to check
*/
public void updateTokenType(Entry<?> target) {
RenamableTokenType tokenType = this.getTokenType(target);
RenamableTokenType oldType = this.tokenTypes.get(target);

if (tokenType != oldType) {
this.entries.get(oldType).remove(target);
this.entries.get(tokenType).add(target);
this.tokenTypes.put(target, tokenType);
this.updateStatsLabel();
}
}

private RenamableTokenType getTokenType(Entry<?> target) {
EnigmaProject project = this.gui.getController().getProject();
RenamableTokenType tokenType = project.getMapper().extendedDeobfuscate(target).getType();
if (tokenType == RenamableTokenType.OBFUSCATED) {
if (project.hasProposedName(target)) {
tokenType = RenamableTokenType.PROPOSED;
}
}

return tokenType;
}

private void updateStatsLabel() {
int index = this.entries.get(this.selectedType).isEmpty() ? 0 : this.currentIndex + 1;
String indexString = String.valueOf(index).length() == 1 ? "0" + index : String.valueOf(index);
this.statsLabel.setText(indexString + "/" + this.entries.get(this.selectedType).size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@

import cuchaz.enigma.gui.util.GuiUtil;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.accessibility.AccessibleContext;
import javax.annotation.Nullable;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeListener;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class ClosableTabTitlePane {
private final JPanel ui;
Expand Down Expand Up @@ -117,22 +114,14 @@ private void updateState(JTabbedPane pane) {
boolean isActive = selectedIndex != -1 && pane.getTabComponentAt(selectedIndex) == this.ui;
this.closeButton.setEnabled(isActive);
this.closeButton.putClientProperty("paintActive", isActive);

this.ui.remove(this.closeButton);
this.ui.add(this.closeButton);

this.ui.repaint();
}

public JPanel getUi() {
return this.ui;
}

@Nullable
public static ClosableTabTitlePane byUi(Component c) {
if (c instanceof JComponent component) {
Object prop = component.getClientProperty(ClosableTabTitlePane.class);
if (prop instanceof ClosableTabTitlePane pane) {
return pane;
}
}

return null;
}
}
Loading