summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/gui')
-rw-r--r--src/main/java/cuchaz/enigma/gui/BrowserCaret.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java521
-rw-r--r--src/main/java/cuchaz/enigma/gui/ClassSelector.java143
-rw-r--r--src/main/java/cuchaz/enigma/gui/CodeReader.java202
-rw-r--r--src/main/java/cuchaz/enigma/gui/Gui.java20
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiController.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/GuiTricks.java52
-rw-r--r--src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java442
-rw-r--r--src/main/java/cuchaz/enigma/gui/ReadableToken.java36
-rw-r--r--src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java29
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java6
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/elements/MenuBar.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java (renamed from src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java)2
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java (renamed from src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java)7
-rw-r--r--src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java (renamed from src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java)8
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java1
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java4
-rw-r--r--src/main/java/cuchaz/enigma/gui/panels/PanelObf.java2
25 files changed, 65 insertions, 1438 deletions
diff --git a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
index a75db36..f58d012 100644
--- a/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
+++ b/src/main/java/cuchaz/enigma/gui/BrowserCaret.java
@@ -15,8 +15,6 @@ import javax.swing.text.Highlighter;
15 15
16public class BrowserCaret extends DefaultCaret { 16public class BrowserCaret extends DefaultCaret {
17 17
18 private static final long serialVersionUID = 1158977422507969940L;
19
20 private static final Highlighter.HighlightPainter selectionPainter = (g, p0, p1, bounds, c) -> { 18 private static final Highlighter.HighlightPainter selectionPainter = (g, p0, p1, bounds, c) -> {
21 }; 19 };
22 20
diff --git a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java b/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
deleted file mode 100644
index e813688..0000000
--- a/src/main/java/cuchaz/enigma/gui/ClassMatchingGui.java
+++ /dev/null
@@ -1,521 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.google.common.collect.BiMap;
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16
17import java.awt.BorderLayout;
18import java.awt.Container;
19import java.awt.Dimension;
20import java.awt.FlowLayout;
21import java.awt.event.ActionListener;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.*;
28
29import cuchaz.enigma.Constants;
30import cuchaz.enigma.Deobfuscator;
31import cuchaz.enigma.convert.*;
32import cuchaz.enigma.mapping.ClassEntry;
33import cuchaz.enigma.mapping.Mappings;
34import cuchaz.enigma.mapping.MappingsChecker;
35import de.sciss.syntaxpane.DefaultSyntaxKit;
36
37
38public class ClassMatchingGui {
39
40 private enum SourceType {
41 Matched {
42 @Override
43 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
44 return matches.getUniqueMatches().keySet();
45 }
46 },
47 Unmatched {
48 @Override
49 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
50 return matches.getUnmatchedSourceClasses();
51 }
52 },
53 Ambiguous {
54 @Override
55 public Collection<ClassEntry> getSourceClasses(ClassMatches matches) {
56 return matches.getAmbiguouslyMatchedSourceClasses();
57 }
58 };
59
60 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
61 JRadioButton button = new JRadioButton(name(), this == getDefault());
62 button.setActionCommand(name());
63 button.addActionListener(listener);
64 group.add(button);
65 return button;
66 }
67
68 public abstract Collection<ClassEntry> getSourceClasses(ClassMatches matches);
69
70 public static SourceType getDefault() {
71 return values()[0];
72 }
73 }
74
75 public interface SaveListener {
76 void save(ClassMatches matches);
77 }
78
79 // controls
80 private JFrame frame;
81 private ClassSelector sourceClasses;
82 private ClassSelector destClasses;
83 private CodeReader sourceReader;
84 private CodeReader destReader;
85 private JLabel sourceClassLabel;
86 private JLabel destClassLabel;
87 private JButton matchButton;
88 private Map<SourceType, JRadioButton> sourceTypeButtons;
89 private JCheckBox advanceCheck;
90 private JCheckBox top10Matches;
91
92 private ClassMatches classMatches;
93 private Deobfuscator sourceDeobfuscator;
94 private Deobfuscator destDeobfuscator;
95 private ClassEntry sourceClass;
96 private ClassEntry destClass;
97 private SourceType sourceType;
98 private SaveListener saveListener;
99
100 public ClassMatchingGui(ClassMatches matches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
101
102 this.classMatches = matches;
103 this.sourceDeobfuscator = sourceDeobfuscator;
104 this.destDeobfuscator = destDeobfuscator;
105
106 // init frame
107 this.frame = new JFrame(Constants.NAME + " - Class Matcher");
108 final Container pane = this.frame.getContentPane();
109 pane.setLayout(new BorderLayout());
110
111 // init source side
112 JPanel sourcePanel = new JPanel();
113 sourcePanel.setLayout(new BoxLayout(sourcePanel, BoxLayout.PAGE_AXIS));
114 sourcePanel.setPreferredSize(new Dimension(200, 0));
115 pane.add(sourcePanel, BorderLayout.WEST);
116 sourcePanel.add(new JLabel("Source Classes"));
117
118 // init source type radios
119 JPanel sourceTypePanel = new JPanel();
120 sourcePanel.add(sourceTypePanel);
121 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
122 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
123 ButtonGroup sourceTypeButtons = new ButtonGroup();
124 this.sourceTypeButtons = Maps.newHashMap();
125 for (SourceType sourceType : SourceType.values()) {
126 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
127 this.sourceTypeButtons.put(sourceType, button);
128 sourceTypePanel.add(button);
129 }
130
131 this.sourceClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
132 this.sourceClasses.setListener(this::setSourceClass);
133 JScrollPane sourceScroller = new JScrollPane(this.sourceClasses);
134 sourcePanel.add(sourceScroller);
135
136 // init dest side
137 JPanel destPanel = new JPanel();
138 destPanel.setLayout(new BoxLayout(destPanel, BoxLayout.PAGE_AXIS));
139 destPanel.setPreferredSize(new Dimension(200, 0));
140 pane.add(destPanel, BorderLayout.WEST);
141 destPanel.add(new JLabel("Destination Classes"));
142
143 this.top10Matches = new JCheckBox("Show only top 10 matches");
144 destPanel.add(this.top10Matches);
145 this.top10Matches.addActionListener(event -> toggleTop10Matches());
146
147 this.destClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
148 this.destClasses.setListener(this::setDestClass);
149 JScrollPane destScroller = new JScrollPane(this.destClasses);
150 destPanel.add(destScroller);
151
152 JButton autoMatchButton = new JButton("AutoMatch");
153 autoMatchButton.addActionListener(event -> autoMatch());
154 destPanel.add(autoMatchButton);
155
156 // init source panels
157 DefaultSyntaxKit.initKit();
158 this.sourceReader = new CodeReader();
159 this.destReader = new CodeReader();
160
161 // init all the splits
162 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, sourcePanel, new JScrollPane(this.sourceReader));
163 splitLeft.setResizeWeight(0); // let the right side take all the slack
164 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(this.destReader), destPanel);
165 splitRight.setResizeWeight(1); // let the left side take all the slack
166 JSplitPane splitCenter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, splitLeft, splitRight);
167 splitCenter.setResizeWeight(0.5); // resize 50:50
168 pane.add(splitCenter, BorderLayout.CENTER);
169 splitCenter.resetToPreferredSizes();
170
171 // init bottom panel
172 JPanel bottomPanel = new JPanel();
173 bottomPanel.setLayout(new FlowLayout());
174
175 this.sourceClassLabel = new JLabel();
176 this.sourceClassLabel.setHorizontalAlignment(SwingConstants.RIGHT);
177 this.destClassLabel = new JLabel();
178 this.destClassLabel.setHorizontalAlignment(SwingConstants.LEFT);
179
180 this.matchButton = new JButton();
181
182 this.advanceCheck = new JCheckBox("Advance to next likely match");
183 this.advanceCheck.addActionListener(event -> {
184 if (this.advanceCheck.isSelected()) {
185 advance();
186 }
187 });
188
189 bottomPanel.add(this.sourceClassLabel);
190 bottomPanel.add(this.matchButton);
191 bottomPanel.add(this.destClassLabel);
192 bottomPanel.add(this.advanceCheck);
193 pane.add(bottomPanel, BorderLayout.SOUTH);
194
195 // show the frame
196 pane.doLayout();
197 this.frame.setSize(1024, 576);
198 this.frame.setMinimumSize(new Dimension(640, 480));
199 this.frame.setVisible(true);
200 this.frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
201
202 // init state
203 updateDestMappings();
204 setSourceType(SourceType.getDefault());
205 updateMatchButton();
206 this.saveListener = null;
207 }
208
209 public void setSaveListener(SaveListener val) {
210 this.saveListener = val;
211 }
212
213 private void updateDestMappings() {
214
215 Mappings newMappings = MappingsConverter.newMappings(this.classMatches, this.sourceDeobfuscator.getMappings(), this.sourceDeobfuscator, this.destDeobfuscator);
216
217 // look for dropped mappings
218 MappingsChecker checker = new MappingsChecker(this.destDeobfuscator.getJarIndex());
219 checker.dropBrokenMappings(newMappings);
220
221 // count them
222 int numDroppedFields = checker.getDroppedFieldMappings().size();
223 int numDroppedMethods = checker.getDroppedMethodMappings().size();
224 System.out.println(String.format("%d mappings from matched classes don't match the dest jar:\n\t%5d fields\n\t%5d methods",
225 numDroppedFields + numDroppedMethods,
226 numDroppedFields,
227 numDroppedMethods
228 ));
229
230 this.destDeobfuscator.setMappings(newMappings);
231 }
232
233 protected void setSourceType(SourceType val) {
234
235 // show the source classes
236 this.sourceType = val;
237 this.sourceClasses.setClasses(deobfuscateClasses(this.sourceType.getSourceClasses(this.classMatches), this.sourceDeobfuscator));
238
239 // update counts
240 for (SourceType sourceType : SourceType.values()) {
241 this.sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
242 sourceType.name(),
243 sourceType.getSourceClasses(this.classMatches).size()
244 ));
245 }
246 }
247
248 private Collection<ClassEntry> deobfuscateClasses(Collection<ClassEntry> in, Deobfuscator deobfuscator) {
249 List<ClassEntry> out = Lists.newArrayList();
250 for (ClassEntry entry : in) {
251
252 ClassEntry deobf = deobfuscator.deobfuscateEntry(entry);
253
254 // make sure we preserve any scores
255 if (entry instanceof ScoredClassEntry) {
256 deobf = new ScoredClassEntry(deobf, ((ScoredClassEntry) entry).getScore());
257 }
258
259 out.add(deobf);
260 }
261 return out;
262 }
263
264 protected void setSourceClass(ClassEntry classEntry) {
265
266 Runnable onGetDestClasses = null;
267 if (this.advanceCheck.isSelected()) {
268 onGetDestClasses = this::pickBestDestClass;
269 }
270
271 setSourceClass(classEntry, onGetDestClasses);
272 }
273
274 protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) {
275
276 // update the current source class
277 this.sourceClass = classEntry;
278 this.sourceClassLabel.setText(this.sourceClass != null ? this.sourceClass.getName() : "");
279
280 if (this.sourceClass != null) {
281
282 // show the dest class(es)
283 ClassMatch match = this.classMatches.getMatchBySource(this.sourceDeobfuscator.obfuscateEntry(this.sourceClass));
284 assert (match != null);
285 if (match.destClasses.isEmpty()) {
286
287 this.destClasses.setClasses(null);
288
289 // run in a separate thread to keep ui responsive
290 new Thread() {
291 @Override
292 public void run() {
293 destClasses.setClasses(deobfuscateClasses(getLikelyMatches(sourceClass), destDeobfuscator));
294 destClasses.expandAll();
295
296 if (onGetDestClasses != null) {
297 onGetDestClasses.run();
298 }
299 }
300 }.start();
301
302 } else {
303
304 this.destClasses.setClasses(deobfuscateClasses(match.destClasses, this.destDeobfuscator));
305 this.destClasses.expandAll();
306
307 if (onGetDestClasses != null) {
308 onGetDestClasses.run();
309 }
310 }
311 }
312
313 setDestClass(null);
314 this.sourceReader.decompileClass(this.sourceClass, this.sourceDeobfuscator, () -> this.sourceReader.navigateToClassDeclaration(this.sourceClass));
315
316 updateMatchButton();
317 }
318
319 private Collection<ClassEntry> getLikelyMatches(ClassEntry sourceClass) {
320
321 ClassEntry obfSourceClass = this.sourceDeobfuscator.obfuscateEntry(sourceClass);
322
323 // set up identifiers
324 ClassNamer namer = new ClassNamer(this.classMatches.getUniqueMatches());
325 ClassIdentifier sourceIdentifier = new ClassIdentifier(this.sourceDeobfuscator.getJar(), this.sourceDeobfuscator.getJarIndex(), namer.getSourceNamer(), true);
326 ClassIdentifier destIdentifier = new ClassIdentifier(this.destDeobfuscator.getJar(), this.destDeobfuscator.getJarIndex(), namer.getDestNamer(), true);
327
328 try {
329
330 // rank all the unmatched dest classes against the source class
331 ClassIdentity sourceIdentity = sourceIdentifier.identify(obfSourceClass);
332 List<ClassEntry> scoredDestClasses = Lists.newArrayList();
333 for (ClassEntry unmatchedDestClass : this.classMatches.getUnmatchedDestClasses()) {
334 ClassIdentity destIdentity = destIdentifier.identify(unmatchedDestClass);
335 float score = 100.0f * (sourceIdentity.getMatchScore(destIdentity) + destIdentity.getMatchScore(sourceIdentity))
336 / (sourceIdentity.getMaxMatchScore() + destIdentity.getMaxMatchScore());
337 scoredDestClasses.add(new ScoredClassEntry(unmatchedDestClass, score));
338 }
339
340 if (this.top10Matches.isSelected() && scoredDestClasses.size() > 10) {
341 Collections.sort(scoredDestClasses, (a, b) -> {
342 ScoredClassEntry sa = (ScoredClassEntry) a;
343 ScoredClassEntry sb = (ScoredClassEntry) b;
344 return -Float.compare(sa.getScore(), sb.getScore());
345 });
346 scoredDestClasses = scoredDestClasses.subList(0, 10);
347 }
348
349 return scoredDestClasses;
350
351 } catch (ClassNotFoundException ex) {
352 throw new Error("Unable to find class " + ex.getMessage());
353 }
354 }
355
356 protected void setDestClass(ClassEntry classEntry) {
357
358 // update the current source class
359 this.destClass = classEntry;
360 this.destClassLabel.setText(this.destClass != null ? this.destClass.getName() : "");
361
362 this.destReader.decompileClass(this.destClass, this.destDeobfuscator, () -> this.destReader.navigateToClassDeclaration(this.destClass));
363
364 updateMatchButton();
365 }
366
367 private void updateMatchButton() {
368
369 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
370 ClassEntry obfDest = this.destDeobfuscator.obfuscateEntry(this.destClass);
371
372 BiMap<ClassEntry, ClassEntry> uniqueMatches = this.classMatches.getUniqueMatches();
373 boolean twoSelected = this.sourceClass != null && this.destClass != null;
374 boolean isMatched = uniqueMatches.containsKey(obfSource) && uniqueMatches.containsValue(obfDest);
375 boolean canMatch = !uniqueMatches.containsKey(obfSource) && !uniqueMatches.containsValue(obfDest);
376
377 GuiTricks.deactivateButton(this.matchButton);
378 if (twoSelected) {
379 if (isMatched) {
380 GuiTricks.activateButton(this.matchButton, "Unmatch", event -> onUnmatchClick());
381 } else if (canMatch) {
382 GuiTricks.activateButton(this.matchButton, "Match", event -> onMatchClick());
383 }
384 }
385 }
386
387 private void onMatchClick() {
388 // precondition: source and dest classes are set correctly
389
390 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
391 ClassEntry obfDest = this.destDeobfuscator.obfuscateEntry(this.destClass);
392
393 // remove the classes from their match
394 this.classMatches.removeSource(obfSource);
395 this.classMatches.removeDest(obfDest);
396
397 // add them as matched classes
398 this.classMatches.add(new ClassMatch(obfSource, obfDest));
399
400 ClassEntry nextClass = null;
401 if (this.advanceCheck.isSelected()) {
402 nextClass = this.sourceClasses.getNextClass(this.sourceClass);
403 }
404
405 save();
406 updateMatches();
407
408 if (nextClass != null) {
409 advance(nextClass);
410 }
411 }
412
413 private void onUnmatchClick() {
414 // precondition: source and dest classes are set to a unique match
415
416 ClassEntry obfSource = this.sourceDeobfuscator.obfuscateEntry(this.sourceClass);
417
418 // remove the source to break the match, then add the source back as unmatched
419 this.classMatches.removeSource(obfSource);
420 this.classMatches.add(new ClassMatch(obfSource, null));
421
422 save();
423 updateMatches();
424 }
425
426 private void updateMatches() {
427 updateDestMappings();
428 setDestClass(null);
429 this.destClasses.setClasses(null);
430 updateMatchButton();
431
432 // remember where we were in the source tree
433 String packageName = this.sourceClasses.getSelectedPackage();
434
435 setSourceType(this.sourceType);
436
437 this.sourceClasses.expandPackage(packageName);
438 }
439
440 private void save() {
441 if (this.saveListener != null) {
442 this.saveListener.save(this.classMatches);
443 }
444 }
445
446 private void autoMatch() {
447
448 System.out.println("Automatching...");
449
450 // compute a new matching
451 ClassMatching matching = MappingsConverter.computeMatching(this.sourceDeobfuscator.getJar(), this.sourceDeobfuscator.getJarIndex(),
452 this.destDeobfuscator.getJar(), this.destDeobfuscator.getJarIndex(), this.classMatches.getUniqueMatches());
453 ClassMatches newMatches = new ClassMatches(matching.matches());
454 System.out.println(String.format("Automatch found %d new matches", newMatches.getUniqueMatches().size() - this.classMatches.getUniqueMatches().size()));
455
456 // update the current matches
457 this.classMatches = newMatches;
458 save();
459 updateMatches();
460 }
461
462 private void advance() {
463 advance(null);
464 }
465
466 private void advance(ClassEntry sourceClass) {
467
468 // make sure we have a source class
469 if (sourceClass == null) {
470 sourceClass = this.sourceClasses.getSelectedClass();
471 if (sourceClass != null) {
472 sourceClass = this.sourceClasses.getNextClass(sourceClass);
473 } else {
474 sourceClass = this.sourceClasses.getFirstClass();
475 }
476 }
477
478 // set the source class
479 setSourceClass(sourceClass, this::pickBestDestClass);
480 this.sourceClasses.setSelectionClass(sourceClass);
481 }
482
483 private void pickBestDestClass() {
484
485 // then, pick the best dest class
486 ClassEntry firstClass = null;
487 ScoredClassEntry bestDestClass = null;
488 for (ClassSelectorPackageNode packageNode : this.destClasses.packageNodes()) {
489 for (ClassSelectorClassNode classNode : this.destClasses.classNodes(packageNode)) {
490 if (firstClass == null) {
491 firstClass = classNode.getClassEntry();
492 }
493 if (classNode.getClassEntry() instanceof ScoredClassEntry) {
494 ScoredClassEntry scoredClass = (ScoredClassEntry) classNode.getClassEntry();
495 if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) {
496 bestDestClass = scoredClass;
497 }
498 }
499 }
500 }
501
502 // pick the entry to show
503 ClassEntry destClass = null;
504 if (bestDestClass != null) {
505 destClass = bestDestClass;
506 } else if (firstClass != null) {
507 destClass = firstClass;
508 }
509
510 setDestClass(destClass);
511 this.destClasses.setSelectionClass(destClass);
512 }
513
514 private void toggleTop10Matches() {
515 if (this.sourceClass != null) {
516 this.destClasses.clearSelection();
517 this.destClasses.setClasses(deobfuscateClasses(getLikelyMatches(this.sourceClass), this.destDeobfuscator));
518 this.destClasses.expandAll();
519 }
520 }
521}
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelector.java b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
index 2ee3b2a..27b4d36 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelector.java
+++ b/src/main/java/cuchaz/enigma/gui/ClassSelector.java
@@ -24,17 +24,13 @@ import javax.swing.tree.DefaultMutableTreeNode;
24import javax.swing.tree.DefaultTreeModel; 24import javax.swing.tree.DefaultTreeModel;
25import javax.swing.tree.TreePath; 25import javax.swing.tree.TreePath;
26 26
27import cuchaz.enigma.gui.node.ClassSelectorClassNode;
28import cuchaz.enigma.gui.node.ClassSelectorPackageNode;
27import cuchaz.enigma.mapping.ClassEntry; 29import cuchaz.enigma.mapping.ClassEntry;
28 30
29public class ClassSelector extends JTree { 31public class ClassSelector extends JTree {
30 32
31 private static final long serialVersionUID = -7632046902384775977L; 33 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = (a, b) -> a.getName().compareTo(b.getName());
32 public static final Comparator<ClassEntry> DEOBF_CLASS_COMPARATOR = (a, b) -> {
33 if (a instanceof ScoredClassEntry && b instanceof ScoredClassEntry) {
34 return Float.compare(((ScoredClassEntry) b).getScore(), ((ScoredClassEntry) a).getScore());
35 }
36 return a.getName().compareTo(b.getName());
37 };
38 34
39 public interface ClassSelectionListener { 35 public interface ClassSelectionListener {
40 void onSelectClass(ClassEntry classEntry); 36 void onSelectClass(ClassEntry classEntry);
@@ -75,6 +71,7 @@ public class ClassSelector extends JTree {
75 } 71 }
76 72
77 public void setClasses(Collection<ClassEntry> classEntries) { 73 public void setClasses(Collection<ClassEntry> classEntries) {
74 String state = getExpansionState(this, 0);
78 if (classEntries == null) { 75 if (classEntries == null) {
79 setModel(null); 76 setModel(null);
80 return; 77 return;
@@ -137,125 +134,45 @@ public class ClassSelector extends JTree {
137 134
138 // finally, update the tree control 135 // finally, update the tree control
139 setModel(new DefaultTreeModel(root)); 136 setModel(new DefaultTreeModel(root));
140 }
141
142 public ClassEntry getSelectedClass() {
143 if (!isSelectionEmpty()) {
144 Object selectedNode = getSelectionPath().getLastPathComponent();
145 if (selectedNode instanceof ClassSelectorClassNode) {
146 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
147 return classNode.getClassEntry();
148 }
149 }
150 return null;
151 }
152
153 public String getSelectedPackage() {
154 if (!isSelectionEmpty()) {
155 Object selectedNode = getSelectionPath().getLastPathComponent();
156 if (selectedNode instanceof ClassSelectorPackageNode) {
157 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) selectedNode;
158 return packageNode.getPackageName();
159 } else if (selectedNode instanceof ClassSelectorClassNode) {
160 ClassSelectorClassNode classNode = (ClassSelectorClassNode) selectedNode;
161 return classNode.getClassEntry().getPackageName();
162 }
163 }
164 return null;
165 }
166
167 public Iterable<ClassSelectorPackageNode> packageNodes() {
168 List<ClassSelectorPackageNode> nodes = Lists.newArrayList();
169 DefaultMutableTreeNode root = (DefaultMutableTreeNode) getModel().getRoot();
170 Enumeration<?> children = root.children();
171 while (children.hasMoreElements()) {
172 ClassSelectorPackageNode packageNode = (ClassSelectorPackageNode) children.nextElement();
173 nodes.add(packageNode);
174 }
175 return nodes;
176 }
177 137
178 public Iterable<ClassSelectorClassNode> classNodes(ClassSelectorPackageNode packageNode) { 138 restoreExpanstionState(this, 0, state);
179 List<ClassSelectorClassNode> nodes = Lists.newArrayList();
180 Enumeration<?> children = packageNode.children();
181 while (children.hasMoreElements()) {
182 ClassSelectorClassNode classNode = (ClassSelectorClassNode) children.nextElement();
183 nodes.add(classNode);
184 }
185 return nodes;
186 } 139 }
187 140
188 public void expandPackage(String packageName) { 141 public boolean isDescendant(TreePath path1, TreePath path2) {
189 if (packageName == null) { 142 int count1 = path1.getPathCount();
190 return; 143 int count2 = path2.getPathCount();
144 if (count1 <= count2) {
145 return false;
191 } 146 }
192 for (ClassSelectorPackageNode packageNode : packageNodes()) { 147 while (count1 != count2) {
193 if (packageNode.getPackageName().equals(packageName)) { 148 path1 = path1.getParentPath();
194 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); 149 count1--;
195 return;
196 }
197 } 150 }
151 return path1.equals(path2);
198 } 152 }
199 153
200 public void expandAll() { 154 public String getExpansionState(JTree tree, int row) {
201 for (ClassSelectorPackageNode packageNode : packageNodes()) { 155 TreePath rowPath = tree.getPathForRow(row);
202 expandPath(new TreePath(new Object[]{getModel().getRoot(), packageNode})); 156 StringBuffer buf = new StringBuffer();
203 } 157 int rowCount = tree.getRowCount();
204 } 158 for (int i = row; i < rowCount; i++) {
205 159 TreePath path = tree.getPathForRow(i);
206 public ClassEntry getFirstClass() { 160 if (i == row || isDescendant(path, rowPath)) {
207 for (ClassSelectorPackageNode packageNode : packageNodes()) { 161 if (tree.isExpanded(path)) {
208 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 162 buf.append("," + String.valueOf(i - row));
209 return classNode.getClassEntry();
210 }
211 }
212 return null;
213 }
214
215 public ClassSelectorPackageNode getPackageNode(ClassEntry entry) {
216 for (ClassSelectorPackageNode packageNode : packageNodes()) {
217 if (packageNode.getPackageName().equals(entry.getPackageName())) {
218 return packageNode;
219 }
220 }
221 return null;
222 }
223
224 public ClassEntry getNextClass(ClassEntry entry) {
225 boolean foundIt = false;
226 for (ClassSelectorPackageNode packageNode : packageNodes()) {
227 if (!foundIt) {
228 // skip to the package with our target in it
229 if (packageNode.getPackageName().equals(entry.getPackageName())) {
230 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
231 if (!foundIt) {
232 if (classNode.getClassEntry().equals(entry)) {
233 foundIt = true;
234 }
235 } else {
236 // return the next class
237 return classNode.getClassEntry();
238 }
239 }
240 } 163 }
241 } else { 164 } else {
242 // return the next class 165 break;
243 for (ClassSelectorClassNode classNode : classNodes(packageNode)) {
244 return classNode.getClassEntry();
245 }
246 } 166 }
247 } 167 }
248 return null; 168 return buf.toString();
249 } 169 }
250 170
251 public void setSelectionClass(ClassEntry classEntry) { 171 public void restoreExpanstionState(JTree tree, int row, String expansionState) {
252 expandPackage(classEntry.getPackageName()); 172 StringTokenizer stok = new StringTokenizer(expansionState, ",");
253 for (ClassSelectorPackageNode packageNode : packageNodes()) { 173 while (stok.hasMoreTokens()) {
254 for (ClassSelectorClassNode classNode : classNodes(packageNode)) { 174 int token = row + Integer.parseInt(stok.nextToken());
255 if (classNode.getClassEntry().equals(classEntry)) { 175 tree.expandRow(token);
256 setSelectionPath(new TreePath(new Object[]{getModel().getRoot(), packageNode, classNode}));
257 }
258 }
259 } 176 }
260 } 177 }
261} 178}
diff --git a/src/main/java/cuchaz/enigma/gui/CodeReader.java b/src/main/java/cuchaz/enigma/gui/CodeReader.java
deleted file mode 100644
index a476fa5..0000000
--- a/src/main/java/cuchaz/enigma/gui/CodeReader.java
+++ /dev/null
@@ -1,202 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.strobel.decompiler.languages.java.ast.CompilationUnit;
14
15import java.awt.Rectangle;
16import java.awt.event.ActionEvent;
17import java.awt.event.ActionListener;
18
19import javax.swing.JEditorPane;
20import javax.swing.SwingUtilities;
21import javax.swing.Timer;
22import javax.swing.text.BadLocationException;
23import javax.swing.text.Highlighter.HighlightPainter;
24
25import cuchaz.enigma.Deobfuscator;
26import cuchaz.enigma.analysis.EntryReference;
27import cuchaz.enigma.analysis.SourceIndex;
28import cuchaz.enigma.analysis.Token;
29import cuchaz.enigma.mapping.ClassEntry;
30import cuchaz.enigma.mapping.Entry;
31import de.sciss.syntaxpane.DefaultSyntaxKit;
32
33
34public class CodeReader extends JEditorPane {
35
36 private static final long serialVersionUID = 3673180950485748810L;
37
38 private static final Object lock = new Object();
39
40 public interface SelectionListener {
41 void onSelect(EntryReference<Entry, Entry> reference);
42 }
43
44 private SelectionHighlightPainter selectionHighlightPainter;
45 private SourceIndex sourceIndex;
46 private SelectionListener selectionListener;
47
48 public CodeReader() {
49
50 setEditable(false);
51 setContentType("text/java");
52
53 // turn off token highlighting (it's wrong most of the time anyway...)
54 DefaultSyntaxKit kit = (DefaultSyntaxKit) getEditorKit();
55 kit.toggleComponent(this, "de.sciss.syntaxpane.components.TokenMarker");
56
57 // hook events
58 addCaretListener(event -> {
59 if (this.selectionListener != null && this.sourceIndex != null) {
60 Token token = this.sourceIndex.getReferenceToken(event.getDot());
61 if (token != null) {
62 this.selectionListener.onSelect(this.sourceIndex.getDeobfReference(token));
63 } else {
64 this.selectionListener.onSelect(null);
65 }
66 }
67 });
68
69 this.selectionHighlightPainter = new SelectionHighlightPainter();
70 this.sourceIndex = null;
71 this.selectionListener = null;
72 }
73
74 public void setSelectionListener(SelectionListener val) {
75 this.selectionListener = val;
76 }
77
78 public void setCode(String code) {
79 // sadly, the java lexer is not thread safe, so we have to serialize all these calls
80 synchronized (lock) {
81 setText(code);
82 }
83 }
84
85 public SourceIndex getSourceIndex() {
86 return this.sourceIndex;
87 }
88
89 public void decompileClass(ClassEntry classEntry, Deobfuscator deobfuscator, Runnable callback) {
90 decompileClass(classEntry, deobfuscator, null, callback);
91 }
92
93 public void decompileClass(final ClassEntry classEntry, final Deobfuscator deobfuscator, final Boolean ignoreBadTokens, final Runnable callback) {
94
95 if (classEntry == null) {
96 setCode(null);
97 return;
98 }
99
100 setCode("(decompiling...)");
101
102 // run decompilation in a separate thread to keep ui responsive
103 new Thread() {
104 @Override
105 public void run() {
106
107 // decompile it
108 CompilationUnit sourceTree = deobfuscator.getSourceTree(classEntry.getOutermostClassName());
109 String source = deobfuscator.getSource(sourceTree);
110 setCode(source);
111 sourceIndex = deobfuscator.getSourceIndex(sourceTree, source, ignoreBadTokens);
112
113 if (callback != null) {
114 callback.run();
115 }
116 }
117 }.start();
118 }
119
120 public void navigateToClassDeclaration(ClassEntry classEntry) {
121
122 // navigate to the class declaration
123 Token token = this.sourceIndex.getDeclarationToken(classEntry);
124 if (token == null) {
125 // couldn't find the class declaration token, might be an anonymous class
126 // look for any declaration in that class instead
127 for (Entry entry : this.sourceIndex.declarations()) {
128 if (entry.getClassEntry().equals(classEntry)) {
129 token = this.sourceIndex.getDeclarationToken(entry);
130 break;
131 }
132 }
133 }
134
135 if (token != null) {
136 navigateToToken(token);
137 } else {
138 // couldn't find anything =(
139 System.out.println("Unable to find declaration in source for " + classEntry);
140 }
141 }
142
143 public void navigateToToken(final Token token) {
144 navigateToToken(this, token, this.selectionHighlightPainter);
145 }
146
147 // HACKHACK: someday we can update the main GUI to use this code reader
148 public static void navigateToToken(final JEditorPane editor, final Token token, final HighlightPainter highlightPainter) {
149
150 // set the caret position to the token
151 editor.setCaretPosition(token.start);
152 editor.grabFocus();
153
154 try {
155 // make sure the token is visible in the scroll window
156 Rectangle start = editor.modelToView(token.start);
157 Rectangle end = editor.modelToView(token.end);
158 final Rectangle show = start.union(end);
159 show.grow(start.width * 10, start.height * 6);
160 SwingUtilities.invokeLater(() -> editor.scrollRectToVisible(show));
161 } catch (BadLocationException ex) {
162 throw new Error(ex);
163 }
164
165 // highlight the token momentarily
166 final Timer timer = new Timer(200, new ActionListener() {
167 private int m_counter = 0;
168 private Object m_highlight = null;
169
170 @Override
171 public void actionPerformed(ActionEvent event) {
172 if (m_counter % 2 == 0) {
173 try {
174 m_highlight = editor.getHighlighter().addHighlight(token.start, token.end, highlightPainter);
175 } catch (BadLocationException ex) {
176 // don't care
177 }
178 } else if (m_highlight != null) {
179 editor.getHighlighter().removeHighlight(m_highlight);
180 }
181
182 if (m_counter++ > 6) {
183 Timer timer = (Timer) event.getSource();
184 timer.stop();
185 }
186 }
187 });
188 timer.start();
189 }
190
191 public void setHighlightedToken(Token token, HighlightPainter painter) {
192 try {
193 getHighlighter().addHighlight(token.start, token.end, painter);
194 } catch (BadLocationException ex) {
195 throw new IllegalArgumentException(ex);
196 }
197 }
198
199 public void clearHighlights() {
200 getHighlighter().removeAllHighlights();
201 }
202}
diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java
index 623e12e..d93aa9f 100644
--- a/src/main/java/cuchaz/enigma/gui/Gui.java
+++ b/src/main/java/cuchaz/enigma/gui/Gui.java
@@ -36,11 +36,17 @@ import cuchaz.enigma.gui.elements.MenuBar;
36import cuchaz.enigma.gui.elements.PopupMenuBar; 36import cuchaz.enigma.gui.elements.PopupMenuBar;
37import cuchaz.enigma.gui.filechooser.FileChooserFile; 37import cuchaz.enigma.gui.filechooser.FileChooserFile;
38import cuchaz.enigma.gui.filechooser.FileChooserFolder; 38import cuchaz.enigma.gui.filechooser.FileChooserFolder;
39import cuchaz.enigma.gui.highlight.DeobfuscatedHighlightPainter;
40import cuchaz.enigma.gui.highlight.ObfuscatedHighlightPainter;
41import cuchaz.enigma.gui.highlight.OtherHighlightPainter;
42import cuchaz.enigma.gui.highlight.SelectionHighlightPainter;
39import cuchaz.enigma.gui.panels.PanelDeobf; 43import cuchaz.enigma.gui.panels.PanelDeobf;
40import cuchaz.enigma.gui.panels.PanelEditor; 44import cuchaz.enigma.gui.panels.PanelEditor;
41import cuchaz.enigma.gui.panels.PanelIdentifier; 45import cuchaz.enigma.gui.panels.PanelIdentifier;
42import cuchaz.enigma.gui.panels.PanelObf; 46import cuchaz.enigma.gui.panels.PanelObf;
43import cuchaz.enigma.mapping.*; 47import cuchaz.enigma.mapping.*;
48import cuchaz.enigma.throwables.IllegalNameException;
49import cuchaz.enigma.utils.Utils;
44import de.sciss.syntaxpane.DefaultSyntaxKit; 50import de.sciss.syntaxpane.DefaultSyntaxKit;
45 51
46public class Gui { 52public class Gui {
@@ -363,7 +369,7 @@ public class Gui {
363 if (token == null) { 369 if (token == null) {
364 throw new IllegalArgumentException("Token cannot be null!"); 370 throw new IllegalArgumentException("Token cannot be null!");
365 } 371 }
366 CodeReader.navigateToToken(this.editor, token, m_selectionHighlightPainter); 372 Utils.navigateToToken(this.editor, token, m_selectionHighlightPainter);
367 redraw(); 373 redraw();
368 } 374 }
369 375
@@ -476,7 +482,7 @@ public class Gui {
476 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); 482 label.setPreferredSize(new Dimension(100, label.getPreferredSize().height));
477 panel.add(label); 483 panel.add(label);
478 484
479 panel.add(GuiTricks.unboldLabel(new JLabel(value, JLabel.LEFT))); 485 panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT)));
480 } 486 }
481 487
482 public void onCaretMove(int pos) { 488 public void onCaretMove(int pos) {
@@ -498,13 +504,13 @@ public class Gui {
498 m_infoPanel.clearReference(); 504 m_infoPanel.clearReference();
499 } 505 }
500 506
501 this.popupMenu.renameMenu.setEnabled(isRenameable && isToken); 507 this.popupMenu.renameMenu.setEnabled(isRenameable);
502 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry); 508 this.popupMenu.showInheritanceMenu.setEnabled(isClassEntry || isMethodEntry || isConstructorEntry);
503 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry); 509 this.popupMenu.showImplementationsMenu.setEnabled(isClassEntry || isMethodEntry);
504 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry); 510 this.popupMenu.showCallsMenu.setEnabled(isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry);
505 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry)); 511 this.popupMenu.openEntryMenu.setEnabled(isInJar && (isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry));
506 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation()); 512 this.popupMenu.openPreviousMenu.setEnabled(this.controller.hasPreviousLocation());
507 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable && isToken); 513 this.popupMenu.toggleMappingMenu.setEnabled(isRenameable);
508 514
509 if (isToken && this.controller.entryHasDeobfuscatedName(m_reference.entry)) { 515 if (isToken && this.controller.entryHasDeobfuscatedName(m_reference.entry)) {
510 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated"); 516 this.popupMenu.toggleMappingMenu.setText("Reset to obfuscated");
@@ -526,7 +532,6 @@ public class Gui {
526 532
527 private void navigateTo(EntryReference<Entry, Entry> reference) { 533 private void navigateTo(EntryReference<Entry, Entry> reference) {
528 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) { 534 if (!this.controller.entryIsInJar(reference.getLocationClassEntry())) {
529 // reference is not in the jar. Ignore it
530 return; 535 return;
531 } 536 }
532 if (m_reference != null) { 537 if (m_reference != null) {
@@ -574,7 +579,7 @@ public class Gui {
574 } catch (IllegalNameException ex) { 579 } catch (IllegalNameException ex) {
575 text.setBorder(BorderFactory.createLineBorder(Color.red, 1)); 580 text.setBorder(BorderFactory.createLineBorder(Color.red, 1));
576 text.setToolTipText(ex.getReason()); 581 text.setToolTipText(ex.getReason());
577 GuiTricks.showToolTipNow(text); 582 Utils.showToolTipNow(text);
578 } 583 }
579 return; 584 return;
580 } 585 }
@@ -582,7 +587,7 @@ public class Gui {
582 // abort the rename 587 // abort the rename
583 JPanel panel = (JPanel) m_infoPanel.getComponent(0); 588 JPanel panel = (JPanel) m_infoPanel.getComponent(0);
584 panel.remove(panel.getComponentCount() - 1); 589 panel.remove(panel.getComponentCount() - 1);
585 panel.add(GuiTricks.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT))); 590 panel.add(Utils.unboldLabel(new JLabel(m_reference.getNamableName(), JLabel.LEFT)));
586 591
587 this.editor.grabFocus(); 592 this.editor.grabFocus();
588 593
@@ -704,6 +709,7 @@ public class Gui {
704 if (!this.controller.isDirty()) { 709 if (!this.controller.isDirty()) {
705 // everything is saved, we can exit safely 710 // everything is saved, we can exit safely
706 this.frame.dispose(); 711 this.frame.dispose();
712 System.exit(0);
707 } else { 713 } else {
708 // ask to save before closing 714 // ask to save before closing
709 String[] options = {"Save and exit", "Discard changes", "Cancel"}; 715 String[] options = {"Save and exit", "Discard changes", "Cancel"};
diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java
index 37244ff..c301594 100644
--- a/src/main/java/cuchaz/enigma/gui/GuiController.java
+++ b/src/main/java/cuchaz/enigma/gui/GuiController.java
@@ -27,6 +27,8 @@ import cuchaz.enigma.Deobfuscator;
27import cuchaz.enigma.analysis.*; 27import cuchaz.enigma.analysis.*;
28import cuchaz.enigma.gui.dialog.ProgressDialog; 28import cuchaz.enigma.gui.dialog.ProgressDialog;
29import cuchaz.enigma.mapping.*; 29import cuchaz.enigma.mapping.*;
30import cuchaz.enigma.throwables.MappingParseException;
31import cuchaz.enigma.utils.ReadableToken;
30 32
31public class GuiController { 33public class GuiController {
32 34
@@ -72,7 +74,7 @@ public class GuiController {
72 refreshCurrentClass(); 74 refreshCurrentClass();
73 } 75 }
74 76
75 public void openMappings(File file) throws IOException, MappingParseException { 77 public void openMappings(File file) throws IOException {
76 this.deobfuscator.setMappings(new MappingsReader().read(file)); 78 this.deobfuscator.setMappings(new MappingsReader().read(file));
77 this.isDirty = false; 79 this.isDirty = false;
78 this.gui.setMappingsFile(file); 80 this.gui.setMappingsFile(file);
diff --git a/src/main/java/cuchaz/enigma/gui/GuiTricks.java b/src/main/java/cuchaz/enigma/gui/GuiTricks.java
deleted file mode 100644
index ffacfec..0000000
--- a/src/main/java/cuchaz/enigma/gui/GuiTricks.java
+++ /dev/null
@@ -1,52 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import java.awt.Font;
14import java.awt.event.ActionListener;
15import java.awt.event.MouseEvent;
16import java.util.Arrays;
17
18import javax.swing.JButton;
19import javax.swing.JComponent;
20import javax.swing.JLabel;
21import javax.swing.ToolTipManager;
22
23public class GuiTricks {
24
25 public static JLabel unboldLabel(JLabel label) {
26 Font font = label.getFont();
27 label.setFont(font.deriveFont(font.getStyle() & ~Font.BOLD));
28 return label;
29 }
30
31 public static void showToolTipNow(JComponent component) {
32 // HACKHACK: trick the tooltip manager into showing the tooltip right now
33 ToolTipManager manager = ToolTipManager.sharedInstance();
34 int oldDelay = manager.getInitialDelay();
35 manager.setInitialDelay(0);
36 manager.mouseMoved(new MouseEvent(component, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, 0, 0, 0, false));
37 manager.setInitialDelay(oldDelay);
38 }
39
40 public static void deactivateButton(JButton button) {
41 button.setEnabled(false);
42 button.setText("");
43 Arrays.asList(button.getActionListeners()).forEach(button::removeActionListener);
44 }
45
46 public static void activateButton(JButton button, String text, ActionListener newListener) {
47 button.setText(text);
48 button.setEnabled(true);
49 Arrays.asList(button.getActionListeners()).forEach(button::removeActionListener);
50 button.addActionListener(newListener);
51 }
52}
diff --git a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java b/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
deleted file mode 100644
index 0713ad5..0000000
--- a/src/main/java/cuchaz/enigma/gui/MemberMatchingGui.java
+++ /dev/null
@@ -1,442 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import com.google.common.collect.Lists;
14import com.google.common.collect.Maps;
15
16import java.awt.BorderLayout;
17import java.awt.Container;
18import java.awt.Dimension;
19import java.awt.FlowLayout;
20import java.awt.event.ActionListener;
21import java.awt.event.KeyAdapter;
22import java.awt.event.KeyEvent;
23import java.util.Collection;
24import java.util.List;
25import java.util.Map;
26
27import javax.swing.*;
28import javax.swing.text.Highlighter.HighlightPainter;
29
30import cuchaz.enigma.Constants;
31import cuchaz.enigma.Deobfuscator;
32import cuchaz.enigma.analysis.SourceIndex;
33import cuchaz.enigma.analysis.Token;
34import cuchaz.enigma.convert.ClassMatches;
35import cuchaz.enigma.convert.MemberMatches;
36import cuchaz.enigma.mapping.ClassEntry;
37import cuchaz.enigma.mapping.Entry;
38import de.sciss.syntaxpane.DefaultSyntaxKit;
39
40
41public class MemberMatchingGui<T extends Entry> {
42
43 private enum SourceType {
44 Matched {
45 @Override
46 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
47 return matches.getSourceClassesWithoutUnmatchedEntries();
48 }
49 },
50 Unmatched {
51 @Override
52 public <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches) {
53 return matches.getSourceClassesWithUnmatchedEntries();
54 }
55 };
56
57 public JRadioButton newRadio(ActionListener listener, ButtonGroup group) {
58 JRadioButton button = new JRadioButton(name(), this == getDefault());
59 button.setActionCommand(name());
60 button.addActionListener(listener);
61 group.add(button);
62 return button;
63 }
64
65 public abstract <T extends Entry> Collection<ClassEntry> getObfSourceClasses(MemberMatches<T> matches);
66
67 public static SourceType getDefault() {
68 return values()[0];
69 }
70 }
71
72 public interface SaveListener<T extends Entry> {
73 void save(MemberMatches<T> matches);
74 }
75
76 // controls
77 private JFrame m_frame;
78 private Map<SourceType, JRadioButton> m_sourceTypeButtons;
79 private ClassSelector m_sourceClasses;
80 private CodeReader m_sourceReader;
81 private CodeReader m_destReader;
82 private JButton m_matchButton;
83 private JButton m_unmatchableButton;
84 private JLabel m_sourceLabel;
85 private JLabel m_destLabel;
86 private HighlightPainter m_unmatchedHighlightPainter;
87 private HighlightPainter m_matchedHighlightPainter;
88
89 private ClassMatches m_classMatches;
90 private MemberMatches<T> m_memberMatches;
91 private Deobfuscator m_sourceDeobfuscator;
92 private Deobfuscator m_destDeobfuscator;
93 private SaveListener<T> m_saveListener;
94 private SourceType m_sourceType;
95 private ClassEntry m_obfSourceClass;
96 private ClassEntry m_obfDestClass;
97 private T m_obfSourceEntry;
98 private T m_obfDestEntry;
99
100 public MemberMatchingGui(ClassMatches classMatches, MemberMatches<T> fieldMatches, Deobfuscator sourceDeobfuscator, Deobfuscator destDeobfuscator) {
101
102 m_classMatches = classMatches;
103 m_memberMatches = fieldMatches;
104 m_sourceDeobfuscator = sourceDeobfuscator;
105 m_destDeobfuscator = destDeobfuscator;
106
107 // init frame
108 m_frame = new JFrame(Constants.NAME + " - Member Matcher");
109 final Container pane = m_frame.getContentPane();
110 pane.setLayout(new BorderLayout());
111
112 // init classes side
113 JPanel classesPanel = new JPanel();
114 classesPanel.setLayout(new BoxLayout(classesPanel, BoxLayout.PAGE_AXIS));
115 classesPanel.setPreferredSize(new Dimension(200, 0));
116 pane.add(classesPanel, BorderLayout.WEST);
117 classesPanel.add(new JLabel("Classes"));
118
119 // init source type radios
120 JPanel sourceTypePanel = new JPanel();
121 classesPanel.add(sourceTypePanel);
122 sourceTypePanel.setLayout(new BoxLayout(sourceTypePanel, BoxLayout.PAGE_AXIS));
123 ActionListener sourceTypeListener = event -> setSourceType(SourceType.valueOf(event.getActionCommand()));
124 ButtonGroup sourceTypeButtons = new ButtonGroup();
125 m_sourceTypeButtons = Maps.newHashMap();
126 for (SourceType sourceType : SourceType.values()) {
127 JRadioButton button = sourceType.newRadio(sourceTypeListener, sourceTypeButtons);
128 m_sourceTypeButtons.put(sourceType, button);
129 sourceTypePanel.add(button);
130 }
131
132 m_sourceClasses = new ClassSelector(ClassSelector.DEOBF_CLASS_COMPARATOR);
133 m_sourceClasses.setListener(this::setSourceClass);
134 JScrollPane sourceScroller = new JScrollPane(m_sourceClasses);
135 classesPanel.add(sourceScroller);
136
137 // init readers
138 DefaultSyntaxKit.initKit();
139 m_sourceReader = new CodeReader();
140 m_sourceReader.setSelectionListener(reference -> {
141 if (reference != null) {
142 onSelectSource(reference.entry);
143 } else {
144 onSelectSource(null);
145 }
146 });
147 m_destReader = new CodeReader();
148 m_destReader.setSelectionListener(reference -> {
149 if (reference != null) {
150 onSelectDest(reference.entry);
151 } else {
152 onSelectDest(null);
153 }
154 });
155
156 // add key bindings
157 KeyAdapter keyListener = new KeyAdapter() {
158 @Override
159 public void keyPressed(KeyEvent event) {
160 switch (event.getKeyCode()) {
161 case KeyEvent.VK_M:
162 m_matchButton.doClick();
163 break;
164 }
165 }
166 };
167 m_sourceReader.addKeyListener(keyListener);
168 m_destReader.addKeyListener(keyListener);
169
170 // init all the splits
171 JSplitPane splitRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, new JScrollPane(m_sourceReader), new JScrollPane(m_destReader));
172 splitRight.setResizeWeight(0.5); // resize 50:50
173 JSplitPane splitLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, classesPanel, splitRight);
174 splitLeft.setResizeWeight(0); // let the right side take all the slack
175 pane.add(splitLeft, BorderLayout.CENTER);
176 splitLeft.resetToPreferredSizes();
177
178 // init bottom panel
179 JPanel bottomPanel = new JPanel();
180 bottomPanel.setLayout(new FlowLayout());
181 pane.add(bottomPanel, BorderLayout.SOUTH);
182
183 m_matchButton = new JButton();
184 m_unmatchableButton = new JButton();
185
186 m_sourceLabel = new JLabel();
187 bottomPanel.add(m_sourceLabel);
188 bottomPanel.add(m_matchButton);
189 bottomPanel.add(m_unmatchableButton);
190 m_destLabel = new JLabel();
191 bottomPanel.add(m_destLabel);
192
193 // show the frame
194 pane.doLayout();
195 m_frame.setSize(1024, 576);
196 m_frame.setMinimumSize(new Dimension(640, 480));
197 m_frame.setVisible(true);
198 m_frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
199
200 m_unmatchedHighlightPainter = new ObfuscatedHighlightPainter();
201 m_matchedHighlightPainter = new DeobfuscatedHighlightPainter();
202
203 // init state
204 m_saveListener = null;
205 m_obfSourceClass = null;
206 m_obfDestClass = null;
207 m_obfSourceEntry = null;
208 m_obfDestEntry = null;
209 setSourceType(SourceType.getDefault());
210 updateButtons();
211 }
212
213 protected void setSourceType(SourceType val) {
214 m_sourceType = val;
215 updateSourceClasses();
216 }
217
218 public void setSaveListener(SaveListener<T> val) {
219 m_saveListener = val;
220 }
221
222 private void updateSourceClasses() {
223
224 String selectedPackage = m_sourceClasses.getSelectedPackage();
225
226 List<ClassEntry> deobfClassEntries = Lists.newArrayList();
227 for (ClassEntry entry : m_sourceType.getObfSourceClasses(m_memberMatches)) {
228 deobfClassEntries.add(m_sourceDeobfuscator.deobfuscateEntry(entry));
229 }
230 m_sourceClasses.setClasses(deobfClassEntries);
231
232 if (selectedPackage != null) {
233 m_sourceClasses.expandPackage(selectedPackage);
234 }
235
236 for (SourceType sourceType : SourceType.values()) {
237 m_sourceTypeButtons.get(sourceType).setText(String.format("%s (%d)",
238 sourceType.name(), sourceType.getObfSourceClasses(m_memberMatches).size()
239 ));
240 }
241 }
242
243 protected void setSourceClass(ClassEntry sourceClass) {
244
245 m_obfSourceClass = m_sourceDeobfuscator.obfuscateEntry(sourceClass);
246 m_obfDestClass = m_classMatches.getUniqueMatches().get(m_obfSourceClass);
247 if (m_obfDestClass == null) {
248 throw new Error("No matching dest class for source class: " + m_obfSourceClass);
249 }
250
251 m_sourceReader.decompileClass(m_obfSourceClass, m_sourceDeobfuscator, false, this::updateSourceHighlights);
252 m_destReader.decompileClass(m_obfDestClass, m_destDeobfuscator, false, this::updateDestHighlights);
253 }
254
255 protected void updateSourceHighlights() {
256 highlightEntries(m_sourceReader, m_sourceDeobfuscator, m_memberMatches.matches().keySet(), m_memberMatches.getUnmatchedSourceEntries());
257 }
258
259 protected void updateDestHighlights() {
260 highlightEntries(m_destReader, m_destDeobfuscator, m_memberMatches.matches().values(), m_memberMatches.getUnmatchedDestEntries());
261 }
262
263 private void highlightEntries(CodeReader reader, Deobfuscator deobfuscator, Collection<T> obfMatchedEntries, Collection<T> obfUnmatchedEntries) {
264 reader.clearHighlights();
265 SourceIndex index = reader.getSourceIndex();
266
267 // matched fields
268 for (T obfT : obfMatchedEntries) {
269 T deobfT = deobfuscator.deobfuscateEntry(obfT);
270 Token token = index.getDeclarationToken(deobfT);
271 if (token != null) {
272 reader.setHighlightedToken(token, m_matchedHighlightPainter);
273 }
274 }
275
276 // unmatched fields
277 for (T obfT : obfUnmatchedEntries) {
278 T deobfT = deobfuscator.deobfuscateEntry(obfT);
279 Token token = index.getDeclarationToken(deobfT);
280 if (token != null) {
281 reader.setHighlightedToken(token, m_unmatchedHighlightPainter);
282 }
283 }
284 }
285
286 private boolean isSelectionMatched() {
287 return m_obfSourceEntry != null && m_obfDestEntry != null
288 && m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry);
289 }
290
291 protected void onSelectSource(Entry source) {
292
293 // start with no selection
294 if (isSelectionMatched()) {
295 setDest(null);
296 }
297 setSource(null);
298
299 // then look for a valid source selection
300 if (source != null) {
301
302 // this looks really scary, but it's actually ok
303 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
304 // and MemberMatches.hasSource() will only pass entries that actually match T
305 @SuppressWarnings("unchecked")
306 T sourceEntry = (T) source;
307
308 T obfSourceEntry = m_sourceDeobfuscator.obfuscateEntry(sourceEntry);
309 if (m_memberMatches.hasSource(obfSourceEntry)) {
310 setSource(obfSourceEntry);
311
312 // look for a matched dest too
313 T obfDestEntry = m_memberMatches.matches().get(obfSourceEntry);
314 if (obfDestEntry != null) {
315 setDest(obfDestEntry);
316 }
317 }
318 }
319
320 updateButtons();
321 }
322
323 protected void onSelectDest(Entry dest) {
324
325 // start with no selection
326 if (isSelectionMatched()) {
327 setSource(null);
328 }
329 setDest(null);
330
331 // then look for a valid dest selection
332 if (dest != null) {
333
334 // this looks really scary, but it's actually ok
335 // Deobfuscator.obfuscateEntry can handle all implementations of Entry
336 // and MemberMatches.hasSource() will only pass entries that actually match T
337 @SuppressWarnings("unchecked")
338 T destEntry = (T) dest;
339
340 T obfDestEntry = m_destDeobfuscator.obfuscateEntry(destEntry);
341 if (m_memberMatches.hasDest(obfDestEntry)) {
342 setDest(obfDestEntry);
343
344 // look for a matched source too
345 T obfSourceEntry = m_memberMatches.matches().inverse().get(obfDestEntry);
346 if (obfSourceEntry != null) {
347 setSource(obfSourceEntry);
348 }
349 }
350 }
351
352 updateButtons();
353 }
354
355 private void setSource(T obfEntry) {
356 m_obfSourceEntry = obfEntry;
357 if (obfEntry == null) {
358 m_sourceLabel.setText("");
359 } else {
360 m_sourceLabel.setText(getEntryLabel(obfEntry, m_sourceDeobfuscator));
361 }
362 }
363
364 private void setDest(T obfEntry) {
365 m_obfDestEntry = obfEntry;
366 if (obfEntry == null) {
367 m_destLabel.setText("");
368 } else {
369 m_destLabel.setText(getEntryLabel(obfEntry, m_destDeobfuscator));
370 }
371 }
372
373 private String getEntryLabel(T obfEntry, Deobfuscator deobfuscator) {
374 // show obfuscated and deobfuscated names, but no types/signatures
375 T deobfEntry = deobfuscator.deobfuscateEntry(obfEntry);
376 return String.format("%s (%s)", deobfEntry.getName(), obfEntry.getName());
377 }
378
379 private void updateButtons() {
380
381 GuiTricks.deactivateButton(m_matchButton);
382 GuiTricks.deactivateButton(m_unmatchableButton);
383
384 if (m_obfSourceEntry != null && m_obfDestEntry != null) {
385 if (m_memberMatches.isMatched(m_obfSourceEntry, m_obfDestEntry)) {
386 GuiTricks.activateButton(m_matchButton, "Unmatch", event -> unmatch());
387 } else if (!m_memberMatches.isMatchedSourceEntry(m_obfSourceEntry) && !m_memberMatches.isMatchedDestEntry(m_obfDestEntry)) {
388 GuiTricks.activateButton(m_matchButton, "Match", event -> match());
389 }
390 } else if (m_obfSourceEntry != null) {
391 GuiTricks.activateButton(m_unmatchableButton, "Set Unmatchable", event -> unmatchable());
392 }
393 }
394
395 protected void match() {
396
397 // update the field matches
398 m_memberMatches.makeMatch(m_obfSourceEntry, m_obfDestEntry);
399 save();
400
401 // update the ui
402 onSelectSource(null);
403 onSelectDest(null);
404 updateSourceHighlights();
405 updateDestHighlights();
406 updateSourceClasses();
407 }
408
409 protected void unmatch() {
410
411 // update the field matches
412 m_memberMatches.unmakeMatch(m_obfSourceEntry, m_obfDestEntry);
413 save();
414
415 // update the ui
416 onSelectSource(null);
417 onSelectDest(null);
418 updateSourceHighlights();
419 updateDestHighlights();
420 updateSourceClasses();
421 }
422
423 protected void unmatchable() {
424
425 // update the field matches
426 m_memberMatches.makeSourceUnmatchable(m_obfSourceEntry);
427 save();
428
429 // update the ui
430 onSelectSource(null);
431 onSelectDest(null);
432 updateSourceHighlights();
433 updateDestHighlights();
434 updateSourceClasses();
435 }
436
437 private void save() {
438 if (m_saveListener != null) {
439 m_saveListener.save(m_memberMatches);
440 }
441 }
442}
diff --git a/src/main/java/cuchaz/enigma/gui/ReadableToken.java b/src/main/java/cuchaz/enigma/gui/ReadableToken.java
deleted file mode 100644
index feec8c0..0000000
--- a/src/main/java/cuchaz/enigma/gui/ReadableToken.java
+++ /dev/null
@@ -1,36 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13public class ReadableToken {
14
15 public int line;
16 public int startColumn;
17 public int endColumn;
18
19 public ReadableToken(int line, int startColumn, int endColumn) {
20 this.line = line;
21 this.startColumn = startColumn;
22 this.endColumn = endColumn;
23 }
24
25 @Override
26 public String toString() {
27 StringBuilder buf = new StringBuilder();
28 buf.append("line ");
29 buf.append(line);
30 buf.append(" columns ");
31 buf.append(startColumn);
32 buf.append("-");
33 buf.append(endColumn);
34 return buf.toString();
35 }
36}
diff --git a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java b/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
deleted file mode 100644
index 359ec7a..0000000
--- a/src/main/java/cuchaz/enigma/gui/ScoredClassEntry.java
+++ /dev/null
@@ -1,29 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.gui;
12
13import cuchaz.enigma.mapping.ClassEntry;
14
15public class ScoredClassEntry extends ClassEntry {
16
17 private static final long serialVersionUID = -8798725308554217105L;
18
19 private float score;
20
21 public ScoredClassEntry(ClassEntry other, float score) {
22 super(other);
23 this.score = score;
24 }
25
26 public float getScore() {
27 return this.score;
28 }
29}
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
index da4f5fb..f690b15 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/AboutDialog.java
@@ -19,7 +19,7 @@ import java.io.IOException;
19import javax.swing.*; 19import javax.swing.*;
20 20
21import cuchaz.enigma.Constants; 21import cuchaz.enigma.Constants;
22import cuchaz.enigma.Util; 22import cuchaz.enigma.utils.Utils;
23 23
24public class AboutDialog { 24public class AboutDialog {
25 25
@@ -31,7 +31,7 @@ public class AboutDialog {
31 31
32 // load the content 32 // load the content
33 try { 33 try {
34 String html = Util.readResourceToString("/about.html"); 34 String html = Utils.readResourceToString("/about.html");
35 html = String.format(html, Constants.NAME, Constants.VERSION); 35 html = String.format(html, Constants.NAME, Constants.VERSION);
36 JLabel label = new JLabel(html); 36 JLabel label = new JLabel(html);
37 label.setHorizontalAlignment(JLabel.CENTER); 37 label.setHorizontalAlignment(JLabel.CENTER);
@@ -44,7 +44,7 @@ public class AboutDialog {
44 String html = "<html><a href=\"%s\">%s</a></html>"; 44 String html = "<html><a href=\"%s\">%s</a></html>";
45 html = String.format(html, Constants.URL, Constants.URL); 45 html = String.format(html, Constants.URL, Constants.URL);
46 JButton link = new JButton(html); 46 JButton link = new JButton(html);
47 link.addActionListener(event -> Util.openUrl(Constants.URL)); 47 link.addActionListener(event -> Utils.openUrl(Constants.URL));
48 link.setBorderPainted(false); 48 link.setBorderPainted(false);
49 link.setOpaque(false); 49 link.setOpaque(false);
50 link.setBackground(Color.WHITE); 50 link.setBackground(Color.WHITE);
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
index 71aab01..8d3df47 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/CrashDialog.java
@@ -19,7 +19,7 @@ import java.io.StringWriter;
19import javax.swing.*; 19import javax.swing.*;
20 20
21import cuchaz.enigma.Constants; 21import cuchaz.enigma.Constants;
22import cuchaz.enigma.gui.GuiTricks; 22import cuchaz.enigma.utils.Utils;
23 23
24public class CrashDialog { 24public class CrashDialog {
25 25
@@ -48,7 +48,7 @@ public class CrashDialog {
48 FlowLayout buttonsLayout = new FlowLayout(); 48 FlowLayout buttonsLayout = new FlowLayout();
49 buttonsLayout.setAlignment(FlowLayout.RIGHT); 49 buttonsLayout.setAlignment(FlowLayout.RIGHT);
50 buttonsPanel.setLayout(buttonsLayout); 50 buttonsPanel.setLayout(buttonsLayout);
51 buttonsPanel.add(GuiTricks.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work."))); 51 buttonsPanel.add(Utils.unboldLabel(new JLabel("If you choose exit, you will lose any unsaved work.")));
52 JButton ignoreButton = new JButton("Ignore"); 52 JButton ignoreButton = new JButton("Ignore");
53 ignoreButton.addActionListener(event -> { 53 ignoreButton.addActionListener(event -> {
54 // close (hide) the dialog 54 // close (hide) the dialog
diff --git a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
index dc4d91e..c752c86 100644
--- a/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
+++ b/src/main/java/cuchaz/enigma/gui/dialog/ProgressDialog.java
@@ -19,7 +19,7 @@ import javax.swing.*;
19 19
20import cuchaz.enigma.Constants; 20import cuchaz.enigma.Constants;
21import cuchaz.enigma.Deobfuscator.ProgressListener; 21import cuchaz.enigma.Deobfuscator.ProgressListener;
22import cuchaz.enigma.gui.GuiTricks; 22import cuchaz.enigma.utils.Utils;
23 23
24public class ProgressDialog implements ProgressListener, AutoCloseable { 24public class ProgressDialog implements ProgressListener, AutoCloseable {
25 25
@@ -44,7 +44,7 @@ public class ProgressDialog implements ProgressListener, AutoCloseable {
44 JPanel panel = new JPanel(); 44 JPanel panel = new JPanel();
45 pane.add(panel); 45 pane.add(panel);
46 panel.setLayout(new BorderLayout()); 46 panel.setLayout(new BorderLayout());
47 this.labelText = GuiTricks.unboldLabel(new JLabel()); 47 this.labelText = Utils.unboldLabel(new JLabel());
48 this.progress = new JProgressBar(); 48 this.progress = new JProgressBar();
49 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); 49 this.labelText.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0));
50 panel.add(this.labelText, BorderLayout.NORTH); 50 panel.add(this.labelText, BorderLayout.NORTH);
diff --git a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
index 233d55e..e870334 100644
--- a/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
+++ b/src/main/java/cuchaz/enigma/gui/elements/MenuBar.java
@@ -9,7 +9,7 @@ import javax.swing.*;
9 9
10import cuchaz.enigma.gui.Gui; 10import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.gui.dialog.AboutDialog; 11import cuchaz.enigma.gui.dialog.AboutDialog;
12import cuchaz.enigma.mapping.MappingParseException; 12import cuchaz.enigma.throwables.MappingParseException;
13 13
14public class MenuBar extends JMenuBar { 14public class MenuBar extends JMenuBar {
15 15
@@ -69,8 +69,6 @@ public class MenuBar extends JMenuBar {
69 this.gui.getController().openMappings(this.gui.mappingsFileChooser.getSelectedFile()); 69 this.gui.getController().openMappings(this.gui.mappingsFileChooser.getSelectedFile());
70 } catch (IOException ex) { 70 } catch (IOException ex) {
71 throw new Error(ex); 71 throw new Error(ex);
72 } catch (MappingParseException ex) {
73 JOptionPane.showMessageDialog(this.gui.getFrame(), ex.getMessage());
74 } 72 }
75 } 73 }
76 }); 74 });
diff --git a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
index bd8f0f0..93ca5d6 100644
--- a/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
+++ b/src/main/java/cuchaz/enigma/gui/filechooser/FileChooserFolder.java
@@ -2,7 +2,7 @@ package cuchaz.enigma.gui.filechooser;
2 2
3import javax.swing.JFileChooser; 3import javax.swing.JFileChooser;
4 4
5public class FileChooserFolder extends JFileChooser{ 5public class FileChooserFolder extends JFileChooser {
6 6
7 public FileChooserFolder() { 7 public FileChooserFolder() {
8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 8 this.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
diff --git a/src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
index b66d13d..0a73088 100644
--- a/src/main/java/cuchaz/enigma/gui/BoxHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/BoxHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14import java.awt.Graphics; 14import java.awt.Graphics;
diff --git a/src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
index d5ad0c8..5d57203 100644
--- a/src/main/java/cuchaz/enigma/gui/DeobfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/DeobfuscatedHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
index 5afc767..ee64d86 100644
--- a/src/main/java/cuchaz/enigma/gui/ObfuscatedHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/ObfuscatedHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
index 256f69e..43d8352 100644
--- a/src/main/java/cuchaz/enigma/gui/OtherHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/OtherHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.Color; 13import java.awt.Color;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
index fcad07c..f772284 100644
--- a/src/main/java/cuchaz/enigma/gui/SelectionHighlightPainter.java
+++ b/src/main/java/cuchaz/enigma/gui/highlight/SelectionHighlightPainter.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.highlight;
12 12
13import java.awt.*; 13import java.awt.*;
14 14
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
index e73340a..e083572 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelectorClassNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorClassNode.java
@@ -8,7 +8,7 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.node;
12 12
13import javax.swing.tree.DefaultMutableTreeNode; 13import javax.swing.tree.DefaultMutableTreeNode;
14 14
@@ -16,8 +16,6 @@ import cuchaz.enigma.mapping.ClassEntry;
16 16
17public class ClassSelectorClassNode extends DefaultMutableTreeNode { 17public class ClassSelectorClassNode extends DefaultMutableTreeNode {
18 18
19 private static final long serialVersionUID = -8956754339813257380L;
20
21 private ClassEntry classEntry; 19 private ClassEntry classEntry;
22 20
23 public ClassSelectorClassNode(ClassEntry classEntry) { 21 public ClassSelectorClassNode(ClassEntry classEntry) {
@@ -30,9 +28,6 @@ public class ClassSelectorClassNode extends DefaultMutableTreeNode {
30 28
31 @Override 29 @Override
32 public String toString() { 30 public String toString() {
33 if (this.classEntry instanceof ScoredClassEntry) {
34 return String.format("%d%% %s", (int) ((ScoredClassEntry) this.classEntry).getScore(), this.classEntry.getSimpleName());
35 }
36 return this.classEntry.getSimpleName(); 31 return this.classEntry.getSimpleName();
37 } 32 }
38 33
diff --git a/src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
index 3b5ba8c..805b3a8 100644
--- a/src/main/java/cuchaz/enigma/gui/ClassSelectorPackageNode.java
+++ b/src/main/java/cuchaz/enigma/gui/node/ClassSelectorPackageNode.java
@@ -8,24 +8,18 @@
8 * Contributors: 8 * Contributors:
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui.node;
12 12
13import javax.swing.tree.DefaultMutableTreeNode; 13import javax.swing.tree.DefaultMutableTreeNode;
14 14
15public class ClassSelectorPackageNode extends DefaultMutableTreeNode { 15public class ClassSelectorPackageNode extends DefaultMutableTreeNode {
16 16
17 private static final long serialVersionUID = -3730868701219548043L;
18
19 private String packageName; 17 private String packageName;
20 18
21 public ClassSelectorPackageNode(String packageName) { 19 public ClassSelectorPackageNode(String packageName) {
22 this.packageName = packageName; 20 this.packageName = packageName;
23 } 21 }
24 22
25 public String getPackageName() {
26 return this.packageName;
27 }
28
29 @Override 23 @Override
30 public String toString() { 24 public String toString() {
31 return this.packageName; 25 return this.packageName;
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
index 2cc8b76..bba7132 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelDeobf.java
@@ -23,6 +23,5 @@ public class PanelDeobf extends JPanel {
23 this.setLayout(new BorderLayout()); 23 this.setLayout(new BorderLayout());
24 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH); 24 this.add(new JLabel("De-obfuscated Classes"), BorderLayout.NORTH);
25 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER); 25 this.add(new JScrollPane(this.deobfClasses), BorderLayout.CENTER);
26
27 } 26 }
28} 27}
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
index 4261eb5..faa023b 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelIdentifier.java
@@ -8,7 +8,7 @@ import javax.swing.JLabel;
8import javax.swing.JPanel; 8import javax.swing.JPanel;
9 9
10import cuchaz.enigma.gui.Gui; 10import cuchaz.enigma.gui.Gui;
11import cuchaz.enigma.gui.GuiTricks; 11import cuchaz.enigma.utils.Utils;
12 12
13public class PanelIdentifier extends JPanel { 13public class PanelIdentifier extends JPanel {
14 14
@@ -25,7 +25,7 @@ public class PanelIdentifier extends JPanel {
25 public void clearReference() { 25 public void clearReference() {
26 this.removeAll(); 26 this.removeAll();
27 JLabel label = new JLabel("No identifier selected"); 27 JLabel label = new JLabel("No identifier selected");
28 GuiTricks.unboldLabel(label); 28 Utils.unboldLabel(label);
29 label.setHorizontalAlignment(JLabel.CENTER); 29 label.setHorizontalAlignment(JLabel.CENTER);
30 this.add(label); 30 this.add(label);
31 31
diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
index 3e0374e..94b384f 100644
--- a/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
+++ b/src/main/java/cuchaz/enigma/gui/panels/PanelObf.java
@@ -19,7 +19,7 @@ public class PanelObf extends JPanel {
19 public PanelObf(Gui gui) { 19 public PanelObf(Gui gui) {
20 this.gui = gui; 20 this.gui = gui;
21 21
22 Comparator<ClassEntry> obfClassComparator = (a, b) -> { 22 Comparator<ClassEntry> obfClassComparator = (a, b) -> {
23 String aname = a.getName(); 23 String aname = a.getName();
24 String bname = b.getName(); 24 String bname = b.getName();
25 if (aname.length() != bname.length()) { 25 if (aname.length() != bname.length()) {