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