summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/gui/Gui.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui/Gui.java')
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java193
1 files changed, 164 insertions, 29 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 3412cd5..3adabae 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -34,6 +34,7 @@ import cuchaz.enigma.config.Themes;
34import cuchaz.enigma.gui.dialog.CrashDialog; 34import cuchaz.enigma.gui.dialog.CrashDialog;
35import cuchaz.enigma.gui.dialog.JavadocDialog; 35import cuchaz.enigma.gui.dialog.JavadocDialog;
36import cuchaz.enigma.gui.dialog.SearchDialog; 36import cuchaz.enigma.gui.dialog.SearchDialog;
37import cuchaz.enigma.gui.elements.CollapsibleTabbedPane;
37import cuchaz.enigma.gui.elements.MenuBar; 38import cuchaz.enigma.gui.elements.MenuBar;
38import cuchaz.enigma.gui.elements.PopupMenuBar; 39import cuchaz.enigma.gui.elements.PopupMenuBar;
39import cuchaz.enigma.gui.filechooser.FileChooserAny; 40import cuchaz.enigma.gui.filechooser.FileChooserAny;
@@ -46,10 +47,12 @@ import cuchaz.enigma.gui.panels.PanelEditor;
46import cuchaz.enigma.gui.panels.PanelIdentifier; 47import cuchaz.enigma.gui.panels.PanelIdentifier;
47import cuchaz.enigma.gui.panels.PanelObf; 48import cuchaz.enigma.gui.panels.PanelObf;
48import cuchaz.enigma.gui.util.History; 49import cuchaz.enigma.gui.util.History;
50import cuchaz.enigma.network.packet.*;
49import cuchaz.enigma.throwables.IllegalNameException; 51import cuchaz.enigma.throwables.IllegalNameException;
50import cuchaz.enigma.translation.mapping.*; 52import cuchaz.enigma.translation.mapping.*;
51import cuchaz.enigma.translation.representation.entry.*; 53import cuchaz.enigma.translation.representation.entry.*;
52import cuchaz.enigma.utils.I18n; 54import cuchaz.enigma.utils.I18n;
55import cuchaz.enigma.utils.Message;
53import cuchaz.enigma.gui.util.ScaleUtil; 56import cuchaz.enigma.gui.util.ScaleUtil;
54import cuchaz.enigma.utils.Utils; 57import cuchaz.enigma.utils.Utils;
55import de.sciss.syntaxpane.DefaultSyntaxKit; 58import de.sciss.syntaxpane.DefaultSyntaxKit;
@@ -63,8 +66,11 @@ public class Gui {
63 private final MenuBar menuBar; 66 private final MenuBar menuBar;
64 // state 67 // state
65 public History<EntryReference<Entry<?>, Entry<?>>> referenceHistory; 68 public History<EntryReference<Entry<?>, Entry<?>>> referenceHistory;
69 public EntryReference<Entry<?>, Entry<?>> renamingReference;
66 public EntryReference<Entry<?>, Entry<?>> cursorReference; 70 public EntryReference<Entry<?>, Entry<?>> cursorReference;
67 private boolean shouldNavigateOnClick; 71 private boolean shouldNavigateOnClick;
72 private ConnectionState connectionState;
73 private boolean isJarOpen;
68 74
69 public FileDialog jarFileChooser; 75 public FileDialog jarFileChooser;
70 public FileDialog tinyMappingsFileChooser; 76 public FileDialog tinyMappingsFileChooser;
@@ -76,6 +82,7 @@ public class Gui {
76 private JFrame frame; 82 private JFrame frame;
77 public Config.LookAndFeel editorFeel; 83 public Config.LookAndFeel editorFeel;
78 public PanelEditor editor; 84 public PanelEditor editor;
85 public JScrollPane sourceScroller;
79 private JPanel classesPanel; 86 private JPanel classesPanel;
80 private JSplitPane splitClasses; 87 private JSplitPane splitClasses;
81 private PanelIdentifier infoPanel; 88 private PanelIdentifier infoPanel;
@@ -87,6 +94,20 @@ public class Gui {
87 private JList<Token> tokens; 94 private JList<Token> tokens;
88 private JTabbedPane tabs; 95 private JTabbedPane tabs;
89 96
97 private JSplitPane splitRight;
98 private JSplitPane logSplit;
99 private CollapsibleTabbedPane logTabs;
100 private JList<String> users;
101 private DefaultListModel<String> userModel;
102 private JScrollPane messageScrollPane;
103 private JList<Message> messages;
104 private DefaultListModel<Message> messageModel;
105 private JTextField chatBox;
106
107 private JPanel statusBar;
108 private JLabel connectionStatusLabel;
109 private JLabel statusLabel;
110
90 public JTextField renameTextField; 111 public JTextField renameTextField;
91 public JTextArea javadocTextArea; 112 public JTextArea javadocTextArea;
92 113
@@ -150,7 +171,7 @@ public class Gui {
150 // init editor 171 // init editor
151 selectionHighlightPainter = new SelectionHighlightPainter(); 172 selectionHighlightPainter = new SelectionHighlightPainter();
152 this.editor = new PanelEditor(this); 173 this.editor = new PanelEditor(this);
153 JScrollPane sourceScroller = new JScrollPane(this.editor); 174 this.sourceScroller = new JScrollPane(this.editor);
154 this.editor.setContentType("text/enigma-sources"); 175 this.editor.setContentType("text/enigma-sources");
155 this.editor.setBackground(new Color(Config.getInstance().editorBackground)); 176 this.editor.setBackground(new Color(Config.getInstance().editorBackground));
156 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit(); 177 DefaultSyntaxKit kit = (DefaultSyntaxKit) this.editor.getEditorKit();
@@ -283,7 +304,34 @@ public class Gui {
283 tabs.addTab(I18n.translate("info_panel.tree.inheritance"), inheritancePanel); 304 tabs.addTab(I18n.translate("info_panel.tree.inheritance"), inheritancePanel);
284 tabs.addTab(I18n.translate("info_panel.tree.implementations"), implementationsPanel); 305 tabs.addTab(I18n.translate("info_panel.tree.implementations"), implementationsPanel);
285 tabs.addTab(I18n.translate("info_panel.tree.calls"), callPanel); 306 tabs.addTab(I18n.translate("info_panel.tree.calls"), callPanel);
286 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, tabs); 307 logTabs = new CollapsibleTabbedPane(JTabbedPane.BOTTOM);
308 userModel = new DefaultListModel<>();
309 users = new JList<>(userModel);
310 messageModel = new DefaultListModel<>();
311 messages = new JList<>(messageModel);
312 messages.setCellRenderer(new MessageListCellRenderer());
313 JPanel messagePanel = new JPanel(new BorderLayout());
314 messageScrollPane = new JScrollPane(this.messages);
315 messagePanel.add(messageScrollPane, BorderLayout.CENTER);
316 JPanel chatPanel = new JPanel(new BorderLayout());
317 chatBox = new JTextField();
318 AbstractAction sendListener = new AbstractAction("Send") {
319 @Override
320 public void actionPerformed(ActionEvent e) {
321 sendMessage();
322 }
323 };
324 chatBox.addActionListener(sendListener);
325 JButton chatSendButton = new JButton(sendListener);
326 chatPanel.add(chatBox, BorderLayout.CENTER);
327 chatPanel.add(chatSendButton, BorderLayout.EAST);
328 messagePanel.add(chatPanel, BorderLayout.SOUTH);
329 logTabs.addTab(I18n.translate("log_panel.users"), new JScrollPane(this.users));
330 logTabs.addTab(I18n.translate("log_panel.messages"), messagePanel);
331 logSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, tabs, logTabs);
332 logSplit.setResizeWeight(0.5);
333 logSplit.resetToPreferredSizes();
334 splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, this.logSplit);
287 splitRight.setResizeWeight(1); // let the left side take all the slack 335 splitRight.setResizeWeight(1); // let the left side take all the slack
288 splitRight.resetToPreferredSizes(); 336 splitRight.resetToPreferredSizes();
289 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight); 337 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, this.classesPanel, splitRight);
@@ -294,7 +342,17 @@ public class Gui {
294 this.menuBar = new MenuBar(this); 342 this.menuBar = new MenuBar(this);
295 this.frame.setJMenuBar(this.menuBar); 343 this.frame.setJMenuBar(this.menuBar);
296 344
345 // init status bar
346 statusBar = new JPanel(new BorderLayout());
347 statusBar.setBorder(BorderFactory.createLoweredBevelBorder());
348 connectionStatusLabel = new JLabel();
349 statusLabel = new JLabel();
350 statusBar.add(statusLabel, BorderLayout.CENTER);
351 statusBar.add(connectionStatusLabel, BorderLayout.EAST);
352 pane.add(statusBar, BorderLayout.SOUTH);
353
297 // init state 354 // init state
355 setConnectionState(ConnectionState.NOT_CONNECTED);
298 onCloseJar(); 356 onCloseJar();
299 357
300 this.frame.addWindowListener(new WindowAdapter() { 358 this.frame.addWindowListener(new WindowAdapter() {
@@ -334,18 +392,14 @@ public class Gui {
334 setEditorText(null); 392 setEditorText(null);
335 393
336 // update menu 394 // update menu
337 this.menuBar.closeJarMenu.setEnabled(true); 395 isJarOpen = true;
338 this.menuBar.openMappingsMenus.forEach(item -> item.setEnabled(true));
339 this.menuBar.saveMappingsMenu.setEnabled(false);
340 this.menuBar.saveMappingsMenus.forEach(item -> item.setEnabled(true));
341 this.menuBar.closeMappingsMenu.setEnabled(true);
342 this.menuBar.exportSourceMenu.setEnabled(true);
343 this.menuBar.exportJarMenu.setEnabled(true);
344 396
397 updateUiState();
345 redraw(); 398 redraw();
346 } 399 }
347 400
348 public void onCloseJar() { 401 public void onCloseJar() {
402
349 // update gui 403 // update gui
350 this.frame.setTitle(Constants.NAME); 404 this.frame.setTitle(Constants.NAME);
351 setObfClasses(null); 405 setObfClasses(null);
@@ -354,14 +408,10 @@ public class Gui {
354 this.classesPanel.removeAll(); 408 this.classesPanel.removeAll();
355 409
356 // update menu 410 // update menu
357 this.menuBar.closeJarMenu.setEnabled(false); 411 isJarOpen = false;
358 this.menuBar.openMappingsMenus.forEach(item -> item.setEnabled(false)); 412 setMappingsFile(null);
359 this.menuBar.saveMappingsMenu.setEnabled(false);
360 this.menuBar.saveMappingsMenus.forEach(item -> item.setEnabled(false));
361 this.menuBar.closeMappingsMenu.setEnabled(false);
362 this.menuBar.exportSourceMenu.setEnabled(false);
363 this.menuBar.exportJarMenu.setEnabled(false);
364 413
414 updateUiState();
365 redraw(); 415 redraw();
366 } 416 }
367 417
@@ -375,7 +425,7 @@ public class Gui {
375 425
376 public void setMappingsFile(Path path) { 426 public void setMappingsFile(Path path) {
377 this.enigmaMappingsFileChooser.setSelectedFile(path != null ? path.toFile() : null); 427 this.enigmaMappingsFileChooser.setSelectedFile(path != null ? path.toFile() : null);
378 this.menuBar.saveMappingsMenu.setEnabled(path != null); 428 updateUiState();
379 } 429 }
380 430
381 public void setEditorText(String source) { 431 public void setEditorText(String source) {
@@ -561,10 +611,12 @@ public class Gui {
561 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor(); 611 boolean isConstructorEntry = isToken && referenceEntry instanceof MethodEntry && ((MethodEntry) referenceEntry).isConstructor();
562 boolean isRenamable = isToken && this.controller.project.isRenamable(cursorReference); 612 boolean isRenamable = isToken && this.controller.project.isRenamable(cursorReference);
563 613
564 if (isToken) { 614 if (!isRenaming()) {
565 showCursorReference(cursorReference); 615 if (isToken) {
566 } else { 616 showCursorReference(cursorReference);
567 infoPanel.clearReference(); 617 } else {
618 infoPanel.clearReference();
619 }
568 } 620 }
569 621
570 this.popupMenu.renameMenu.setEnabled(isRenamable); 622 this.popupMenu.renameMenu.setEnabled(isRenamable);
@@ -586,6 +638,11 @@ public class Gui {
586 } 638 }
587 639
588 public void startDocChange() { 640 public void startDocChange() {
641 EntryReference<Entry<?>, Entry<?>> curReference = cursorReference;
642 if (isRenaming()) {
643 finishRename(false);
644 }
645 renamingReference = curReference;
589 646
590 // init the text box 647 // init the text box
591 javadocTextArea = new JTextArea(10, 40); 648 javadocTextArea = new JTextArea(10, 40);
@@ -603,7 +660,8 @@ public class Gui {
603 String newName = javadocTextArea.getText(); 660 String newName = javadocTextArea.getText();
604 if (saveName) { 661 if (saveName) {
605 try { 662 try {
606 this.controller.changeDocs(cursorReference, newName); 663 this.controller.changeDocs(renamingReference, newName);
664 this.controller.sendPacket(new ChangeDocsC2SPacket(renamingReference.getNameableEntry(), newName));
607 } catch (IllegalNameException ex) { 665 } catch (IllegalNameException ex) {
608 javadocTextArea.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 666 javadocTextArea.setBorder(BorderFactory.createLineBorder(Color.red, 1));
609 javadocTextArea.setToolTipText(ex.getReason()); 667 javadocTextArea.setToolTipText(ex.getReason());
@@ -665,14 +723,19 @@ public class Gui {
665 else 723 else
666 renameTextField.selectAll(); 724 renameTextField.selectAll();
667 725
726 renamingReference = cursorReference;
727
668 redraw(); 728 redraw();
669 } 729 }
670 730
671 private void finishRename(boolean saveName) { 731 private void finishRename(boolean saveName) {
672 String newName = renameTextField.getText(); 732 String newName = renameTextField.getText();
733
673 if (saveName && newName != null && !newName.isEmpty()) { 734 if (saveName && newName != null && !newName.isEmpty()) {
674 try { 735 try {
675 this.controller.rename(cursorReference, newName, true); 736 this.controller.rename(renamingReference, newName, true);
737 this.controller.sendPacket(new RenameC2SPacket(renamingReference.getNameableEntry(), newName, true));
738 renameTextField = null;
676 } catch (IllegalNameException ex) { 739 } catch (IllegalNameException ex) {
677 renameTextField.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 740 renameTextField.setBorder(BorderFactory.createLineBorder(Color.red, 1));
678 renameTextField.setToolTipText(ex.getReason()); 741 renameTextField.setToolTipText(ex.getReason());
@@ -681,18 +744,20 @@ public class Gui {
681 return; 744 return;
682 } 745 }
683 746
684 // abort the rename
685 JPanel panel = (JPanel) infoPanel.getComponent(0);
686 panel.remove(panel.getComponentCount() - 1);
687 panel.add(Utils.unboldLabel(new JLabel(cursorReference.getNameableName(), JLabel.LEFT)));
688
689 renameTextField = null; 747 renameTextField = null;
690 748
749 // abort the rename
750 showCursorReference(cursorReference);
751
691 this.editor.grabFocus(); 752 this.editor.grabFocus();
692 753
693 redraw(); 754 redraw();
694 } 755 }
695 756
757 private boolean isRenaming() {
758 return renameTextField != null;
759 }
760
696 public void showInheritance() { 761 public void showInheritance() {
697 762
698 if (cursorReference == null) { 763 if (cursorReference == null) {
@@ -783,8 +848,10 @@ public class Gui {
783 848
784 if (!Objects.equals(obfEntry, deobfEntry)) { 849 if (!Objects.equals(obfEntry, deobfEntry)) {
785 this.controller.removeMapping(cursorReference); 850 this.controller.removeMapping(cursorReference);
851 this.controller.sendPacket(new RemoveMappingC2SPacket(cursorReference.getNameableEntry()));
786 } else { 852 } else {
787 this.controller.markAsDeobfuscated(cursorReference); 853 this.controller.markAsDeobfuscated(cursorReference);
854 this.controller.sendPacket(new MarkDeobfuscatedC2SPacket(cursorReference.getNameableEntry()));
788 } 855 }
789 } 856 }
790 857
@@ -850,6 +917,7 @@ public class Gui {
850 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject(); 917 ClassEntry prevDataChild = (ClassEntry) childNode.getUserObject();
851 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName()); 918 ClassEntry dataChild = new ClassEntry(data + "/" + prevDataChild.getSimpleName());
852 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getFullName()), dataChild.getFullName(), false); 919 this.controller.rename(new EntryReference<>(prevDataChild, prevDataChild.getFullName()), dataChild.getFullName(), false);
920 this.controller.sendPacket(new RenameC2SPacket(prevDataChild, dataChild.getFullName(), false));
853 childNode.setUserObject(dataChild); 921 childNode.setUserObject(dataChild);
854 } 922 }
855 node.setUserObject(data); 923 node.setUserObject(data);
@@ -857,8 +925,10 @@ public class Gui {
857 this.deobfPanel.deobfClasses.reload(); 925 this.deobfPanel.deobfClasses.reload();
858 } 926 }
859 // class rename 927 // class rename
860 else if (data instanceof ClassEntry) 928 else if (data instanceof ClassEntry) {
861 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false); 929 this.controller.rename(new EntryReference<>((ClassEntry) prevData, ((ClassEntry) prevData).getFullName()), ((ClassEntry) data).getFullName(), false);
930 this.controller.sendPacket(new RenameC2SPacket((ClassEntry) prevData, ((ClassEntry) data).getFullName(), false));
931 }
862 } 932 }
863 933
864 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, String newName) { 934 public void moveClassTree(EntryReference<Entry<?>, Entry<?>> obfReference, String newName) {
@@ -920,4 +990,69 @@ public class Gui {
920 return searchDialog; 990 return searchDialog;
921 } 991 }
922 992
993
994 public MenuBar getMenuBar() {
995 return menuBar;
996 }
997
998 public void addMessage(Message message) {
999 JScrollBar verticalScrollBar = messageScrollPane.getVerticalScrollBar();
1000 boolean isAtBottom = verticalScrollBar.getValue() >= verticalScrollBar.getMaximum() - verticalScrollBar.getModel().getExtent();
1001 messageModel.addElement(message);
1002 if (isAtBottom) {
1003 SwingUtilities.invokeLater(() -> verticalScrollBar.setValue(verticalScrollBar.getMaximum() - verticalScrollBar.getModel().getExtent()));
1004 }
1005 statusLabel.setText(message.translate());
1006 }
1007
1008 public void setUserList(List<String> users) {
1009 userModel.clear();
1010 users.forEach(userModel::addElement);
1011 connectionStatusLabel.setText(String.format(I18n.translate("status.connected_user_count"), users.size()));
1012 }
1013
1014 private void sendMessage() {
1015 String text = chatBox.getText().trim();
1016 if (!text.isEmpty()) {
1017 getController().sendPacket(new MessageC2SPacket(text));
1018 }
1019 chatBox.setText("");
1020 }
1021
1022 /**
1023 * Updates the state of the UI elements (button text, enabled state, ...) to reflect the current program state.
1024 * This is a central place to update the UI state to prevent multiple code paths from changing the same state,
1025 * causing inconsistencies.
1026 */
1027 public void updateUiState() {
1028 menuBar.connectToServerMenu.setEnabled(isJarOpen && connectionState != ConnectionState.HOSTING);
1029 menuBar.connectToServerMenu.setText(I18n.translate(connectionState != ConnectionState.CONNECTED ? "menu.collab.connect" : "menu.collab.disconnect"));
1030 menuBar.startServerMenu.setEnabled(isJarOpen && connectionState != ConnectionState.CONNECTED);
1031 menuBar.startServerMenu.setText(I18n.translate(connectionState != ConnectionState.HOSTING ? "menu.collab.server.start" : "menu.collab.server.stop"));
1032
1033 menuBar.closeJarMenu.setEnabled(isJarOpen);
1034 menuBar.openMappingsMenus.forEach(item -> item.setEnabled(isJarOpen));
1035 menuBar.saveMappingsMenu.setEnabled(isJarOpen && enigmaMappingsFileChooser.getSelectedFile() != null && connectionState != ConnectionState.CONNECTED);
1036 menuBar.saveMappingsMenus.forEach(item -> item.setEnabled(isJarOpen));
1037 menuBar.closeMappingsMenu.setEnabled(isJarOpen);
1038 menuBar.exportSourceMenu.setEnabled(isJarOpen);
1039 menuBar.exportJarMenu.setEnabled(isJarOpen);
1040
1041 connectionStatusLabel.setText(I18n.translate(connectionState == ConnectionState.NOT_CONNECTED ? "status.disconnected" : "status.connected"));
1042
1043 if (connectionState == ConnectionState.NOT_CONNECTED) {
1044 logSplit.setLeftComponent(null);
1045 splitRight.setRightComponent(tabs);
1046 } else {
1047 splitRight.setRightComponent(logSplit);
1048 logSplit.setLeftComponent(tabs);
1049 }
1050 }
1051
1052 public void setConnectionState(ConnectionState state) {
1053 connectionState = state;
1054 statusLabel.setText(I18n.translate("status.ready"));
1055 updateUiState();
1056 }
1057
923} 1058}