summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/gui/Gui.java
diff options
context:
space:
mode:
authorGravatar jeff2015-02-03 22:00:53 -0500
committerGravatar jeff2015-02-03 22:00:53 -0500
commit52ab426d8fad3dbee7e728f523a35af94facebda (patch)
tree146fadfd8e639a909d6c1d6a193e7eddeab0be4a /src/cuchaz/enigma/gui/Gui.java
downloadenigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.tar.gz
enigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.tar.xz
enigma-fork-52ab426d8fad3dbee7e728f523a35af94facebda.zip
oops, don't depend on local procyon project
Diffstat (limited to 'src/cuchaz/enigma/gui/Gui.java')
-rw-r--r--src/cuchaz/enigma/gui/Gui.java1164
1 files changed, 1164 insertions, 0 deletions
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java
new file mode 100644
index 0000000..e652202
--- /dev/null
+++ b/src/cuchaz/enigma/gui/Gui.java
@@ -0,0 +1,1164 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.BorderLayout;
14import java.awt.Color;
15import java.awt.Container;
16import java.awt.Dimension;
17import java.awt.FlowLayout;
18import java.awt.GridLayout;
19import java.awt.Rectangle;
20import java.awt.event.ActionEvent;
21import java.awt.event.ActionListener;
22import java.awt.event.InputEvent;
23import java.awt.event.KeyAdapter;
24import java.awt.event.KeyEvent;
25import java.awt.event.MouseAdapter;
26import java.awt.event.MouseEvent;
27import java.awt.event.WindowAdapter;
28import java.awt.event.WindowEvent;
29import java.io.File;
30import java.io.IOException;
31import java.lang.Thread.UncaughtExceptionHandler;
32import java.util.Collection;
33import java.util.Collections;
34import java.util.List;
35import java.util.Vector;
36import java.util.jar.JarFile;
37
38import javax.swing.BorderFactory;
39import javax.swing.JEditorPane;
40import javax.swing.JFileChooser;
41import javax.swing.JFrame;
42import javax.swing.JLabel;
43import javax.swing.JList;
44import javax.swing.JMenu;
45import javax.swing.JMenuBar;
46import javax.swing.JMenuItem;
47import javax.swing.JOptionPane;
48import javax.swing.JPanel;
49import javax.swing.JPopupMenu;
50import javax.swing.JScrollPane;
51import javax.swing.JSplitPane;
52import javax.swing.JTabbedPane;
53import javax.swing.JTextField;
54import javax.swing.JTree;
55import javax.swing.KeyStroke;
56import javax.swing.ListSelectionModel;
57import javax.swing.SwingUtilities;
58import javax.swing.Timer;
59import javax.swing.WindowConstants;
60import javax.swing.event.CaretEvent;
61import javax.swing.event.CaretListener;
62import javax.swing.text.BadLocationException;
63import javax.swing.text.Highlighter;
64import javax.swing.tree.DefaultTreeModel;
65import javax.swing.tree.TreeNode;
66import javax.swing.tree.TreePath;
67
68import jsyntaxpane.DefaultSyntaxKit;
69
70import com.google.common.collect.Lists;
71
72import cuchaz.enigma.Constants;
73import cuchaz.enigma.analysis.BehaviorReferenceTreeNode;
74import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
75import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
76import cuchaz.enigma.analysis.EntryReference;
77import cuchaz.enigma.analysis.FieldReferenceTreeNode;
78import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
79import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
80import cuchaz.enigma.analysis.ReferenceTreeNode;
81import cuchaz.enigma.analysis.Token;
82import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener;
83import cuchaz.enigma.mapping.ArgumentEntry;
84import cuchaz.enigma.mapping.ClassEntry;
85import cuchaz.enigma.mapping.ConstructorEntry;
86import cuchaz.enigma.mapping.Entry;
87import cuchaz.enigma.mapping.FieldEntry;
88import cuchaz.enigma.mapping.IllegalNameException;
89import cuchaz.enigma.mapping.MappingParseException;
90import cuchaz.enigma.mapping.MethodEntry;
91
92public class Gui {
93
94 private GuiController m_controller;
95
96 // controls
97 private JFrame m_frame;
98 private ClassSelector m_obfClasses;
99 private ClassSelector m_deobfClasses;
100 private JEditorPane m_editor;
101 private JPanel m_classesPanel;
102 private JSplitPane m_splitClasses;
103 private JPanel m_infoPanel;
104 private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter;
105 private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter;
106 private OtherHighlightPainter m_otherHighlightPainter;
107 private SelectionHighlightPainter m_selectionHighlightPainter;
108 private JTree m_inheritanceTree;
109 private JTree m_implementationsTree;
110 private JTree m_callsTree;
111 private JList<Token> m_tokens;
112 private JTabbedPane m_tabs;
113
114 // dynamic menu items
115 private JMenuItem m_closeJarMenu;
116 private JMenuItem m_openMappingsMenu;
117 private JMenuItem m_saveMappingsMenu;
118 private JMenuItem m_saveMappingsAsMenu;
119 private JMenuItem m_closeMappingsMenu;
120 private JMenuItem m_renameMenu;
121 private JMenuItem m_showInheritanceMenu;
122 private JMenuItem m_openEntryMenu;
123 private JMenuItem m_openPreviousMenu;
124 private JMenuItem m_showCallsMenu;
125 private JMenuItem m_showImplementationsMenu;
126 private JMenuItem m_toggleMappingMenu;
127 private JMenuItem m_exportSourceMenu;
128 private JMenuItem m_exportJarMenu;
129
130 // state
131 private EntryReference<Entry,Entry> m_reference;
132 private JFileChooser m_jarFileChooser;
133 private JFileChooser m_mappingsFileChooser;
134 private JFileChooser m_exportSourceFileChooser;
135 private JFileChooser m_exportJarFileChooser;
136
137 public Gui() {
138
139 // init frame
140 m_frame = new JFrame(Constants.Name);
141 final Container pane = m_frame.getContentPane();
142 pane.setLayout(new BorderLayout());
143
144 if (Boolean.parseBoolean(System.getProperty("enigma.catchExceptions", "true"))) {
145 // install a global exception handler to the event thread
146 CrashDialog.init(m_frame);
147 Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
148 @Override
149 public void uncaughtException(Thread thread, Throwable ex) {
150 ex.printStackTrace(System.err);
151 CrashDialog.show(ex);
152 }
153 });
154 }
155
156 m_controller = new GuiController(this);
157
158 // init file choosers
159 m_jarFileChooser = new JFileChooser();
160 m_mappingsFileChooser = new JFileChooser();
161 m_exportSourceFileChooser = new JFileChooser();
162 m_exportSourceFileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
163 m_exportJarFileChooser = new JFileChooser();
164
165 // init obfuscated classes list
166 m_obfClasses = new ClassSelector(ClassSelector.ObfuscatedClassEntryComparator);
167 m_obfClasses.setListener(new ClassSelectionListener() {
168 @Override
169 public void onSelectClass(ClassEntry classEntry) {
170 navigateTo(classEntry);
171 }
172 });
173 JScrollPane obfScroller = new JScrollPane(m_obfClasses);
174 JPanel obfPanel = new JPanel();
175 obfPanel.setLayout(new BorderLayout());
176 obfPanel.add(new JLabel("Obfuscated Classes"), BorderLayout.NORTH);
177 obfPanel.add(obfScroller, BorderLayout.CENTER);
178
179 // init deobfuscated classes list
180 m_deobfClasses = new ClassSelector(ClassSelector.DeobfuscatedClassEntryComparator);
181 m_deobfClasses.setListener(new ClassSelectionListener() {
182 @Override
183 public void onSelectClass(ClassEntry classEntry) {
184 navigateTo(classEntry);
185 }
186 });
187 JScrollPane deobfScroller = new JScrollPane(m_deobfClasses);
188 JPanel deobfPanel = new JPanel();
189 deobfPanel.setLayout(new BorderLayout());
190 deobfPanel.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH);
191 deobfPanel.add(deobfScroller, BorderLayout.CENTER);
192
193 // set up classes panel (don't add the splitter yet)
194 m_splitClasses = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel);
195 m_splitClasses.setResizeWeight(0.3);
196 m_classesPanel = new JPanel();
197 m_classesPanel.setLayout(new BorderLayout());
198 m_classesPanel.setPreferredSize(new Dimension(250, 0));
199
200 // init info panel
201 m_infoPanel = new JPanel();
202 m_infoPanel.setLayout(new GridLayout(4, 1, 0, 0));
203 m_infoPanel.setPreferredSize(new Dimension(0, 100));
204 m_infoPanel.setBorder(BorderFactory.createTitledBorder("Identifier Info"));
205 clearReference();
206
207 // init editor
208 DefaultSyntaxKit.initKit();
209 m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter();
210 m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter();
211 m_otherHighlightPainter = new OtherHighlightPainter();
212 m_selectionHighlightPainter = new SelectionHighlightPainter();
213 m_editor = new JEditorPane();
214 m_editor.setEditable(false);
215 m_editor.setCaret(new BrowserCaret());
216 JScrollPane sourceScroller = new JScrollPane(m_editor);
217 m_editor.setContentType("text/java");
218 m_editor.addCaretListener(new CaretListener() {
219 @Override
220 public void caretUpdate(CaretEvent event) {
221 onCaretMove(event.getDot());
222 }
223 });
224 m_editor.addKeyListener(new KeyAdapter() {
225 @Override
226 public void keyPressed(KeyEvent event) {
227 switch (event.getKeyCode()) {
228 case KeyEvent.VK_R:
229 m_renameMenu.doClick();
230 break;
231
232 case KeyEvent.VK_I:
233 m_showInheritanceMenu.doClick();
234 break;
235
236 case KeyEvent.VK_M:
237 m_showImplementationsMenu.doClick();
238 break;
239
240 case KeyEvent.VK_N:
241 m_openEntryMenu.doClick();
242 break;
243
244 case KeyEvent.VK_P:
245 m_openPreviousMenu.doClick();
246 break;
247
248 case KeyEvent.VK_C:
249 m_showCallsMenu.doClick();
250 break;
251
252 case KeyEvent.VK_T:
253 m_toggleMappingMenu.doClick();
254 break;
255 }
256 }
257 });
258
259 // turn off token highlighting (it's wrong most of the time anyway...)
260 DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit();
261 kit.toggleComponent(m_editor, "jsyntaxpane.components.TokenMarker");
262
263 // init editor popup menu
264 JPopupMenu popupMenu = new JPopupMenu();
265 m_editor.setComponentPopupMenu(popupMenu);
266 {
267 JMenuItem menu = new JMenuItem("Rename");
268 menu.addActionListener(new ActionListener() {
269 @Override
270 public void actionPerformed(ActionEvent event) {
271 startRename();
272 }
273 });
274 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0));
275 menu.setEnabled(false);
276 popupMenu.add(menu);
277 m_renameMenu = menu;
278 }
279 {
280 JMenuItem menu = new JMenuItem("Show Inheritance");
281 menu.addActionListener(new ActionListener() {
282 @Override
283 public void actionPerformed(ActionEvent event) {
284 showInheritance();
285 }
286 });
287 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, 0));
288 menu.setEnabled(false);
289 popupMenu.add(menu);
290 m_showInheritanceMenu = menu;
291 }
292 {
293 JMenuItem menu = new JMenuItem("Show Implementations");
294 menu.addActionListener(new ActionListener() {
295 @Override
296 public void actionPerformed(ActionEvent event) {
297 showImplementations();
298 }
299 });
300 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, 0));
301 menu.setEnabled(false);
302 popupMenu.add(menu);
303 m_showImplementationsMenu = menu;
304 }
305 {
306 JMenuItem menu = new JMenuItem("Show Calls");
307 menu.addActionListener(new ActionListener() {
308 @Override
309 public void actionPerformed(ActionEvent event) {
310 showCalls();
311 }
312 });
313 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0));
314 menu.setEnabled(false);
315 popupMenu.add(menu);
316 m_showCallsMenu = menu;
317 }
318 {
319 JMenuItem menu = new JMenuItem("Go to Declaration");
320 menu.addActionListener(new ActionListener() {
321 @Override
322 public void actionPerformed(ActionEvent event) {
323 navigateTo(m_reference.entry);
324 }
325 });
326 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, 0));
327 menu.setEnabled(false);
328 popupMenu.add(menu);
329 m_openEntryMenu = menu;
330 }
331 {
332 JMenuItem menu = new JMenuItem("Go to previous");
333 menu.addActionListener(new ActionListener() {
334 @Override
335 public void actionPerformed(ActionEvent event) {
336 m_controller.openPreviousReference();
337 }
338 });
339 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0));
340 menu.setEnabled(false);
341 popupMenu.add(menu);
342 m_openPreviousMenu = menu;
343 }
344 {
345 JMenuItem menu = new JMenuItem("Mark as deobfuscated");
346 menu.addActionListener(new ActionListener() {
347 @Override
348 public void actionPerformed(ActionEvent event) {
349 toggleMapping();
350 }
351 });
352 menu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0));
353 menu.setEnabled(false);
354 popupMenu.add(menu);
355 m_toggleMappingMenu = menu;
356 }
357
358 // init inheritance panel
359 m_inheritanceTree = new JTree();
360 m_inheritanceTree.setModel(null);
361 m_inheritanceTree.addMouseListener(new MouseAdapter() {
362 @Override
363 public void mouseClicked(MouseEvent event) {
364 if (event.getClickCount() == 2) {
365 // get the selected node
366 TreePath path = m_inheritanceTree.getSelectionPath();
367 if (path == null) {
368 return;
369 }
370
371 Object node = path.getLastPathComponent();
372 if (node instanceof ClassInheritanceTreeNode) {
373 ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node;
374 navigateTo(new ClassEntry(classNode.getObfClassName()));
375 } else if (node instanceof MethodInheritanceTreeNode) {
376 MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node;
377 if (methodNode.isImplemented()) {
378 navigateTo(methodNode.getMethodEntry());
379 }
380 }
381 }
382 }
383 });
384 JPanel inheritancePanel = new JPanel();
385 inheritancePanel.setLayout(new BorderLayout());
386 inheritancePanel.add(new JScrollPane(m_inheritanceTree));
387
388 // init implementations panel
389 m_implementationsTree = new JTree();
390 m_implementationsTree.setModel(null);
391 m_implementationsTree.addMouseListener(new MouseAdapter() {
392 @Override
393 public void mouseClicked(MouseEvent event) {
394 if (event.getClickCount() == 2) {
395 // get the selected node
396 TreePath path = m_implementationsTree.getSelectionPath();
397 if (path == null) {
398 return;
399 }
400
401 Object node = path.getLastPathComponent();
402 if (node instanceof ClassImplementationsTreeNode) {
403 ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node;
404 navigateTo(classNode.getClassEntry());
405 } else if (node instanceof MethodImplementationsTreeNode) {
406 MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node;
407 navigateTo(methodNode.getMethodEntry());
408 }
409 }
410 }
411 });
412 JPanel implementationsPanel = new JPanel();
413 implementationsPanel.setLayout(new BorderLayout());
414 implementationsPanel.add(new JScrollPane(m_implementationsTree));
415
416 // init call panel
417 m_callsTree = new JTree();
418 m_callsTree.setModel(null);
419 m_callsTree.addMouseListener(new MouseAdapter() {
420 @SuppressWarnings("unchecked")
421 @Override
422 public void mouseClicked(MouseEvent event) {
423 if (event.getClickCount() == 2) {
424 // get the selected node
425 TreePath path = m_callsTree.getSelectionPath();
426 if (path == null) {
427 return;
428 }
429
430 Object node = path.getLastPathComponent();
431 if (node instanceof ReferenceTreeNode) {
432 ReferenceTreeNode<Entry,Entry> referenceNode = ((ReferenceTreeNode<Entry,Entry>)node);
433 if (referenceNode.getReference() != null) {
434 navigateTo(referenceNode.getReference());
435 } else {
436 navigateTo(referenceNode.getEntry());
437 }
438 }
439 }
440 }
441 });
442 m_tokens = new JList<Token>();
443 m_tokens.setCellRenderer(new TokenListCellRenderer(m_controller));
444 m_tokens.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
445 m_tokens.setLayoutOrientation(JList.VERTICAL);
446 m_tokens.addMouseListener(new MouseAdapter() {
447 @Override
448 public void mouseClicked(MouseEvent event) {
449 if (event.getClickCount() == 2) {
450 Token selected = m_tokens.getSelectedValue();
451 if (selected != null) {
452 showToken(selected);
453 }
454 }
455 }
456 });
457 m_tokens.setPreferredSize(new Dimension(0, 200));
458 m_tokens.setMinimumSize(new Dimension(0, 200));
459 JSplitPane callPanel = new JSplitPane(
460 JSplitPane.VERTICAL_SPLIT,
461 true,
462 new JScrollPane(m_callsTree),
463 new JScrollPane(m_tokens)
464 );
465 callPanel.setResizeWeight(1); // let the top side take all the slack
466 callPanel.resetToPreferredSizes();
467
468 // layout controls
469 JPanel centerPanel = new JPanel();
470 centerPanel.setLayout(new BorderLayout());
471 centerPanel.add(m_infoPanel, BorderLayout.NORTH);
472 centerPanel.add(sourceScroller, BorderLayout.CENTER);
473 m_tabs = new JTabbedPane();
474 m_tabs.setPreferredSize(new Dimension(250, 0));
475 m_tabs.addTab("Inheritance", inheritancePanel);
476 m_tabs.addTab("Implementations", implementationsPanel);
477 m_tabs.addTab("Call Graph", callPanel);
478 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs);
479 splitRight.setResizeWeight(1); // let the left side take all the slack
480 splitRight.resetToPreferredSizes();
481 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight);
482 splitCenter.setResizeWeight(0); // let the right side take all the slack
483 pane.add(splitCenter, BorderLayout.CENTER);
484
485 // init menus
486 JMenuBar menuBar = new JMenuBar();
487 m_frame.setJMenuBar(menuBar);
488 {
489 JMenu menu = new JMenu("File");
490 menuBar.add(menu);
491 {
492 JMenuItem item = new JMenuItem("Open Jar...");
493 menu.add(item);
494 item.addActionListener(new ActionListener() {
495 @Override
496 public void actionPerformed(ActionEvent event) {
497 if (m_jarFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
498 // load the jar in a separate thread
499 new Thread() {
500 @Override
501 public void run() {
502 try {
503 m_controller.openJar(new JarFile(m_jarFileChooser.getSelectedFile()));
504 } catch (IOException ex) {
505 throw new Error(ex);
506 }
507 }
508 }.start();
509 }
510 }
511 });
512 }
513 {
514 JMenuItem item = new JMenuItem("Close Jar");
515 menu.add(item);
516 item.addActionListener(new ActionListener() {
517 @Override
518 public void actionPerformed(ActionEvent event) {
519 m_controller.closeJar();
520 }
521 });
522 m_closeJarMenu = item;
523 }
524 menu.addSeparator();
525 {
526 JMenuItem item = new JMenuItem("Open Mappings...");
527 menu.add(item);
528 item.addActionListener(new ActionListener() {
529 @Override
530 public void actionPerformed(ActionEvent event) {
531 if (m_mappingsFileChooser.showOpenDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
532 try {
533 m_controller.openMappings(m_mappingsFileChooser.getSelectedFile());
534 } catch (IOException ex) {
535 throw new Error(ex);
536 } catch (MappingParseException ex) {
537 JOptionPane.showMessageDialog(m_frame, ex.getMessage());
538 }
539 }
540 }
541 });
542 m_openMappingsMenu = item;
543 }
544 {
545 JMenuItem item = new JMenuItem("Save Mappings");
546 menu.add(item);
547 item.addActionListener(new ActionListener() {
548 @Override
549 public void actionPerformed(ActionEvent event) {
550 try {
551 m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile());
552 } catch (IOException ex) {
553 throw new Error(ex);
554 }
555 }
556 });
557 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
558 m_saveMappingsMenu = item;
559 }
560 {
561 JMenuItem item = new JMenuItem("Save Mappings As...");
562 menu.add(item);
563 item.addActionListener(new ActionListener() {
564 @Override
565 public void actionPerformed(ActionEvent event) {
566 if (m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
567 try {
568 m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile());
569 m_saveMappingsMenu.setEnabled(true);
570 } catch (IOException ex) {
571 throw new Error(ex);
572 }
573 }
574 }
575 });
576 item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
577 m_saveMappingsAsMenu = item;
578 }
579 {
580 JMenuItem item = new JMenuItem("Close Mappings");
581 menu.add(item);
582 item.addActionListener(new ActionListener() {
583 @Override
584 public void actionPerformed(ActionEvent event) {
585 m_controller.closeMappings();
586 }
587 });
588 m_closeMappingsMenu = item;
589 }
590 menu.addSeparator();
591 {
592 JMenuItem item = new JMenuItem("Export Source...");
593 menu.add(item);
594 item.addActionListener(new ActionListener() {
595 @Override
596 public void actionPerformed(ActionEvent event) {
597 if (m_exportSourceFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
598 m_controller.exportSource(m_exportSourceFileChooser.getSelectedFile());
599 }
600 }
601 });
602 m_exportSourceMenu = item;
603 }
604 {
605 JMenuItem item = new JMenuItem("Export Jar...");
606 menu.add(item);
607 item.addActionListener(new ActionListener() {
608 @Override
609 public void actionPerformed(ActionEvent event) {
610 if (m_exportJarFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
611 m_controller.exportJar(m_exportJarFileChooser.getSelectedFile());
612 }
613 }
614 });
615 m_exportJarMenu = item;
616 }
617 menu.addSeparator();
618 {
619 JMenuItem item = new JMenuItem("Exit");
620 menu.add(item);
621 item.addActionListener(new ActionListener() {
622 @Override
623 public void actionPerformed(ActionEvent event) {
624 close();
625 }
626 });
627 }
628 }
629 {
630 JMenu menu = new JMenu("Help");
631 menuBar.add(menu);
632 {
633 JMenuItem item = new JMenuItem("About");
634 menu.add(item);
635 item.addActionListener(new ActionListener() {
636 @Override
637 public void actionPerformed(ActionEvent event) {
638 AboutDialog.show(m_frame);
639 }
640 });
641 }
642 }
643
644 // init state
645 onCloseJar();
646
647 m_frame.addWindowListener(new WindowAdapter() {
648 @Override
649 public void windowClosing(WindowEvent event) {
650 close();
651 }
652 });
653
654 // show the frame
655 pane.doLayout();
656 m_frame.setSize(1024, 576);
657 m_frame.setMinimumSize(new Dimension(640, 480));
658 m_frame.setVisible(true);
659 m_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
660 }
661
662 public JFrame getFrame() {
663 return m_frame;
664 }
665
666 public GuiController getController() {
667 return m_controller;
668 }
669
670 public void onStartOpenJar() {
671 m_classesPanel.removeAll();
672 JPanel panel = new JPanel();
673 panel.setLayout(new FlowLayout());
674 panel.add(new JLabel("Loading..."));
675 m_classesPanel.add(panel);
676 redraw();
677 }
678
679 public void onFinishOpenJar(String jarName) {
680 // update gui
681 m_frame.setTitle(Constants.Name + " - " + jarName);
682 m_classesPanel.removeAll();
683 m_classesPanel.add(m_splitClasses);
684 setSource(null);
685
686 // update menu
687 m_closeJarMenu.setEnabled(true);
688 m_openMappingsMenu.setEnabled(true);
689 m_saveMappingsMenu.setEnabled(false);
690 m_saveMappingsAsMenu.setEnabled(true);
691 m_closeMappingsMenu.setEnabled(true);
692 m_exportSourceMenu.setEnabled(true);
693 m_exportJarMenu.setEnabled(true);
694
695 redraw();
696 }
697
698 public void onCloseJar() {
699 // update gui
700 m_frame.setTitle(Constants.Name);
701 setObfClasses(null);
702 setDeobfClasses(null);
703 setSource(null);
704 m_classesPanel.removeAll();
705
706 // update menu
707 m_closeJarMenu.setEnabled(false);
708 m_openMappingsMenu.setEnabled(false);
709 m_saveMappingsMenu.setEnabled(false);
710 m_saveMappingsAsMenu.setEnabled(false);
711 m_closeMappingsMenu.setEnabled(false);
712 m_exportSourceMenu.setEnabled(false);
713 m_exportJarMenu.setEnabled(false);
714
715 redraw();
716 }
717
718 public void setObfClasses(Collection<ClassEntry> obfClasses) {
719 m_obfClasses.setClasses(obfClasses);
720 }
721
722 public void setDeobfClasses(Collection<ClassEntry> deobfClasses) {
723 m_deobfClasses.setClasses(deobfClasses);
724 }
725
726 public void setMappingsFile(File file) {
727 m_mappingsFileChooser.setSelectedFile(file);
728 m_saveMappingsMenu.setEnabled(file != null);
729 }
730
731 public void setSource(String source) {
732 m_editor.getHighlighter().removeAllHighlights();
733 m_editor.setText(source);
734 }
735
736 public void showToken(final Token token) {
737 if (token == null) {
738 throw new IllegalArgumentException("Token cannot be null!");
739 }
740
741 // set the caret position to the token
742 m_editor.setCaretPosition(token.start);
743 m_editor.grabFocus();
744
745 try {
746 // make sure the token is visible in the scroll window
747 Rectangle start = m_editor.modelToView(token.start);
748 Rectangle end = m_editor.modelToView(token.end);
749 final Rectangle show = start.union(end);
750 show.grow(start.width * 10, start.height * 6);
751 SwingUtilities.invokeLater(new Runnable() {
752 @Override
753 public void run() {
754 m_editor.scrollRectToVisible(show);
755 }
756 });
757 } catch (BadLocationException ex) {
758 throw new Error(ex);
759 }
760
761 // highlight the token momentarily
762 final Timer timer = new Timer(200, new ActionListener() {
763 private int m_counter = 0;
764 private Object m_highlight = null;
765
766 @Override
767 public void actionPerformed(ActionEvent event) {
768 if (m_counter % 2 == 0) {
769 try {
770 m_highlight = m_editor.getHighlighter().addHighlight(token.start, token.end, m_selectionHighlightPainter);
771 } catch (BadLocationException ex) {
772 // don't care
773 }
774 } else if (m_highlight != null) {
775 m_editor.getHighlighter().removeHighlight(m_highlight);
776 }
777
778 if (m_counter++ > 6) {
779 Timer timer = (Timer)event.getSource();
780 timer.stop();
781 }
782 }
783 });
784 timer.start();
785
786 redraw();
787 }
788
789 public void showTokens(Collection<Token> tokens) {
790 Vector<Token> sortedTokens = new Vector<Token>(tokens);
791 Collections.sort(sortedTokens);
792 if (sortedTokens.size() > 1) {
793 // sort the tokens and update the tokens panel
794 m_tokens.setListData(sortedTokens);
795 m_tokens.setSelectedIndex(0);
796 } else {
797 m_tokens.setListData(new Vector<Token>());
798 }
799
800 // show the first token
801 showToken(sortedTokens.get(0));
802 }
803
804 public void setHighlightedTokens(Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens) {
805
806 // remove any old highlighters
807 m_editor.getHighlighter().removeAllHighlights();
808
809 // color things based on the index
810 if (obfuscatedTokens != null) {
811 setHighlightedTokens(obfuscatedTokens, m_obfuscatedHighlightPainter);
812 }
813 if (deobfuscatedTokens != null) {
814 setHighlightedTokens(deobfuscatedTokens, m_deobfuscatedHighlightPainter);
815 }
816 if (otherTokens != null) {
817 setHighlightedTokens(otherTokens, m_otherHighlightPainter);
818 }
819
820 redraw();
821 }
822
823 private void setHighlightedTokens(Iterable<Token> tokens, Highlighter.HighlightPainter painter) {
824 for (Token token : tokens) {
825 try {
826 m_editor.getHighlighter().addHighlight(token.start, token.end, painter);
827 } catch (BadLocationException ex) {
828 throw new IllegalArgumentException(ex);
829 }
830 }
831 }
832
833 private void clearReference() {
834 m_infoPanel.removeAll();
835 JLabel label = new JLabel("No identifier selected");
836 GuiTricks.unboldLabel(label);
837 label.setHorizontalAlignment(JLabel.CENTER);
838 m_infoPanel.add(label);
839
840 redraw();
841 }
842
843 private void showReference(EntryReference<Entry,Entry> reference) {
844 if (reference == null) {
845 clearReference();
846 return;
847 }
848
849 m_reference = reference;
850
851 m_infoPanel.removeAll();
852 if (reference.entry instanceof ClassEntry) {
853 showClassEntry((ClassEntry)m_reference.entry);
854 } else if (m_reference.entry instanceof FieldEntry) {
855 showFieldEntry((FieldEntry)m_reference.entry);
856 } else if (m_reference.entry instanceof MethodEntry) {
857 showMethodEntry((MethodEntry)m_reference.entry);
858 } else if (m_reference.entry instanceof ConstructorEntry) {
859 showConstructorEntry((ConstructorEntry)m_reference.entry);
860 } else if (m_reference.entry instanceof ArgumentEntry) {
861 showArgumentEntry((ArgumentEntry)m_reference.entry);
862 } else {
863 throw new Error("Unknown entry type: " + m_reference.entry.getClass().getName());
864 }
865
866 redraw();
867 }
868
869 private void showClassEntry(ClassEntry entry) {
870 addNameValue(m_infoPanel, "Class", entry.getName());
871 }
872
873 private void showFieldEntry(FieldEntry entry) {
874 addNameValue(m_infoPanel, "Field", entry.getName());
875 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
876 }
877
878 private void showMethodEntry(MethodEntry entry) {
879 addNameValue(m_infoPanel, "Method", entry.getName());
880 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
881 addNameValue(m_infoPanel, "Signature", entry.getSignature());
882 }
883
884 private void showConstructorEntry(ConstructorEntry entry) {
885 addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName());
886 addNameValue(m_infoPanel, "Signature", entry.getSignature());
887 }
888
889 private void showArgumentEntry(ArgumentEntry entry) {
890 addNameValue(m_infoPanel, "Argument", entry.getName());
891 addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName());
892 addNameValue(m_infoPanel, "Method", entry.getBehaviorEntry().getName());
893 addNameValue(m_infoPanel, "Index", Integer.toString(entry.getIndex()));
894 }
895
896 private void addNameValue(JPanel container, String name, String value) {
897 JPanel panel = new JPanel();
898 panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0));
899 container.add(panel);
900
901 JLabel label = new JLabel(name + ":", JLabel.RIGHT);
902 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
903 panel.add(label);
904
905 panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT)));
906 }
907
908 private void onCaretMove(int pos) {
909
910 Token token = m_controller.getToken(pos);
911 boolean isToken = token != null;
912
913 m_reference = m_controller.getDeobfReference(token);
914 boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry;
915 boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry;
916 boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry;
917 boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry;
918 boolean isInJar = isToken && m_controller.entryIsInJar(m_reference.entry);
919 boolean isRenameable = isToken && m_controller.referenceIsRenameable(m_reference);
920
921 if (isToken) {
922 showReference(m_reference);
923 } else {
924 clearReference();
925 }
926
927 m_renameMenu.setEnabled(isRenameable && isToken);
928 m_showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry);
929 m_showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry);
930 m_showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry);
931 m_openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry));
932 m_openPreviousMenu.setEnabled(m_controller.hasPreviousLocation());
933 m_toggleMappingMenu.setEnabled(isRenameable && isToken);
934
935 if (isToken && m_controller.entryHasDeobfuscatedName(m_reference.entry)) {
936 m_toggleMappingMenu.setText("Reset to obfuscated");
937 } else {
938 m_toggleMappingMenu.setText("Mark as deobfuscated");
939 }
940 }
941
942 private void navigateTo(Entry entry) {
943 if (!m_controller.entryIsInJar(entry)) {
944 // entry is not in the jar. Ignore it
945 return;
946 }
947 if (m_reference != null) {
948 m_controller.savePreviousReference(m_reference);
949 }
950 m_controller.openDeclaration(entry);
951 }
952
953 private void navigateTo(EntryReference<Entry,Entry> reference) {
954 if (!m_controller.entryIsInJar(reference.getLocationClassEntry())) {
955 // reference is not in the jar. Ignore it
956 return;
957 }
958 if (m_reference != null) {
959 m_controller.savePreviousReference(m_reference);
960 }
961 m_controller.openReference(reference);
962 }
963
964 private void startRename() {
965
966 // init the text box
967 final JTextField text = new JTextField();
968 text.setText(m_reference.getNamableName());
969 text.setPreferredSize(new Dimension(360, text.getPreferredSize().height));
970 text.addKeyListener(new KeyAdapter() {
971 @Override
972 public void keyPressed(KeyEvent event) {
973 switch (event.getKeyCode()) {
974 case KeyEvent.VK_ENTER:
975 finishRename(text, true);
976 break;
977
978 case KeyEvent.VK_ESCAPE:
979 finishRename(text, false);
980 break;
981 }
982 }
983 });
984
985 // find the label with the name and replace it with the text box
986 JPanel panel = (JPanel)m_infoPanel.getComponent(0);
987 panel.remove(panel.getComponentCount() - 1);
988 panel.add(text);
989 text.grabFocus();
990 text.selectAll();
991
992 redraw();
993 }
994
995 private void finishRename(JTextField text, boolean saveName) {
996 String newName = text.getText();
997 if (saveName && newName != null && newName.length() > 0) {
998 try {
999 m_controller.rename(m_reference, newName);
1000 } catch (IllegalNameException ex) {
1001 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
1002 text.setToolTipText(ex.getReason());
1003 GuiTricks.showToolTipNow(text);
1004 }
1005 return;
1006 }
1007
1008 // abort the rename
1009 JPanel panel = (JPanel)m_infoPanel.getComponent(0);
1010 panel.remove(panel.getComponentCount() - 1);
1011 panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT)));
1012
1013 m_editor.grabFocus();
1014
1015 redraw();
1016 }
1017
1018 private void showInheritance() {
1019
1020 if (m_reference == null) {
1021 return;
1022 }
1023
1024 m_inheritanceTree.setModel(null);
1025
1026 if (m_reference.entry instanceof ClassEntry) {
1027 // get the class inheritance
1028 ClassInheritanceTreeNode classNode = m_controller.getClassInheritance((ClassEntry)m_reference.entry);
1029
1030 // show the tree at the root
1031 TreePath path = getPathToRoot(classNode);
1032 m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0)));
1033 m_inheritanceTree.expandPath(path);
1034 m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path));
1035 } else if (m_reference.entry instanceof MethodEntry) {
1036 // get the method inheritance
1037 MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance((MethodEntry)m_reference.entry);
1038
1039 // show the tree at the root
1040 TreePath path = getPathToRoot(classNode);
1041 m_inheritanceTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0)));
1042 m_inheritanceTree.expandPath(path);
1043 m_inheritanceTree.setSelectionRow(m_inheritanceTree.getRowForPath(path));
1044 }
1045
1046 m_tabs.setSelectedIndex(0);
1047 redraw();
1048 }
1049
1050 private void showImplementations() {
1051
1052 if (m_reference == null) {
1053 return;
1054 }
1055
1056 m_implementationsTree.setModel(null);
1057
1058 if (m_reference.entry instanceof ClassEntry) {
1059 // get the class implementations
1060 ClassImplementationsTreeNode node = m_controller.getClassImplementations((ClassEntry)m_reference.entry);
1061 if (node != null) {
1062 // show the tree at the root
1063 TreePath path = getPathToRoot(node);
1064 m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0)));
1065 m_implementationsTree.expandPath(path);
1066 m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path));
1067 }
1068 } else if (m_reference.entry instanceof MethodEntry) {
1069 // get the method implementations
1070 MethodImplementationsTreeNode node = m_controller.getMethodImplementations((MethodEntry)m_reference.entry);
1071 if (node != null) {
1072 // show the tree at the root
1073 TreePath path = getPathToRoot(node);
1074 m_implementationsTree.setModel(new DefaultTreeModel((TreeNode)path.getPathComponent(0)));
1075 m_implementationsTree.expandPath(path);
1076 m_implementationsTree.setSelectionRow(m_implementationsTree.getRowForPath(path));
1077 }
1078 }
1079
1080 m_tabs.setSelectedIndex(1);
1081 redraw();
1082 }
1083
1084 private void showCalls() {
1085
1086 if (m_reference == null) {
1087 return;
1088 }
1089
1090 if (m_reference.entry instanceof ClassEntry) {
1091 // look for calls to the default constructor
1092 // TODO: get a list of all the constructors and find calls to all of them
1093 BehaviorReferenceTreeNode node = m_controller.getMethodReferences(new ConstructorEntry((ClassEntry)m_reference.entry, "()V"));
1094 m_callsTree.setModel(new DefaultTreeModel(node));
1095 } else if (m_reference.entry instanceof FieldEntry) {
1096 FieldReferenceTreeNode node = m_controller.getFieldReferences((FieldEntry)m_reference.entry);
1097 m_callsTree.setModel(new DefaultTreeModel(node));
1098 } else if (m_reference.entry instanceof MethodEntry) {
1099 BehaviorReferenceTreeNode node = m_controller.getMethodReferences((MethodEntry)m_reference.entry);
1100 m_callsTree.setModel(new DefaultTreeModel(node));
1101 } else if (m_reference.entry instanceof ConstructorEntry) {
1102 BehaviorReferenceTreeNode node = m_controller.getMethodReferences((ConstructorEntry)m_reference.entry);
1103 m_callsTree.setModel(new DefaultTreeModel(node));
1104 }
1105
1106 m_tabs.setSelectedIndex(2);
1107 redraw();
1108 }
1109
1110 private void toggleMapping() {
1111 if (m_controller.entryHasDeobfuscatedName(m_reference.entry)) {
1112 m_controller.removeMapping(m_reference);
1113 } else {
1114 m_controller.markAsDeobfuscated(m_reference);
1115 }
1116 }
1117
1118 private TreePath getPathToRoot(TreeNode node) {
1119 List<TreeNode> nodes = Lists.newArrayList();
1120 TreeNode n = node;
1121 do {
1122 nodes.add(n);
1123 n = n.getParent();
1124 } while (n != null);
1125 Collections.reverse(nodes);
1126 return new TreePath(nodes.toArray());
1127 }
1128
1129 private void close() {
1130 if (!m_controller.isDirty()) {
1131 // everything is saved, we can exit safely
1132 m_frame.dispose();
1133 } else {
1134 // ask to save before closing
1135 String[] options = { "Save and exit", "Discard changes", "Cancel" };
1136 int response = JOptionPane.showOptionDialog(m_frame, "Your mappings have not been saved yet. Do you want to save?", "Save your changes?", JOptionPane.YES_NO_CANCEL_OPTION,
1137 JOptionPane.QUESTION_MESSAGE, null, options, options[2]);
1138 switch (response) {
1139 case JOptionPane.YES_OPTION: // save and exit
1140 if (m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog(m_frame) == JFileChooser.APPROVE_OPTION) {
1141 try {
1142 m_controller.saveMappings(m_mappingsFileChooser.getSelectedFile());
1143 m_frame.dispose();
1144 } catch (IOException ex) {
1145 throw new Error(ex);
1146 }
1147 }
1148 break;
1149
1150 case JOptionPane.NO_OPTION:
1151 // don't save, exit
1152 m_frame.dispose();
1153 break;
1154
1155 // cancel means do nothing
1156 }
1157 }
1158 }
1159
1160 private void redraw() {
1161 m_frame.validate();
1162 m_frame.repaint();
1163 }
1164}